@koraidv/core 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1049 @@
1
+ /**
2
+ * Supported document types.
3
+ *
4
+ * Note: This enum is maintained for backward compatibility. The SDK now fetches
5
+ * the full list of supported countries and document types dynamically from the API.
6
+ */
7
+ declare enum DocumentType {
8
+ US_DRIVERS_LICENSE = "us_drivers_license",
9
+ US_STATE_ID = "us_state_id",
10
+ US_GREEN_CARD = "us_green_card",
11
+ INTERNATIONAL_PASSPORT = "international_passport",
12
+ EU_ID_GERMANY = "eu_id_de",
13
+ EU_ID_FRANCE = "eu_id_fr",
14
+ EU_ID_SPAIN = "eu_id_es",
15
+ EU_ID_ITALY = "eu_id_it",
16
+ GHANA_CARD = "ghana_card",
17
+ NIGERIA_NIN = "ng_nin",
18
+ NIGERIA_DRIVERS_LICENSE = "ng_drivers_license",
19
+ GHANA_DRIVERS_LICENSE = "gh_drivers_license",
20
+ KENYA_ID = "ke_id",
21
+ KENYA_DRIVERS_LICENSE = "ke_drivers_license",
22
+ SOUTH_AFRICA_ID = "za_id",
23
+ SOUTH_AFRICA_DRIVERS_LICENSE = "za_drivers_license",
24
+ UK_DRIVERS_LICENSE = "uk_drivers_license",
25
+ NIGERIA_VOTERS_CARD = "ng_voters_card",
26
+ LIBERIA_ID = "lr_id",
27
+ LIBERIA_DRIVERS_LICENSE = "lr_drivers_license",
28
+ LIBERIA_VOTERS_CARD = "lr_voters_card",
29
+ SIERRA_LEONE_ID = "sl_id",
30
+ SIERRA_LEONE_DRIVERS_LICENSE = "sl_drivers_license",
31
+ SIERRA_LEONE_VOTERS_CARD = "sl_voters_card",
32
+ GAMBIA_ID = "gm_id",
33
+ GAMBIA_DRIVERS_LICENSE = "gm_drivers_license",
34
+ UK_BRP = "uk_brp",
35
+ CANADA_DRIVERS_LICENSE = "ca_drivers_license",
36
+ CANADA_PR_CARD = "ca_pr_card",
37
+ CANADA_NATIONAL_ID = "ca_national_id",
38
+ INDIA_DRIVERS_LICENSE = "in_drivers_license",
39
+ GERMANY_RP = "de_rp",
40
+ FRANCE_RP = "fr_rp",
41
+ ITALY_RP = "it_rp",
42
+ SPAIN_RP = "es_rp",
43
+ IRELAND_RP = "ie_rp",
44
+ PORTUGAL_RP = "pt_rp",
45
+ SWEDEN_RP = "se_rp",
46
+ DENMARK_RP = "dk_rp",
47
+ NORWAY_RP = "no_rp",
48
+ FINLAND_RP = "fi_rp",
49
+ POLAND_RP = "pl_rp"
50
+ }
51
+ /**
52
+ * Document type metadata
53
+ */
54
+ interface DocumentTypeInfo {
55
+ code: DocumentType;
56
+ displayName: string;
57
+ hasMRZ: boolean;
58
+ requiresBack: boolean;
59
+ }
60
+ /**
61
+ * Get document type info
62
+ */
63
+ declare function getDocumentTypeInfo(type: DocumentType): DocumentTypeInfo;
64
+
65
+ /**
66
+ * SDK Configuration
67
+ */
68
+ interface Configuration {
69
+ /** API key (starts with ck_live_ or ck_sandbox_) */
70
+ apiKey: string;
71
+ /** Tenant ID (UUID) */
72
+ tenantId: string;
73
+ /** API environment */
74
+ environment: Environment;
75
+ /** Allowed document types */
76
+ documentTypes: DocumentType[];
77
+ /** Liveness detection mode */
78
+ livenessMode: LivenessMode;
79
+ /** Custom theme for UI */
80
+ theme: Theme;
81
+ /** Localization settings */
82
+ locale: Locale;
83
+ /** Session timeout in seconds */
84
+ timeout: number;
85
+ /** Enable debug logging */
86
+ debugLogging: boolean;
87
+ }
88
+ /**
89
+ * API Environment
90
+ */
91
+ type Environment = 'production' | 'sandbox';
92
+ /**
93
+ * Liveness detection mode
94
+ */
95
+ type LivenessMode = 'active' | 'passive';
96
+ /**
97
+ * Theme configuration
98
+ */
99
+ interface Theme {
100
+ primaryColor: string;
101
+ backgroundColor: string;
102
+ surfaceColor: string;
103
+ textColor: string;
104
+ secondaryTextColor: string;
105
+ errorColor: string;
106
+ successColor: string;
107
+ borderRadius: number;
108
+ fontFamily?: string;
109
+ }
110
+ /**
111
+ * Localization settings
112
+ */
113
+ interface Locale {
114
+ language: string;
115
+ region?: string;
116
+ }
117
+
118
+ /**
119
+ * Verification model
120
+ */
121
+ interface Verification {
122
+ /** Unique verification ID */
123
+ id: string;
124
+ /** External ID provided by the client */
125
+ externalId: string;
126
+ /** Tenant ID */
127
+ tenantId: string;
128
+ /** Verification tier */
129
+ tier: string;
130
+ /** Current status */
131
+ status: VerificationStatus;
132
+ /** Document verification result */
133
+ documentVerification?: DocumentVerification;
134
+ /** Face verification result */
135
+ faceVerification?: FaceVerification;
136
+ /** Liveness verification result */
137
+ livenessVerification?: LivenessVerification;
138
+ /** Risk signals */
139
+ riskSignals?: RiskSignal[];
140
+ /** Overall risk score (0-100) */
141
+ riskScore?: number;
142
+ /** Creation timestamp */
143
+ createdAt: Date;
144
+ /** Last update timestamp */
145
+ updatedAt: Date;
146
+ /** Completion timestamp */
147
+ completedAt?: Date;
148
+ }
149
+ /**
150
+ * Verification status
151
+ */
152
+ type VerificationStatus = 'pending' | 'document_required' | 'selfie_required' | 'liveness_required' | 'processing' | 'approved' | 'rejected' | 'review_required' | 'expired';
153
+ /**
154
+ * Document verification result
155
+ */
156
+ interface DocumentVerification {
157
+ documentType: string;
158
+ documentNumber?: string;
159
+ firstName?: string;
160
+ lastName?: string;
161
+ dateOfBirth?: string;
162
+ expirationDate?: string;
163
+ issuingCountry?: string;
164
+ mrzValid?: boolean;
165
+ authenticityScore?: number;
166
+ extractedFields?: Record<string, string>;
167
+ }
168
+ /**
169
+ * Face verification result
170
+ */
171
+ interface FaceVerification {
172
+ matchScore: number;
173
+ matchResult: string;
174
+ confidence: number;
175
+ }
176
+ /**
177
+ * Liveness verification result
178
+ */
179
+ interface LivenessVerification {
180
+ livenessScore: number;
181
+ isLive: boolean;
182
+ challengeResults?: ChallengeResult[];
183
+ }
184
+ /**
185
+ * Individual challenge result
186
+ */
187
+ interface ChallengeResult {
188
+ type: string;
189
+ passed: boolean;
190
+ confidence: number;
191
+ }
192
+ /**
193
+ * Risk signal
194
+ */
195
+ interface RiskSignal {
196
+ code: string;
197
+ severity: string;
198
+ message: string;
199
+ }
200
+
201
+ /**
202
+ * Kora error codes
203
+ */
204
+ declare enum KoraErrorCode {
205
+ NOT_CONFIGURED = "NOT_CONFIGURED",
206
+ INVALID_API_KEY = "INVALID_API_KEY",
207
+ INVALID_TENANT_ID = "INVALID_TENANT_ID",
208
+ NETWORK_ERROR = "NETWORK_ERROR",
209
+ TIMEOUT = "TIMEOUT",
210
+ NO_INTERNET = "NO_INTERNET",
211
+ UNAUTHORIZED = "UNAUTHORIZED",
212
+ FORBIDDEN = "FORBIDDEN",
213
+ NOT_FOUND = "NOT_FOUND",
214
+ VALIDATION_ERROR = "VALIDATION_ERROR",
215
+ RATE_LIMITED = "RATE_LIMITED",
216
+ SERVER_ERROR = "SERVER_ERROR",
217
+ HTTP_ERROR = "HTTP_ERROR",
218
+ CAMERA_ACCESS_DENIED = "CAMERA_ACCESS_DENIED",
219
+ CAMERA_NOT_AVAILABLE = "CAMERA_NOT_AVAILABLE",
220
+ CAPTURE_FAILED = "CAPTURE_FAILED",
221
+ QUALITY_VALIDATION_FAILED = "QUALITY_VALIDATION_FAILED",
222
+ DOCUMENT_NOT_DETECTED = "DOCUMENT_NOT_DETECTED",
223
+ DOCUMENT_TYPE_NOT_SUPPORTED = "DOCUMENT_TYPE_NOT_SUPPORTED",
224
+ MRZ_READ_FAILED = "MRZ_READ_FAILED",
225
+ FACE_NOT_DETECTED = "FACE_NOT_DETECTED",
226
+ MULTIPLE_FACES_DETECTED = "MULTIPLE_FACES_DETECTED",
227
+ FACE_MATCH_FAILED = "FACE_MATCH_FAILED",
228
+ LIVENESS_CHECK_FAILED = "LIVENESS_CHECK_FAILED",
229
+ CHALLENGE_NOT_COMPLETED = "CHALLENGE_NOT_COMPLETED",
230
+ SESSION_EXPIRED = "SESSION_EXPIRED",
231
+ VERIFICATION_EXPIRED = "VERIFICATION_EXPIRED",
232
+ VERIFICATION_ALREADY_COMPLETED = "VERIFICATION_ALREADY_COMPLETED",
233
+ INVALID_VERIFICATION_STATE = "INVALID_VERIFICATION_STATE",
234
+ UNKNOWN = "UNKNOWN",
235
+ USER_CANCELLED = "USER_CANCELLED"
236
+ }
237
+ /**
238
+ * Kora SDK Error
239
+ */
240
+ declare class KoraError extends Error {
241
+ readonly code: KoraErrorCode;
242
+ readonly recoverySuggestion?: string;
243
+ readonly details?: unknown;
244
+ constructor(code: KoraErrorCode, details?: unknown);
245
+ /**
246
+ * Create error from HTTP status code
247
+ */
248
+ static fromHttpStatus(status: number, details?: unknown): KoraError;
249
+ /**
250
+ * Check if error is retryable
251
+ */
252
+ get isRetryable(): boolean;
253
+ toJSON(): {
254
+ name: string;
255
+ code: KoraErrorCode;
256
+ message: string;
257
+ recoverySuggestion: string | undefined;
258
+ details: unknown;
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Create verification request
264
+ */
265
+ interface CreateVerificationRequest {
266
+ externalId: string;
267
+ tier: string;
268
+ }
269
+ /**
270
+ * Document upload response
271
+ */
272
+ interface DocumentUploadResponse {
273
+ success: boolean;
274
+ documentId?: string;
275
+ qualityScore?: number;
276
+ qualityIssues?: QualityIssue$1[];
277
+ extractedData?: DocumentExtractedData;
278
+ }
279
+ /**
280
+ * Quality issue
281
+ */
282
+ interface QualityIssue$1 {
283
+ type: string;
284
+ message: string;
285
+ severity: 'error' | 'warning';
286
+ }
287
+ /**
288
+ * Extracted document data
289
+ */
290
+ interface DocumentExtractedData {
291
+ documentType: string;
292
+ documentNumber?: string;
293
+ firstName?: string;
294
+ lastName?: string;
295
+ dateOfBirth?: string;
296
+ expirationDate?: string;
297
+ issuingCountry?: string;
298
+ mrzValid?: boolean;
299
+ }
300
+ /**
301
+ * Selfie upload response
302
+ */
303
+ interface SelfieUploadResponse {
304
+ success: boolean;
305
+ selfieId?: string;
306
+ faceDetected: boolean;
307
+ qualityScore?: number;
308
+ qualityIssues?: QualityIssue$1[];
309
+ }
310
+ /**
311
+ * Liveness session
312
+ */
313
+ interface LivenessSession {
314
+ sessionId: string;
315
+ challenges: LivenessChallenge[];
316
+ expiresAt: Date;
317
+ }
318
+ /**
319
+ * Liveness challenge
320
+ */
321
+ interface LivenessChallenge {
322
+ id: string;
323
+ type: ChallengeType;
324
+ instruction: string;
325
+ order: number;
326
+ }
327
+ /**
328
+ * Challenge type
329
+ */
330
+ type ChallengeType = 'blink' | 'smile' | 'turn_left' | 'turn_right' | 'nod_up' | 'nod_down';
331
+ /**
332
+ * Liveness challenge response
333
+ */
334
+ interface LivenessChallengeResponse {
335
+ success: boolean;
336
+ challengePassed: boolean;
337
+ confidence: number;
338
+ remainingChallenges: number;
339
+ }
340
+ /**
341
+ * Document quality check response
342
+ */
343
+ interface DocumentQualityResponse {
344
+ success: boolean;
345
+ qualityScore: number;
346
+ qualityIssues: string[];
347
+ details: {
348
+ textReadability: number;
349
+ faceQuality: number;
350
+ imageClarity: number;
351
+ };
352
+ }
353
+ /**
354
+ * Supported country with its available document types
355
+ */
356
+ interface SupportedCountry {
357
+ id: string;
358
+ name: string;
359
+ flagEmoji: string;
360
+ documentTypes: string[];
361
+ }
362
+ /**
363
+ * Handoff session created by the web SDK for cross-device mobile capture.
364
+ */
365
+ interface HandoffSession {
366
+ token: string;
367
+ captureUrl: string;
368
+ expiresAt: string;
369
+ expiresIn: number;
370
+ }
371
+ /**
372
+ * Context returned when a mobile browser validates a handoff token.
373
+ */
374
+ interface HandoffContext {
375
+ verificationId: string;
376
+ tenantId: string;
377
+ apiKey: string;
378
+ expiresAt: string;
379
+ }
380
+
381
+ /**
382
+ * Verification flow callbacks
383
+ */
384
+ interface VerificationCallbacks {
385
+ onComplete?: (verification: Verification) => void;
386
+ onError?: (error: KoraError) => void;
387
+ onCancel?: () => void;
388
+ onStepChange?: (step: VerificationStep) => void;
389
+ }
390
+ /**
391
+ * Verification flow options
392
+ */
393
+ interface VerificationOptions {
394
+ externalId: string;
395
+ tier?: 'basic' | 'standard' | 'enhanced';
396
+ documentTypes?: DocumentType[];
397
+ }
398
+ /**
399
+ * Verification step
400
+ */
401
+ type VerificationStep = 'consent' | 'document_selection' | 'document_front' | 'document_back' | 'selfie' | 'liveness' | 'processing' | 'complete';
402
+ /**
403
+ * Main Kora IDV SDK class
404
+ */
405
+ declare class KoraIDV {
406
+ private configuration;
407
+ private apiClient;
408
+ private currentVerification;
409
+ private livenessSession;
410
+ private sessionStartTime;
411
+ static readonly VERSION = "1.5.0";
412
+ constructor(config: Partial<Configuration> & {
413
+ apiKey: string;
414
+ tenantId: string;
415
+ });
416
+ private detectEnvironment;
417
+ /**
418
+ * Get supported countries and their document types from the API
419
+ */
420
+ getSupportedCountries(): Promise<SupportedCountry[]>;
421
+ /**
422
+ * Start a new verification flow
423
+ */
424
+ startVerification(options: VerificationOptions, callbacks: VerificationCallbacks): Promise<void>;
425
+ /**
426
+ * Resume an existing verification
427
+ */
428
+ resumeVerification(verificationId: string, callbacks: VerificationCallbacks): Promise<void>;
429
+ /**
430
+ * Check document quality before uploading (no active verification required)
431
+ */
432
+ checkDocumentQuality(imageData: Blob, documentType: string): Promise<DocumentQualityResponse>;
433
+ /**
434
+ * Upload document image
435
+ */
436
+ uploadDocument(imageData: Blob, side: 'front' | 'back', documentType: DocumentType): Promise<{
437
+ success: boolean;
438
+ qualityIssues?: string[];
439
+ }>;
440
+ /**
441
+ * Upload selfie image
442
+ */
443
+ uploadSelfie(imageData: Blob): Promise<{
444
+ success: boolean;
445
+ qualityIssues?: string[];
446
+ }>;
447
+ /**
448
+ * Start liveness session
449
+ */
450
+ startLivenessSession(): Promise<LivenessSession>;
451
+ /**
452
+ * Submit liveness challenge
453
+ */
454
+ submitLivenessChallenge(challenge: LivenessChallenge, imageData: Blob): Promise<{
455
+ passed: boolean;
456
+ remainingChallenges: number;
457
+ }>;
458
+ /**
459
+ * Complete the verification
460
+ */
461
+ completeVerification(): Promise<Verification>;
462
+ /**
463
+ * Get current verification
464
+ */
465
+ getCurrentVerification(): Verification | null;
466
+ /**
467
+ * Get current liveness session
468
+ */
469
+ getLivenessSession(): LivenessSession | null;
470
+ /**
471
+ * Check if session has timed out
472
+ */
473
+ isSessionTimedOut(): boolean;
474
+ /**
475
+ * Reset the session
476
+ */
477
+ reset(): void;
478
+ private determineStepFromStatus;
479
+ }
480
+
481
+ /**
482
+ * API Client for Kora IDV
483
+ */
484
+ declare class ApiClient {
485
+ private readonly baseUrl;
486
+ private readonly configuration;
487
+ private readonly maxRetries;
488
+ private readonly baseDelay;
489
+ constructor(configuration: Configuration);
490
+ /**
491
+ * Get supported countries and their document types
492
+ */
493
+ getSupportedCountries(): Promise<SupportedCountry[]>;
494
+ /**
495
+ * Create a new verification
496
+ */
497
+ createVerification(request: CreateVerificationRequest): Promise<Verification>;
498
+ /**
499
+ * Get an existing verification
500
+ */
501
+ getVerification(id: string): Promise<Verification>;
502
+ /**
503
+ * Upload document image.
504
+ *
505
+ * `decodedBarcodePayload` is the optional Phase 4 fast-path: when the
506
+ * client decoded the PDF417 / QR / DataMatrix on-device using the
507
+ * browser's BarcodeDetector API (or a polyfill), the AAMVA payload
508
+ * travels here so the server can skip image-based barcode decoding
509
+ * (~1-3 s round-trip savings). Empty/`undefined` = server falls
510
+ * back to its zxing-cpp + pdf417decoder cascade. Only meaningful for
511
+ * back captures on documents that carry a barcode.
512
+ * See `docs/architecture/idv-decode-roadmap.md` Phase 4.
513
+ */
514
+ uploadDocument(verificationId: string, imageData: Blob, side: 'front' | 'back', documentType: DocumentType, decodedBarcodePayload?: string): Promise<DocumentUploadResponse>;
515
+ /**
516
+ * Upload selfie image
517
+ */
518
+ uploadSelfie(verificationId: string, imageData: Blob): Promise<SelfieUploadResponse>;
519
+ /**
520
+ * Create liveness session
521
+ */
522
+ createLivenessSession(verificationId: string): Promise<LivenessSession>;
523
+ /**
524
+ * Submit liveness challenge
525
+ */
526
+ submitLivenessChallenge(verificationId: string, challenge: LivenessChallenge, imageData: Blob): Promise<LivenessChallengeResponse>;
527
+ /**
528
+ * Check document quality before uploading
529
+ */
530
+ checkDocumentQuality(imageData: Blob, documentType: string): Promise<DocumentQualityResponse>;
531
+ /**
532
+ * Complete the verification
533
+ */
534
+ completeVerification(verificationId: string): Promise<Verification>;
535
+ /**
536
+ * Create a handoff session for cross-device mobile capture.
537
+ * Returns a token and capture URL to encode in a QR code.
538
+ */
539
+ createHandoffSession(verificationId: string): Promise<HandoffSession>;
540
+ /**
541
+ * Validate a handoff token (called by the mobile capture page).
542
+ * Returns the verification context needed to continue capture.
543
+ */
544
+ validateHandoffToken(token: string): Promise<HandoffContext>;
545
+ /**
546
+ * Subscribe to verification status events via Server-Sent Events.
547
+ * Returns an EventSource that emits 'status' and 'complete' events.
548
+ */
549
+ subscribeToVerificationEvents(verificationId: string): EventSource;
550
+ /**
551
+ * Make an API request with retry logic
552
+ */
553
+ private request;
554
+ private executeWithRetry;
555
+ private shouldRetry;
556
+ private shouldRetryNetworkError;
557
+ private calculateDelay;
558
+ private sleep;
559
+ private blobToBase64;
560
+ /**
561
+ * Transform snake_case response to camelCase
562
+ */
563
+ private transformResponse;
564
+ }
565
+
566
+ /**
567
+ * Quality validation thresholds
568
+ */
569
+ interface QualityThresholds {
570
+ /** Minimum blur score (Laplacian variance) */
571
+ minBlurScore: number;
572
+ /** Minimum brightness (0-1) */
573
+ minBrightness: number;
574
+ /** Maximum brightness (0-1) */
575
+ maxBrightness: number;
576
+ /** Maximum glare percentage */
577
+ maxGlarePercentage: number;
578
+ /** Minimum face size as percentage of frame */
579
+ minFaceSizePercentage: number;
580
+ /** Face detection confidence threshold */
581
+ minFaceConfidence: number;
582
+ }
583
+ /**
584
+ * Quality validation result
585
+ */
586
+ interface QualityResult {
587
+ isValid: boolean;
588
+ issues: QualityIssue[];
589
+ metrics: QualityMetrics;
590
+ }
591
+ /**
592
+ * Quality issue
593
+ */
594
+ interface QualityIssue {
595
+ type: QualityIssueType;
596
+ message: string;
597
+ severity: 'error' | 'warning';
598
+ }
599
+ /**
600
+ * Quality issue types
601
+ */
602
+ type QualityIssueType = 'blur' | 'too_dark' | 'too_bright' | 'glare' | 'face_not_detected' | 'face_too_small' | 'face_off_center' | 'multiple_faces' | 'document_not_detected' | 'document_partially_visible';
603
+ /**
604
+ * Quality metrics
605
+ */
606
+ interface QualityMetrics {
607
+ blurScore?: number;
608
+ brightness?: number;
609
+ glarePercentage?: number;
610
+ faceSize?: number;
611
+ faceConfidence?: number;
612
+ faceCenterOffset?: {
613
+ x: number;
614
+ y: number;
615
+ };
616
+ }
617
+ /**
618
+ * Quality validator for captured images
619
+ */
620
+ declare class QualityValidator {
621
+ private thresholds;
622
+ constructor(thresholds?: Partial<QualityThresholds>);
623
+ /**
624
+ * Validate document image quality
625
+ */
626
+ validateDocumentImage(imageData: ImageData): Promise<QualityResult>;
627
+ /**
628
+ * Validate selfie image quality
629
+ */
630
+ validateSelfieImage(imageData: ImageData, faceDetection?: {
631
+ confidence: number;
632
+ boundingBox: DOMRect;
633
+ }): Promise<QualityResult>;
634
+ /**
635
+ * Calculate blur score using Laplacian variance
636
+ */
637
+ private calculateBlurScore;
638
+ /**
639
+ * Calculate average brightness (0-1)
640
+ */
641
+ private calculateBrightness;
642
+ /**
643
+ * Calculate percentage of overexposed pixels (glare)
644
+ */
645
+ private calculateGlarePercentage;
646
+ }
647
+
648
+ /**
649
+ * MRZ (Machine Readable Zone) Parser
650
+ * Supports TD1, TD2, and TD3 formats
651
+ */
652
+ /**
653
+ * Parsed MRZ data
654
+ */
655
+ interface MrzData {
656
+ /** Document format (TD1, TD2, TD3) */
657
+ format: MrzFormat;
658
+ /** Document type (P=Passport, I=ID, etc.) */
659
+ documentType: string;
660
+ /** Issuing country code (3-letter) */
661
+ issuingCountry: string;
662
+ /** Last name (surname) */
663
+ lastName: string;
664
+ /** First name(s) */
665
+ firstName: string;
666
+ /** Document number */
667
+ documentNumber: string;
668
+ /** Nationality (3-letter country code) */
669
+ nationality: string;
670
+ /** Date of birth (YYMMDD) */
671
+ dateOfBirth: string;
672
+ /** Sex (M/F/<) */
673
+ sex: string;
674
+ /** Expiration date (YYMMDD) */
675
+ expirationDate: string;
676
+ /** Optional data field 1 */
677
+ optionalData1?: string;
678
+ /** Optional data field 2 */
679
+ optionalData2?: string;
680
+ /** Whether all check digits are valid */
681
+ isValid: boolean;
682
+ /** Validation errors */
683
+ validationErrors: string[];
684
+ }
685
+ /**
686
+ * MRZ format type
687
+ */
688
+ type MrzFormat = 'TD1' | 'TD2' | 'TD3';
689
+ /**
690
+ * MRZ Parser class
691
+ */
692
+ declare class MrzParser {
693
+ /**
694
+ * Parse MRZ text
695
+ */
696
+ parse(mrzText: string): MrzData | null;
697
+ /**
698
+ * Clean and normalize MRZ text
699
+ */
700
+ private cleanMrzText;
701
+ /**
702
+ * Detect MRZ line length
703
+ */
704
+ private detectLineLength;
705
+ /**
706
+ * Detect MRZ format
707
+ */
708
+ private detectFormat;
709
+ /**
710
+ * Parse TD1 format (ID cards - 3 lines × 30 chars)
711
+ */
712
+ private parseTD1;
713
+ /**
714
+ * Parse TD2 format (Some ID cards - 2 lines × 36 chars)
715
+ */
716
+ private parseTD2;
717
+ /**
718
+ * Parse TD3 format (Passports - 2 lines × 44 chars)
719
+ */
720
+ private parseTD3;
721
+ /**
722
+ * Parse name field
723
+ */
724
+ private parseName;
725
+ /**
726
+ * Validate MRZ check digit
727
+ */
728
+ private validateCheckDigit;
729
+ /**
730
+ * Format date from YYMMDD to human readable
731
+ */
732
+ static formatDate(yymmdd: string): string;
733
+ }
734
+
735
+ /**
736
+ * Convert a Blob to a base64-encoded string (no data URL prefix).
737
+ *
738
+ * Used by the back-side document upload path to build the JSON request
739
+ * body that matches the server's `imageBase64` contract (and the
740
+ * Android/iOS SDK wire formats).
741
+ */
742
+ declare function blobToBase64(blob: Blob): Promise<string>;
743
+
744
+ /**
745
+ * On-device barcode decoder for the back side of identity documents.
746
+ *
747
+ * The KoraIDV pipeline cascades through three decoders in priority order:
748
+ * 1. **This class (browser BarcodeDetector)** — runs on the captured
749
+ * back-side image before upload. When it succeeds, the decoded
750
+ * AAMVA payload travels to the server in
751
+ * `uploadDocument(..., decodedBarcodePayload)` and the server skips
752
+ * image decoding entirely.
753
+ * 2. **zxing-cpp** (server-side) — primary decoder when the client failed
754
+ * or the browser lacks BarcodeDetector.
755
+ * 3. **pdf417decoder** (server-side) — fallback for captures zxing-cpp
756
+ * can't read.
757
+ *
758
+ * Why decode in the browser when the server can do it? Three reasons:
759
+ * - **Latency**: in-browser decode finishes in ~100-300 ms vs. ~1-3 s
760
+ * server round-trip + cascade.
761
+ * - **Cost**: zero ml-service compute on the happy path.
762
+ * - **Offline-friendly**: in some embedded flows (kiosk, on-prem),
763
+ * server-side decode is unavailable but the browser can still read
764
+ * the barcode.
765
+ *
766
+ * Browser support (as of 2026):
767
+ * - **Native**: Chrome 88+, Edge 88+, Samsung Internet 15+, Opera 74+
768
+ * on Android. Safari 16.4+ macOS/iOS supports a subset (no PDF417 on
769
+ * iOS Safari yet — the symbology list is a moving target). When
770
+ * unsupported, `isSupported()` returns false and the SDK silently
771
+ * falls back to the server cascade.
772
+ * - **Polyfill**: applications that need cross-browser support can
773
+ * install a JS polyfill (e.g. `barcode-detector` from npm) before
774
+ * the SDK loads; it registers `globalThis.BarcodeDetector` and this
775
+ * class will pick it up automatically.
776
+ *
777
+ * See `docs/architecture/idv-decode-roadmap.md` Phase 4.
778
+ */
779
+ type BarcodeFormat = 'pdf417' | 'qr_code' | 'data_matrix' | 'aztec' | 'code_128' | 'code_39' | 'code_93' | 'codabar' | 'ean_13' | 'ean_8' | 'itf' | 'upc_a' | 'upc_e';
780
+ declare class WebBarcodeScanner {
781
+ private detector;
782
+ /**
783
+ * Whether the current environment has a usable BarcodeDetector.
784
+ * Either native (modern Chromium / Samsung) or a polyfill.
785
+ */
786
+ static isSupported(): boolean;
787
+ /**
788
+ * Construct a scanner. Restricts to PDF417 by default to avoid false
789
+ * positives on the small Code128 strip US DLs also carry. Callers
790
+ * onboarding QR-based docs (Nigeria voter's card) should pass
791
+ * `['pdf417', 'qr_code']`.
792
+ */
793
+ constructor(formats?: BarcodeFormat[]);
794
+ /**
795
+ * Attempt to decode a PDF417 barcode from the supplied image source.
796
+ * Accepts any `ImageBitmapSource`: Blob, HTMLImageElement,
797
+ * HTMLCanvasElement, ImageBitmap, OffscreenCanvas.
798
+ *
799
+ * Returns the raw AAMVA payload as a single string (newline-separated
800
+ * records, exactly the form the server's AAMVA parser expects) or
801
+ * `null` when no barcode was found, decoding failed, or the API is
802
+ * unavailable.
803
+ */
804
+ decodePdf417(source: ImageBitmapSource): Promise<string | null>;
805
+ }
806
+
807
+ /**
808
+ * WalletModels.ts
809
+ * KoraIDV Wallet — W3C Verifiable Credential types for Web
810
+ *
811
+ * Types are prefixed with "Wallet" to avoid conflicts with existing KoraIDV types.
812
+ */
813
+ interface WalletCredential {
814
+ readonly '@context': string[];
815
+ readonly id: string;
816
+ readonly type: string[];
817
+ readonly issuer: string;
818
+ readonly issuanceDate: string;
819
+ readonly expirationDate: string;
820
+ readonly credentialSubject: WalletCredentialSubject;
821
+ readonly credentialStatus?: WalletCredentialStatus;
822
+ readonly proof?: WalletDataIntegrityProof;
823
+ }
824
+ interface WalletCredentialSubject {
825
+ readonly id: string;
826
+ readonly fullName: string;
827
+ readonly dateOfBirth?: string;
828
+ readonly nationality?: string;
829
+ readonly verificationLevel: string;
830
+ readonly documentType: string;
831
+ readonly documentCountry: string;
832
+ readonly biometricMatch: boolean;
833
+ readonly livenessCheck: boolean;
834
+ readonly governmentDbVerified: boolean;
835
+ readonly verifiedAt: string;
836
+ readonly confidenceScore: number;
837
+ }
838
+ interface WalletCredentialStatus {
839
+ readonly id: string;
840
+ readonly type: string;
841
+ readonly statusPurpose: string;
842
+ readonly statusListIndex: string;
843
+ readonly statusListCredential: string;
844
+ }
845
+ interface WalletDataIntegrityProof {
846
+ readonly type: string;
847
+ readonly cryptosuite: string;
848
+ readonly created: string;
849
+ readonly verificationMethod: string;
850
+ readonly proofPurpose: string;
851
+ readonly proofValue: string;
852
+ }
853
+ interface StoredWalletCredential {
854
+ readonly id: string;
855
+ readonly credential: WalletCredential;
856
+ readonly storedAt: string;
857
+ readonly issuerDID: string;
858
+ readonly subjectName: string;
859
+ readonly expiresAt: string;
860
+ }
861
+ interface WalletPresentation {
862
+ readonly '@context': string[];
863
+ readonly type: string[];
864
+ readonly holder: string | null;
865
+ readonly verifiableCredential: WalletCredential[];
866
+ readonly created: string;
867
+ readonly audience: string | null;
868
+ readonly challenge: string | null;
869
+ }
870
+ declare class WalletError extends Error {
871
+ readonly code: string;
872
+ constructor(code: string, message: string);
873
+ static storageFailed(): WalletError;
874
+ static credentialNotFound(): WalletError;
875
+ static credentialExpired(): WalletError;
876
+ static encodingFailed(): WalletError;
877
+ static cryptoUnavailable(): WalletError;
878
+ }
879
+ declare function createWalletCredential(params: Omit<WalletCredential, '@context' | 'type'> & {
880
+ '@context'?: string[];
881
+ type?: string[];
882
+ }): WalletCredential;
883
+
884
+ /**
885
+ * SelectiveDisclosure.ts
886
+ * KoraIDV Wallet — Selective disclosure profiles for Verifiable Presentations (Web)
887
+ */
888
+
889
+ declare enum DisclosureClaim {
890
+ FullName = "fullName",
891
+ DateOfBirth = "dateOfBirth",
892
+ Nationality = "nationality",
893
+ VerificationLevel = "verificationLevel",
894
+ DocumentType = "documentType",
895
+ DocumentCountry = "documentCountry",
896
+ BiometricMatch = "biometricMatch",
897
+ LivenessCheck = "livenessCheck",
898
+ GovernmentDbVerified = "governmentDbVerified",
899
+ VerifiedAt = "verifiedAt",
900
+ ConfidenceScore = "confidenceScore"
901
+ }
902
+ type DisclosureProfile = {
903
+ type: 'full';
904
+ } | {
905
+ type: 'onboarding';
906
+ } | {
907
+ type: 'ageOnly';
908
+ } | {
909
+ type: 'nationalityOnly';
910
+ } | {
911
+ type: 'verificationOnly';
912
+ } | {
913
+ type: 'custom';
914
+ claims: Set<DisclosureClaim>;
915
+ };
916
+ declare const DisclosureProfiles: {
917
+ full: DisclosureProfile;
918
+ onboarding: DisclosureProfile;
919
+ ageOnly: DisclosureProfile;
920
+ nationalityOnly: DisclosureProfile;
921
+ verificationOnly: DisclosureProfile;
922
+ custom: (claims: Set<DisclosureClaim>) => DisclosureProfile;
923
+ };
924
+ /**
925
+ * Apply a disclosure profile to a credential, returning a new credential
926
+ * containing only the disclosed claims in its subject.
927
+ */
928
+ declare function applyDisclosure(profile: DisclosureProfile, credential: WalletCredential): WalletCredential;
929
+ /**
930
+ * For ageOnly profile, compute whether the subject is over 18.
931
+ */
932
+ declare function computeAgeOver18(dateOfBirth?: string): boolean;
933
+
934
+ /**
935
+ * KoraWallet.ts
936
+ * KoraIDV Wallet — Main wallet class for Web
937
+ */
938
+
939
+ /**
940
+ * Main entry point for the Kora Wallet SDK module on Web.
941
+ *
942
+ * Provides credential storage (IndexedDB + Web Crypto), selective disclosure,
943
+ * Verifiable Presentation creation, and deep-link sharing.
944
+ */
945
+ declare class KoraWallet {
946
+ private readonly credentialStore;
947
+ constructor();
948
+ /**
949
+ * Store a Verifiable Credential in the wallet.
950
+ * Returns the storage ID (same as the credential's `id`).
951
+ */
952
+ store(credential: WalletCredential): Promise<string>;
953
+ /**
954
+ * Retrieve all stored credentials.
955
+ */
956
+ getCredentials(): Promise<StoredWalletCredential[]>;
957
+ /**
958
+ * Retrieve a single credential by ID.
959
+ */
960
+ getCredential(id: string): Promise<StoredWalletCredential | null>;
961
+ /**
962
+ * Delete a credential from the wallet.
963
+ */
964
+ deleteCredential(id: string): Promise<void>;
965
+ /**
966
+ * Number of credentials currently stored.
967
+ */
968
+ getCredentialCount(): Promise<number>;
969
+ /**
970
+ * Create a Verifiable Presentation with selective disclosure.
971
+ */
972
+ createPresentation(params: {
973
+ credentialId: string;
974
+ profile: DisclosureProfile;
975
+ audience?: string;
976
+ nonce?: string;
977
+ }): Promise<WalletPresentation>;
978
+ /**
979
+ * Generate a deep link URL for sharing a presentation.
980
+ */
981
+ generateDeepLink(presentation: WalletPresentation, profile?: DisclosureProfile): string | null;
982
+ /**
983
+ * Check whether a stored credential has expired.
984
+ */
985
+ isExpired(credentialId: string): Promise<boolean>;
986
+ /**
987
+ * Close the store and free resources.
988
+ */
989
+ close(): void;
990
+ }
991
+
992
+ /**
993
+ * CredentialStore.ts
994
+ * KoraIDV Wallet — IndexedDB + Web Crypto API encrypted credential storage for Web
995
+ *
996
+ * Uses the native SubtleCrypto API for AES-GCM encryption and IndexedDB
997
+ * for persistent storage. No external crypto libraries required.
998
+ */
999
+
1000
+ /**
1001
+ * Encrypted credential storage backed by IndexedDB and Web Crypto API.
1002
+ */
1003
+ declare class WalletCredentialStore {
1004
+ private db;
1005
+ private cryptoKey;
1006
+ private getDb;
1007
+ private getCryptoKey;
1008
+ private encrypt;
1009
+ private decrypt;
1010
+ save(id: string, credential: StoredWalletCredential): Promise<void>;
1011
+ load(id: string): Promise<StoredWalletCredential | null>;
1012
+ delete(id: string): Promise<void>;
1013
+ listIds(): Promise<string[]>;
1014
+ /**
1015
+ * Close the database connection and clear cached crypto key.
1016
+ */
1017
+ close(): void;
1018
+ }
1019
+
1020
+ /**
1021
+ * VerifiablePresentation.ts
1022
+ * KoraIDV Wallet — VP creation with selective disclosure for Web
1023
+ */
1024
+
1025
+ /**
1026
+ * Factory for building W3C Verifiable Presentations.
1027
+ */
1028
+ declare const WalletPresentationBuilder: {
1029
+ /**
1030
+ * Create a Verifiable Presentation from a credential with selective disclosure.
1031
+ */
1032
+ create(params: {
1033
+ credential: WalletCredential;
1034
+ profile: DisclosureProfile;
1035
+ holder?: string;
1036
+ audience?: string;
1037
+ nonce?: string;
1038
+ }): WalletPresentation;
1039
+ /**
1040
+ * Serialize a presentation to a JSON string.
1041
+ */
1042
+ encode(presentation: WalletPresentation): string;
1043
+ /**
1044
+ * Deserialize a presentation from a JSON string.
1045
+ */
1046
+ decode(json: string): WalletPresentation;
1047
+ };
1048
+
1049
+ export { ApiClient, type ChallengeResult, type ChallengeType, type Configuration, type CreateVerificationRequest, DisclosureClaim, type DisclosureProfile, DisclosureProfiles, type DocumentQualityResponse, DocumentType, type DocumentTypeInfo, type DocumentUploadResponse, type DocumentVerification, type Environment, type FaceVerification, type HandoffContext, type HandoffSession, KoraError, KoraErrorCode, KoraIDV, KoraWallet, type LivenessChallenge, type LivenessChallengeResponse, type LivenessMode, type LivenessSession, type LivenessVerification, type Locale, MrzParser, type QualityIssue$1 as QualityIssue, QualityValidator, type RiskSignal, type SelfieUploadResponse, type StoredWalletCredential, type SupportedCountry, type Theme, type Verification, type VerificationCallbacks, type VerificationOptions, type VerificationStatus, type VerificationStep, type WalletCredential, type WalletCredentialStatus, WalletCredentialStore, type WalletCredentialSubject, type WalletDataIntegrityProof, WalletError, type WalletPresentation, WalletPresentationBuilder, WebBarcodeScanner, applyDisclosure, blobToBase64, computeAgeOver18, createWalletCredential, getDocumentTypeInfo };