@hexar/biometric-identity-sdk-core 1.0.11 → 1.0.13

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.
@@ -183,12 +183,25 @@ class BiometricIdentitySDK {
183
183
  */
184
184
  async storeVideoRecording(videoResult) {
185
185
  try {
186
+ logger_1.logger.info('Storing video recording', {
187
+ framesCount: videoResult.frames?.length || 0,
188
+ duration: videoResult.duration,
189
+ hasSessionId: !!videoResult.sessionId
190
+ });
191
+ if (!videoResult.frames || videoResult.frames.length === 0) {
192
+ throw new Error('Video recording has no frames');
193
+ }
186
194
  this.updateState({ isLoading: true, currentStep: types_1.SDKStep.RECORD_LIVENESS });
187
195
  this.updateState({
188
196
  videoData: videoResult,
189
197
  progress: 75,
190
198
  isLoading: false,
191
199
  });
200
+ const stateAfterStore = this.getState();
201
+ logger_1.logger.info('Video recording stored', {
202
+ storedFramesCount: stateAfterStore.videoData?.frames?.length || 0,
203
+ hasVideoData: !!stateAfterStore.videoData
204
+ });
192
205
  }
193
206
  catch (error) {
194
207
  this.handleError(error);
@@ -258,22 +271,50 @@ class BiometricIdentitySDK {
258
271
  */
259
272
  async validateWithBackend() {
260
273
  if (!this.state.frontID || !this.state.videoData) {
274
+ logger_1.logger.error('Validation failed: missing required data', {
275
+ hasFrontID: !!this.state.frontID,
276
+ hasVideoData: !!this.state.videoData
277
+ });
261
278
  throw new Error('Required data not available');
262
279
  }
280
+ logger_1.logger.info('Starting backend validation', {
281
+ videoFramesCount: this.state.videoData.frames?.length || 0,
282
+ videoDuration: this.state.videoData.duration,
283
+ hasBackID: !!this.state.backID
284
+ });
263
285
  this.updateState({ progress: 80 });
264
286
  try {
265
- // Limit and sample video frames to prevent payload size issues
266
- // Take max 30 frames, sampling evenly across the video
267
287
  let videoFrames = this.state.videoData.frames;
268
- if (videoFrames.length === 0) {
288
+ if (!videoFrames || videoFrames.length === 0) {
289
+ logger_1.logger.error('No video frames in state', {
290
+ videoDataExists: !!this.state.videoData,
291
+ framesProperty: typeof this.state.videoData.frames,
292
+ framesLength: this.state.videoData.frames?.length
293
+ });
269
294
  throw new Error('No video frames available for validation');
270
295
  }
271
- // Sample frames if we have too many (max 30 frames to keep payload reasonable)
296
+ if (videoFrames.length < 10) {
297
+ logger_1.logger.warn('Insufficient frames for validation', {
298
+ frameCount: videoFrames.length,
299
+ minimumRequired: 10
300
+ });
301
+ }
272
302
  const MAX_FRAMES = 30;
273
303
  if (videoFrames.length > MAX_FRAMES) {
274
304
  const step = Math.floor(videoFrames.length / MAX_FRAMES);
275
305
  videoFrames = videoFrames.filter((_, index) => index % step === 0).slice(0, MAX_FRAMES);
306
+ logger_1.logger.info('Sampled frames for validation', {
307
+ originalCount: this.state.videoData.frames.length,
308
+ sampledCount: videoFrames.length
309
+ });
276
310
  }
311
+ logger_1.logger.info('Sending validation request to backend', {
312
+ framesCount: videoFrames.length,
313
+ duration: this.state.videoData.duration,
314
+ challengesCount: this.state.videoData.challengesCompleted?.length || 0,
315
+ frontImageSize: this.state.frontID.data?.length || 0,
316
+ backImageSize: this.state.backID?.data?.length || 0
317
+ });
277
318
  const response = await this.backendClient.fullValidation({
278
319
  frontIdImage: this.state.frontID.data,
279
320
  backIdImage: this.state.backID?.data,
@@ -282,11 +323,15 @@ class BiometricIdentitySDK {
282
323
  challengesCompleted: this.state.videoData.challengesCompleted || [],
283
324
  });
284
325
  this.updateState({ progress: 95 });
285
- // Convert backend response to SDK format
286
326
  return this.backendClient.convertToValidationResult(response);
287
327
  }
288
328
  catch (error) {
289
- throw this.createError(types_1.BiometricErrorCode.NETWORK_ERROR, 'Backend validation failed', error);
329
+ logger_1.logger.error('Backend validation failed', {
330
+ errorMessage: error?.message,
331
+ errorName: error?.name,
332
+ errorStack: error?.stack
333
+ });
334
+ throw this.createError(types_1.BiometricErrorCode.NETWORK_ERROR, error?.message || 'Backend validation failed', error);
290
335
  }
291
336
  }
292
337
  /**
@@ -88,10 +88,10 @@ class BackendClient {
88
88
  */
89
89
  async fullValidation(params) {
90
90
  if (!this.currentSessionId) {
91
- // Generate a challenge first if no session exists
91
+ logger_1.logger.info('No session ID, generating challenge');
92
92
  await this.generateChallenge();
93
93
  }
94
- return this.request('/api/v1/validate', 'POST', {
94
+ const requestBody = {
95
95
  front_id_image: params.frontIdImage,
96
96
  back_id_image: params.backIdImage,
97
97
  video_frames: params.videoFrames,
@@ -101,7 +101,20 @@ class BackendClient {
101
101
  document_type: params.documentType,
102
102
  country_code: params.countryCode,
103
103
  device_info: params.deviceInfo,
104
+ };
105
+ logger_1.logger.info('Full validation request', {
106
+ hasSessionId: !!this.currentSessionId,
107
+ sessionId: this.currentSessionId,
108
+ videoFramesCount: params.videoFrames.length,
109
+ hasFrontImage: !!params.frontIdImage,
110
+ hasBackImage: !!params.backIdImage,
111
+ frontImageLength: params.frontIdImage?.length || 0,
112
+ backImageLength: params.backIdImage?.length || 0,
113
+ averageFrameLength: params.videoFrames.length > 0
114
+ ? Math.round(params.videoFrames.reduce((sum, f) => sum + f.length, 0) / params.videoFrames.length)
115
+ : 0
104
116
  });
117
+ return this.request('/api/v1/validate', 'POST', requestBody);
105
118
  }
106
119
  /**
107
120
  * Convert backend response to SDK ValidationResult format
@@ -189,23 +202,45 @@ class BackendClient {
189
202
  });
190
203
  clearTimeout(timeoutId);
191
204
  if (!response.ok) {
192
- // Handle 413 Payload Too Large specifically
205
+ let errorData = {};
206
+ try {
207
+ const text = await response.text();
208
+ if (text) {
209
+ errorData = JSON.parse(text);
210
+ }
211
+ }
212
+ catch {
213
+ errorData = {};
214
+ }
215
+ const errorMessage = errorData?.error?.message ||
216
+ errorData?.detail ||
217
+ errorData?.message ||
218
+ `Request failed with status ${response.status}`;
219
+ logger_1.logger.error('Backend request failed', {
220
+ url,
221
+ method,
222
+ status: response.status,
223
+ statusText: response.statusText,
224
+ errorData
225
+ });
193
226
  if (response.status === 413) {
194
227
  throw new Error(`Payload too large (413). The request body exceeds the server's size limit. Try reducing the number of video frames or image sizes.`);
195
228
  }
196
- const errorData = await response.json().catch(() => ({}));
197
- throw new Error(errorData?.error?.message ||
198
- errorData?.detail ||
199
- `Request failed with status ${response.status}`);
229
+ throw new Error(errorMessage);
200
230
  }
201
231
  return await response.json();
202
232
  }
203
233
  catch (error) {
204
234
  clearTimeout(timeoutId);
205
235
  if (error.name === 'AbortError') {
236
+ logger_1.logger.error('Request timeout', { url, method, timeout: this.config.timeout });
206
237
  throw new Error('Request timeout');
207
238
  }
208
- throw error;
239
+ if (error.message) {
240
+ throw error;
241
+ }
242
+ logger_1.logger.error('Unexpected request error', { url, method, error });
243
+ throw new Error('Network request failed');
209
244
  }
210
245
  }
211
246
  }
@@ -54,7 +54,7 @@ exports.esAR = {
54
54
  liveness: {
55
55
  title: 'Verificación Facial',
56
56
  preparing: 'Preparando verificación...',
57
- getReady: '¡Prepárate!',
57
+ getReady: '¡Preparate!',
58
58
  countdownMessage: 'Vas a realizar acciones.\nSeguí las instrucciones en pantalla.',
59
59
  recordingInstructions: 'Mantené tu rostro visible y seguí las instrucciones',
60
60
  instructions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-core",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Core AI engine for biometric identity verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -236,6 +236,16 @@ export class BiometricIdentitySDK {
236
236
  */
237
237
  async storeVideoRecording(videoResult: VideoResult): Promise<void> {
238
238
  try {
239
+ logger.info('Storing video recording', {
240
+ framesCount: videoResult.frames?.length || 0,
241
+ duration: videoResult.duration,
242
+ hasSessionId: !!videoResult.sessionId
243
+ });
244
+
245
+ if (!videoResult.frames || videoResult.frames.length === 0) {
246
+ throw new Error('Video recording has no frames');
247
+ }
248
+
239
249
  this.updateState({ isLoading: true, currentStep: SDKStep.RECORD_LIVENESS });
240
250
 
241
251
  this.updateState({
@@ -244,6 +254,12 @@ export class BiometricIdentitySDK {
244
254
  isLoading: false,
245
255
  });
246
256
 
257
+ const stateAfterStore = this.getState();
258
+ logger.info('Video recording stored', {
259
+ storedFramesCount: stateAfterStore.videoData?.frames?.length || 0,
260
+ hasVideoData: !!stateAfterStore.videoData
261
+ });
262
+
247
263
  } catch (error) {
248
264
  this.handleError(error);
249
265
  throw error;
@@ -331,26 +347,58 @@ export class BiometricIdentitySDK {
331
347
  */
332
348
  private async validateWithBackend(): Promise<ValidationResult> {
333
349
  if (!this.state.frontID || !this.state.videoData) {
350
+ logger.error('Validation failed: missing required data', {
351
+ hasFrontID: !!this.state.frontID,
352
+ hasVideoData: !!this.state.videoData
353
+ });
334
354
  throw new Error('Required data not available');
335
355
  }
336
356
 
357
+ logger.info('Starting backend validation', {
358
+ videoFramesCount: this.state.videoData.frames?.length || 0,
359
+ videoDuration: this.state.videoData.duration,
360
+ hasBackID: !!this.state.backID
361
+ });
362
+
337
363
  this.updateState({ progress: 80 });
338
364
 
339
365
  try {
340
- // Limit and sample video frames to prevent payload size issues
341
- // Take max 30 frames, sampling evenly across the video
342
366
  let videoFrames = this.state.videoData.frames;
343
- if (videoFrames.length === 0) {
367
+
368
+ if (!videoFrames || videoFrames.length === 0) {
369
+ logger.error('No video frames in state', {
370
+ videoDataExists: !!this.state.videoData,
371
+ framesProperty: typeof this.state.videoData.frames,
372
+ framesLength: this.state.videoData.frames?.length
373
+ });
344
374
  throw new Error('No video frames available for validation');
345
375
  }
376
+
377
+ if (videoFrames.length < 10) {
378
+ logger.warn('Insufficient frames for validation', {
379
+ frameCount: videoFrames.length,
380
+ minimumRequired: 10
381
+ });
382
+ }
346
383
 
347
- // Sample frames if we have too many (max 30 frames to keep payload reasonable)
348
384
  const MAX_FRAMES = 30;
349
385
  if (videoFrames.length > MAX_FRAMES) {
350
386
  const step = Math.floor(videoFrames.length / MAX_FRAMES);
351
387
  videoFrames = videoFrames.filter((_, index) => index % step === 0).slice(0, MAX_FRAMES);
388
+ logger.info('Sampled frames for validation', {
389
+ originalCount: this.state.videoData.frames.length,
390
+ sampledCount: videoFrames.length
391
+ });
352
392
  }
353
393
 
394
+ logger.info('Sending validation request to backend', {
395
+ framesCount: videoFrames.length,
396
+ duration: this.state.videoData.duration,
397
+ challengesCount: this.state.videoData.challengesCompleted?.length || 0,
398
+ frontImageSize: this.state.frontID.data?.length || 0,
399
+ backImageSize: this.state.backID?.data?.length || 0
400
+ });
401
+
354
402
  const response = await this.backendClient.fullValidation({
355
403
  frontIdImage: this.state.frontID.data,
356
404
  backIdImage: this.state.backID?.data,
@@ -361,12 +409,16 @@ export class BiometricIdentitySDK {
361
409
 
362
410
  this.updateState({ progress: 95 });
363
411
 
364
- // Convert backend response to SDK format
365
412
  return this.backendClient.convertToValidationResult(response);
366
- } catch (error) {
413
+ } catch (error: any) {
414
+ logger.error('Backend validation failed', {
415
+ errorMessage: error?.message,
416
+ errorName: error?.name,
417
+ errorStack: error?.stack
418
+ });
367
419
  throw this.createError(
368
420
  BiometricErrorCode.NETWORK_ERROR,
369
- 'Backend validation failed',
421
+ error?.message || 'Backend validation failed',
370
422
  error
371
423
  );
372
424
  }
@@ -261,24 +261,39 @@ export class BackendClient {
261
261
  deviceInfo?: Record<string, any>;
262
262
  }): Promise<FullValidationResponse> {
263
263
  if (!this.currentSessionId) {
264
- // Generate a challenge first if no session exists
264
+ logger.info('No session ID, generating challenge');
265
265
  await this.generateChallenge();
266
266
  }
267
267
 
268
+ const requestBody = {
269
+ front_id_image: params.frontIdImage,
270
+ back_id_image: params.backIdImage,
271
+ video_frames: params.videoFrames,
272
+ video_duration_ms: params.videoDurationMs,
273
+ session_id: this.currentSessionId,
274
+ challenges_completed: params.challengesCompleted || [],
275
+ document_type: params.documentType,
276
+ country_code: params.countryCode,
277
+ device_info: params.deviceInfo,
278
+ };
279
+
280
+ logger.info('Full validation request', {
281
+ hasSessionId: !!this.currentSessionId,
282
+ sessionId: this.currentSessionId,
283
+ videoFramesCount: params.videoFrames.length,
284
+ hasFrontImage: !!params.frontIdImage,
285
+ hasBackImage: !!params.backIdImage,
286
+ frontImageLength: params.frontIdImage?.length || 0,
287
+ backImageLength: params.backIdImage?.length || 0,
288
+ averageFrameLength: params.videoFrames.length > 0
289
+ ? Math.round(params.videoFrames.reduce((sum, f) => sum + f.length, 0) / params.videoFrames.length)
290
+ : 0
291
+ });
292
+
268
293
  return this.request<FullValidationResponse>(
269
294
  '/api/v1/validate',
270
295
  'POST',
271
- {
272
- front_id_image: params.frontIdImage,
273
- back_id_image: params.backIdImage,
274
- video_frames: params.videoFrames,
275
- video_duration_ms: params.videoDurationMs,
276
- session_id: this.currentSessionId,
277
- challenges_completed: params.challengesCompleted || [],
278
- document_type: params.documentType,
279
- country_code: params.countryCode,
280
- device_info: params.deviceInfo,
281
- }
296
+ requestBody
282
297
  );
283
298
  }
284
299
 
@@ -382,19 +397,36 @@ export class BackendClient {
382
397
  clearTimeout(timeoutId);
383
398
 
384
399
  if (!response.ok) {
385
- // Handle 413 Payload Too Large specifically
400
+ let errorData: Record<string, any> = {};
401
+ try {
402
+ const text = await response.text();
403
+ if (text) {
404
+ errorData = JSON.parse(text);
405
+ }
406
+ } catch {
407
+ errorData = {};
408
+ }
409
+
410
+ const errorMessage = errorData?.error?.message ||
411
+ errorData?.detail ||
412
+ errorData?.message ||
413
+ `Request failed with status ${response.status}`;
414
+
415
+ logger.error('Backend request failed', {
416
+ url,
417
+ method,
418
+ status: response.status,
419
+ statusText: response.statusText,
420
+ errorData
421
+ });
422
+
386
423
  if (response.status === 413) {
387
424
  throw new Error(
388
425
  `Payload too large (413). The request body exceeds the server's size limit. Try reducing the number of video frames or image sizes.`
389
426
  );
390
427
  }
391
428
 
392
- const errorData = await response.json().catch(() => ({})) as Record<string, any>;
393
- throw new Error(
394
- errorData?.error?.message ||
395
- errorData?.detail ||
396
- `Request failed with status ${response.status}`
397
- );
429
+ throw new Error(errorMessage);
398
430
  }
399
431
 
400
432
  return await response.json() as T;
@@ -402,10 +434,16 @@ export class BackendClient {
402
434
  clearTimeout(timeoutId);
403
435
 
404
436
  if (error.name === 'AbortError') {
437
+ logger.error('Request timeout', { url, method, timeout: this.config.timeout });
405
438
  throw new Error('Request timeout');
406
439
  }
407
440
 
408
- throw error;
441
+ if (error.message) {
442
+ throw error;
443
+ }
444
+
445
+ logger.error('Unexpected request error', { url, method, error });
446
+ throw new Error('Network request failed');
409
447
  }
410
448
  }
411
449
  }
@@ -57,7 +57,7 @@ export const esAR: LanguageStrings = {
57
57
  liveness: {
58
58
  title: 'Verificación Facial',
59
59
  preparing: 'Preparando verificación...',
60
- getReady: '¡Prepárate!',
60
+ getReady: '¡Preparate!',
61
61
  countdownMessage: 'Vas a realizar acciones.\nSeguí las instrucciones en pantalla.',
62
62
  recordingInstructions: 'Mantené tu rostro visible y seguí las instrucciones',
63
63
  instructions: {