idmission-web-sdk 2.3.200 → 2.3.201

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sdk2.esm.js CHANGED
@@ -204,7 +204,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
204
204
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
205
205
  };
206
206
 
207
- var webSdkVersion = '2.3.200';
207
+ var webSdkVersion = '2.3.201';
208
208
 
209
209
  function getPlatform() {
210
210
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -1208,57 +1208,476 @@ function AuthProvider(_a) {
1208
1208
  }, children));
1209
1209
  }
1210
1210
 
1211
- var defaultSubmissionUrl = 'https://portal-api.idmission.com/swagger';
1212
- function setDefaultSubmissionUrl(url) {
1213
- defaultSubmissionUrl = url;
1211
+ /**
1212
+ * Parser for AAMVA (American Association of Motor Vehicle Administrators)
1213
+ * PDF417 barcode data found on US and Canadian driver's licenses.
1214
+ */
1215
+ // AAMVA field codes to human-readable labels
1216
+ var AAMVA_FIELD_LABELS = {
1217
+ // Required fields
1218
+ DCS: 'Last Name',
1219
+ DAC: 'First Name',
1220
+ DAD: 'Middle Name',
1221
+ DAA: 'Full Name',
1222
+ // Older AAMVA versions (1-3) use this instead of separate name fields
1223
+ DBB: 'Date of Birth',
1224
+ DBC: 'Sex',
1225
+ DAY: 'Eye Color',
1226
+ DAU: 'Height',
1227
+ DAG: 'Street Address',
1228
+ DAI: 'City',
1229
+ DAJ: 'State',
1230
+ DAK: 'ZIP Code',
1231
+ DAQ: 'License Number',
1232
+ DCF: 'Document Discriminator',
1233
+ DCG: 'Country',
1234
+ DDE: 'Last Name Truncation',
1235
+ DDF: 'First Name Truncation',
1236
+ DDG: 'Middle Name Truncation',
1237
+ // Optional fields
1238
+ DBD: 'Document Issue Date',
1239
+ DBA: 'Document Expiration Date',
1240
+ DCD: 'Jurisdiction Restriction Codes',
1241
+ DCB: 'Jurisdiction Endorsement Codes',
1242
+ DCA: 'Vehicle Class',
1243
+ DCE: 'Weight Range',
1244
+ DCL: 'Race/Ethnicity',
1245
+ DCM: 'Standard Vehicle Classification',
1246
+ DCN: 'Standard Endorsement Code',
1247
+ DCO: 'Standard Restriction Code',
1248
+ DCP: 'Jurisdiction Vehicle Class Description',
1249
+ DCQ: 'Jurisdiction Endorsement Code Description',
1250
+ DCR: 'Jurisdiction Restriction Code Description',
1251
+ DDA: 'Compliance Type',
1252
+ DDB: 'Card Revision Date',
1253
+ DDC: 'HAZMAT Endorsement Expiration Date',
1254
+ DDD: 'Limited Duration Document Indicator',
1255
+ DDH: 'Under 18 Until',
1256
+ DDI: 'Under 19 Until',
1257
+ DDJ: 'Under 21 Until',
1258
+ DDK: 'Organ Donor Indicator',
1259
+ DDL: 'Veteran Indicator',
1260
+ DAW: 'Weight (lbs)',
1261
+ DAX: 'Weight (kg)',
1262
+ DAZ: 'Hair Color',
1263
+ DBN: 'Alias/AKA Last Name',
1264
+ DBG: 'Alias/AKA First Name',
1265
+ DBS: 'Alias/AKA Suffix',
1266
+ DCU: 'Name Suffix',
1267
+ DCT: 'First Name Alias',
1268
+ DAH: 'Street Address Line 2',
1269
+ DAL: 'Residence Street Address',
1270
+ DAN: 'Residence City',
1271
+ DAO: 'Residence State',
1272
+ DAP: 'Residence ZIP'
1273
+ };
1274
+ // Sex code mappings
1275
+ var SEX_CODES = {
1276
+ '1': 'Male',
1277
+ '2': 'Female',
1278
+ '9': 'Not Specified'
1279
+ };
1280
+ // Eye color code mappings
1281
+ var EYE_COLOR_CODES = {
1282
+ BLK: 'Black',
1283
+ BLU: 'Blue',
1284
+ BRO: 'Brown',
1285
+ GRY: 'Gray',
1286
+ GRN: 'Green',
1287
+ HAZ: 'Hazel',
1288
+ MAR: 'Maroon',
1289
+ PNK: 'Pink',
1290
+ DIC: 'Dichromatic',
1291
+ UNK: 'Unknown'
1292
+ };
1293
+ // Hair color code mappings
1294
+ var HAIR_COLOR_CODES = {
1295
+ BAL: 'Bald',
1296
+ BLK: 'Black',
1297
+ BLN: 'Blond',
1298
+ BRO: 'Brown',
1299
+ GRY: 'Gray',
1300
+ RED: 'Red/Auburn',
1301
+ SDY: 'Sandy',
1302
+ WHI: 'White',
1303
+ UNK: 'Unknown'
1304
+ };
1305
+ /**
1306
+ * Formats a date string from AAMVA format to readable format (MM/DD/YYYY).
1307
+ * AAMVA versions 1-3 use YYYYMMDD (CCYYMMDD) format.
1308
+ * AAMVA versions 4+ use MMDDYYYY (MMDDCCYY) format.
1309
+ */
1310
+ function formatDate(value, aamvaVersion) {
1311
+ if (value.length !== 8) return value;
1312
+ // AAMVA versions 1-3 (pre-2000) use YYYYMMDD format
1313
+ // AAMVA versions 4+ (2000 onwards) use MMDDYYYY format
1314
+ var usesYYYYMMDD = aamvaVersion !== undefined && aamvaVersion <= 3;
1315
+ var month, day, year;
1316
+ if (usesYYYYMMDD) {
1317
+ // YYYYMMDD format (older versions)
1318
+ year = value.substring(0, 4);
1319
+ month = value.substring(4, 6);
1320
+ day = value.substring(6, 8);
1321
+ } else {
1322
+ // MMDDYYYY format (newer versions, default)
1323
+ month = value.substring(0, 2);
1324
+ day = value.substring(2, 4);
1325
+ year = value.substring(4, 8);
1326
+ }
1327
+ return "".concat(month, "/").concat(day, "/").concat(year);
1214
1328
  }
1215
- var defaultDocumentServiceUrl = 'https://portal-api.idmission.com/files/';
1216
- function setDefaultDocumentServiceUrl(url) {
1217
- defaultDocumentServiceUrl = url;
1329
+ /**
1330
+ * Formats a value based on its field code
1331
+ */
1332
+ function formatValue$1(code, value, aamvaVersion) {
1333
+ // Date fields
1334
+ if (['DBB', 'DBD', 'DBA', 'DDB', 'DDC', 'DDH', 'DDI', 'DDJ'].includes(code)) {
1335
+ return formatDate(value, aamvaVersion);
1336
+ }
1337
+ // Sex field
1338
+ if (code === 'DBC') {
1339
+ return SEX_CODES[value] || value;
1340
+ }
1341
+ // Eye color
1342
+ if (code === 'DAY') {
1343
+ return EYE_COLOR_CODES[value] || value;
1344
+ }
1345
+ // Hair color
1346
+ if (code === 'DAZ') {
1347
+ return HAIR_COLOR_CODES[value] || value;
1348
+ }
1349
+ // Height - format as feet and inches
1350
+ if (code === 'DAU') {
1351
+ // Format is typically "XXX in" or "FT IN"
1352
+ var match = value.match(/^(\d{3})/);
1353
+ if (match) {
1354
+ var inches = parseInt(match[1], 10);
1355
+ var feet = Math.floor(inches / 12);
1356
+ var remainingInches = inches % 12;
1357
+ return "".concat(feet, "'").concat(remainingInches, "\"");
1358
+ }
1359
+ return value;
1360
+ }
1361
+ // ZIP code - format with hyphen if 9 digits
1362
+ if (code === 'DAK' && value.length === 9) {
1363
+ return "".concat(value.substring(0, 5), "-").concat(value.substring(5));
1364
+ }
1365
+ return value.trim();
1218
1366
  }
1219
- function setServerUrl(url) {
1220
- setDefaultAuthUrl(url);
1221
- setDefaultSubmissionUrl(url + '/swagger');
1222
- setDefaultDocumentServiceUrl(url + '/files/');
1367
+ /**
1368
+ * Extracts the AAMVA version from the barcode header.
1369
+ * Header format: ANSI <IIN(6 digits)><AAMVA Version(2 digits)><Jurisdiction Version(2 digits)>...
1370
+ * Returns undefined if version cannot be determined.
1371
+ */
1372
+ function extractAAMVAVersion(rawData) {
1373
+ // Match ANSI followed by at least 8 digits (6 for IIN + 2 for version)
1374
+ var headerMatch = rawData.match(/ANSI\s*(\d{6})(\d{2})/);
1375
+ if (headerMatch) {
1376
+ var version = parseInt(headerMatch[2], 10);
1377
+ // Valid AAMVA versions are 1-99 (realistically 1-10 as of 2024)
1378
+ if (version >= 1 && version <= 99) {
1379
+ return version;
1380
+ }
1381
+ }
1382
+ return undefined;
1223
1383
  }
1224
- var capturedDocumentTypeToSubmissionKey = {
1225
- idCardFront: 'idFrontImage',
1226
- idCardBack: 'idBackImage',
1227
- passport: 'passportImage',
1228
- singlePage: 'idFrontImage',
1229
- selfie: 'selfieImage',
1230
- idFrontIrImage: 'idFrontIrImage',
1231
- idBackIrImage: 'idBackIrImage',
1232
- idFrontUvImage: 'idFrontUvImage',
1233
- idBackUvImage: 'idBackUvImage',
1234
- idBarcodeImage: 'idBarcodeImage'
1235
- };
1236
- var sanitizeMetadata = function sanitizeMetadata(metadata) {
1237
- var entries = Object.entries(metadata).filter(function (_a) {
1238
- var value = _a[1];
1239
- return value !== undefined && value !== null;
1384
+ /**
1385
+ * Parses the DAA field (full name) used in older AAMVA versions.
1386
+ * Format is typically "LAST,FIRST,MIDDLE" or "LAST,FIRST MIDDLE".
1387
+ * Returns an array of ParsedAAMVAField for LastName, FirstName, and MiddleName.
1388
+ */
1389
+ function parseDAAField(value) {
1390
+ var fields = [];
1391
+ // DAA format is typically "LASTNAME,FIRSTNAME,MIDDLENAME" or "LASTNAME,FIRSTNAME MIDDLENAME"
1392
+ // Some jurisdictions use spaces instead of commas
1393
+ var parts = value.split(',').map(function (p) {
1394
+ return p.trim();
1240
1395
  });
1241
- if (!entries.length) return null;
1242
- return Object.fromEntries(entries);
1243
- };
1244
- var SubmissionContext = /*#__PURE__*/createContext({
1245
- submit: function submit() {
1246
- return __awaiter(void 0, void 0, void 0, function () {
1247
- return __generator(this, function (_a) {
1248
- return [2 /*return*/, null];
1396
+ if (parts.length >= 1 && parts[0]) {
1397
+ fields.push({
1398
+ code: 'DCS',
1399
+ label: 'Last Name',
1400
+ value: parts[0]
1401
+ });
1402
+ }
1403
+ if (parts.length >= 2 && parts[1]) {
1404
+ // The second part might contain "FIRSTNAME MIDDLENAME"
1405
+ var nameParts = parts[1].split(/\s+/);
1406
+ if (nameParts[0]) {
1407
+ fields.push({
1408
+ code: 'DAC',
1409
+ label: 'First Name',
1410
+ value: nameParts[0]
1411
+ });
1412
+ }
1413
+ // If there's more after the first name, treat it as middle name
1414
+ if (nameParts.length > 1) {
1415
+ fields.push({
1416
+ code: 'DAD',
1417
+ label: 'Middle Name',
1418
+ value: nameParts.slice(1).join(' ')
1249
1419
  });
1420
+ }
1421
+ }
1422
+ // If there's a third comma-separated part, it's the middle name
1423
+ if (parts.length >= 3 && parts[2]) {
1424
+ // Check if we already added a middle name from the second part
1425
+ var existingMiddle = fields.find(function (f) {
1426
+ return f.code === 'DAD';
1250
1427
  });
1251
- },
1252
- submissionStatus: SubmissionStatus.READY,
1253
- submissionRequest: null,
1254
- submissionResponse: null,
1255
- submissionError: null,
1256
- submissionEnvironment: 'prod',
1257
- idFrontImage: null,
1258
- idBackImage: null,
1259
- passportImage: null,
1260
- idFrontIrImage: null,
1261
- idBackIrImage: null,
1428
+ if (!existingMiddle) {
1429
+ fields.push({
1430
+ code: 'DAD',
1431
+ label: 'Middle Name',
1432
+ value: parts[2]
1433
+ });
1434
+ }
1435
+ }
1436
+ return fields;
1437
+ }
1438
+ /**
1439
+ * Parses AAMVA PDF417 barcode data into structured fields
1440
+ */
1441
+ function parseAAMVABarcode(rawData) {
1442
+ var result = {
1443
+ fields: [],
1444
+ rawData: rawData
1445
+ };
1446
+ // Extract AAMVA version first - needed for date format detection
1447
+ result.aamvaVersion = extractAAMVAVersion(rawData);
1448
+ // Extract jurisdiction ID from header
1449
+ var headerMatch = rawData.match(/ANSI\s*(\d{6})/);
1450
+ if (headerMatch) {
1451
+ result.jurisdictionId = headerMatch[1];
1452
+ }
1453
+ // Clean up the data - remove control characters but keep newlines for parsing
1454
+ // AAMVA uses various separators: \n, \r, record separator (0x1E), etc.
1455
+ // eslint-disable-next-line no-control-regex
1456
+ var controlCharRegex = /[\x00-\x09\x0B\x0C\x0E-\x1F]/g;
1457
+ var cleanData = rawData.replace(controlCharRegex, '\n') // Replace control chars with newlines
1458
+ .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
1459
+ // Split into lines
1460
+ var lines = cleanData.split('\n').filter(function (line) {
1461
+ return line.trim();
1462
+ });
1463
+ // Extract fields using regex - AAMVA fields are 3-letter codes followed by data
1464
+ var fieldRegex = /^([A-Z]{3})(.*)$/;
1465
+ // Track if we've seen separate name fields (DCS, DAC, DAD)
1466
+ var hasSeenNameFields = {
1467
+ DCS: false,
1468
+ DAC: false,
1469
+ DAD: false
1470
+ };
1471
+ var daaValue = null;
1472
+ for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
1473
+ var line = lines_1[_i];
1474
+ var match = line.match(fieldRegex);
1475
+ if (match) {
1476
+ var code = match[1],
1477
+ rawValue = match[2];
1478
+ var value = rawValue.trim();
1479
+ // Skip empty values and header/version info
1480
+ if (!value || code === 'ANS') continue;
1481
+ // Track name fields
1482
+ if (code === 'DCS' || code === 'DAC' || code === 'DAD') {
1483
+ hasSeenNameFields[code] = true;
1484
+ }
1485
+ // Store DAA value for later processing (in case no separate name fields exist)
1486
+ if (code === 'DAA') {
1487
+ daaValue = value;
1488
+ continue; // Don't add DAA directly, we'll process it below
1489
+ }
1490
+ var label = AAMVA_FIELD_LABELS[code] || code;
1491
+ var formattedValue = formatValue$1(code, value, result.aamvaVersion);
1492
+ result.fields.push({
1493
+ code: code,
1494
+ label: label,
1495
+ value: formattedValue
1496
+ });
1497
+ }
1498
+ }
1499
+ // If we have a DAA field and are missing any separate name fields, parse DAA
1500
+ if (daaValue && (!hasSeenNameFields.DCS || !hasSeenNameFields.DAC)) {
1501
+ var daaFields = parseDAAField(daaValue);
1502
+ var _loop_1 = function _loop_1(field) {
1503
+ // Only add if we don't already have this field
1504
+ var existingField = result.fields.find(function (f) {
1505
+ return f.code === field.code;
1506
+ });
1507
+ if (!existingField) {
1508
+ result.fields.push(field);
1509
+ }
1510
+ };
1511
+ for (var _a = 0, daaFields_1 = daaFields; _a < daaFields_1.length; _a++) {
1512
+ var field = daaFields_1[_a];
1513
+ _loop_1(field);
1514
+ }
1515
+ }
1516
+ return result;
1517
+ }
1518
+ /**
1519
+ * Gets a specific field value from parsed AAMVA data
1520
+ */
1521
+ function getAAMVAField(parsed, code) {
1522
+ var field = parsed.fields.find(function (f) {
1523
+ return f.code === code;
1524
+ });
1525
+ return field === null || field === void 0 ? void 0 : field.value;
1526
+ }
1527
+ /**
1528
+ * Converts a date from MM/DD/YYYY format to DD/MM/YYYY format.
1529
+ * Returns empty string if input is empty or invalid.
1530
+ */
1531
+ function convertToApiDateFormat(dateStr) {
1532
+ if (!dateStr) return '';
1533
+ var parts = dateStr.split('/');
1534
+ if (parts.length !== 3) return dateStr;
1535
+ var month = parts[0],
1536
+ day = parts[1],
1537
+ year = parts[2];
1538
+ return "".concat(day, "/").concat(month, "/").concat(year);
1539
+ }
1540
+ /**
1541
+ * Converts parsed AAMVA barcode data to ocrMetadata format for submission API.
1542
+ * Returns the IDData object ready to be stringified for the ocrMetadata field.
1543
+ */
1544
+ function convertAAMVAToOCRMetadata(parsed) {
1545
+ var getField = function getField(code) {
1546
+ return getAAMVAField(parsed, code);
1547
+ };
1548
+ // Get state code for various fields
1549
+ var state = getField('DAJ') || '';
1550
+ // Determine gender from sex code
1551
+ var sexCode = getField('DBC');
1552
+ var gender = '';
1553
+ if (sexCode === 'Male' || sexCode === '1') gender = 'M';else if (sexCode === 'Female' || sexCode === '2') gender = 'F';
1554
+ // Get dates and convert from MM/DD/YYYY to DD/MM/YYYY format
1555
+ var expiryDate = convertToApiDateFormat(getField('DBA') || '');
1556
+ var issueDate = convertToApiDateFormat(getField('DBD') || '');
1557
+ var dateOfBirth = convertToApiDateFormat(getField('DBB') || '');
1558
+ // Calculate Under21 status based on DOB (using original MM/DD/YYYY format)
1559
+ var under21 = 'N';
1560
+ var rawDob = getField('DBB') || '';
1561
+ if (rawDob) {
1562
+ var parts = rawDob.split('/');
1563
+ if (parts.length === 3) {
1564
+ var month = parseInt(parts[0], 10);
1565
+ var day = parseInt(parts[1], 10);
1566
+ var year = parseInt(parts[2], 10);
1567
+ var dob = new Date(year, month - 1, day);
1568
+ var today = new Date();
1569
+ var age = today.getFullYear() - dob.getFullYear();
1570
+ var monthDiff = today.getMonth() - dob.getMonth();
1571
+ if (monthDiff < 0 || monthDiff === 0 && today.getDate() < dob.getDate()) {
1572
+ if (age - 1 < 21) under21 = 'Y';
1573
+ } else {
1574
+ if (age < 21) under21 = 'Y';
1575
+ }
1576
+ }
1577
+ }
1578
+ var idData = {
1579
+ IDCountry: 'USA',
1580
+ IDCountry_ISO2: 'US',
1581
+ IssuingCountry: 'USA',
1582
+ IDType: 'DL',
1583
+ DocumentTypeCode: 'DL',
1584
+ IDNumber: getField('DAQ') || '',
1585
+ IDState: state,
1586
+ State: state,
1587
+ FirstName: getField('DAC') || '',
1588
+ MiddleName: getField('DAD') || '',
1589
+ LastName: getField('DCS') || '',
1590
+ Gender: gender,
1591
+ DateofBirth: dateOfBirth,
1592
+ DateOfExpiry: expiryDate,
1593
+ ExpiryDate: expiryDate,
1594
+ DateOfIssue: issueDate,
1595
+ IssueDate: issueDate,
1596
+ AddressLine1: getField('DAG') || '',
1597
+ City: getField('DAI') || '',
1598
+ ZipCode: (getField('DAK') || '').replace(/-/g, ''),
1599
+ // Remove hyphen for API
1600
+ Country: 'USA',
1601
+ Eyes: getField('DAY') || '',
1602
+ Under21: under21,
1603
+ BarcodeDataParsed: 'Y'
1604
+ };
1605
+ // Remove empty string values to keep the payload clean
1606
+ var cleanedIdData = Object.fromEntries(Object.entries(idData).filter(function (_a) {
1607
+ var value = _a[1];
1608
+ return value !== '';
1609
+ }));
1610
+ // Always include BarcodeDataParsed
1611
+ cleanedIdData.BarcodeDataParsed = 'Y';
1612
+ return {
1613
+ IDData: cleanedIdData
1614
+ };
1615
+ }
1616
+ /**
1617
+ * Builds the ocrMetadata payload reported when a barcode capture flow ran
1618
+ * but no barcode was successfully parsed. Records the attempt count so the
1619
+ * backend can distinguish "user never tried" from "user tried N times".
1620
+ */
1621
+ function buildNoBarcodeOCRMetadata(attemptCount) {
1622
+ return {
1623
+ IDData: {
1624
+ BarcodeDataParsed: 'N',
1625
+ BarcodeAttemptCount: String(attemptCount)
1626
+ }
1627
+ };
1628
+ }
1629
+
1630
+ var defaultSubmissionUrl = 'https://portal-api.idmission.com/swagger';
1631
+ function setDefaultSubmissionUrl(url) {
1632
+ defaultSubmissionUrl = url;
1633
+ }
1634
+ var defaultDocumentServiceUrl = 'https://portal-api.idmission.com/files/';
1635
+ function setDefaultDocumentServiceUrl(url) {
1636
+ defaultDocumentServiceUrl = url;
1637
+ }
1638
+ function setServerUrl(url) {
1639
+ setDefaultAuthUrl(url);
1640
+ setDefaultSubmissionUrl(url + '/swagger');
1641
+ setDefaultDocumentServiceUrl(url + '/files/');
1642
+ }
1643
+ var capturedDocumentTypeToSubmissionKey = {
1644
+ idCardFront: 'idFrontImage',
1645
+ idCardBack: 'idBackImage',
1646
+ passport: 'passportImage',
1647
+ singlePage: 'idFrontImage',
1648
+ selfie: 'selfieImage',
1649
+ idFrontIrImage: 'idFrontIrImage',
1650
+ idBackIrImage: 'idBackIrImage',
1651
+ idFrontUvImage: 'idFrontUvImage',
1652
+ idBackUvImage: 'idBackUvImage',
1653
+ idBarcodeImage: 'idBarcodeImage'
1654
+ };
1655
+ var sanitizeMetadata = function sanitizeMetadata(metadata) {
1656
+ var entries = Object.entries(metadata).filter(function (_a) {
1657
+ var value = _a[1];
1658
+ return value !== undefined && value !== null;
1659
+ });
1660
+ if (!entries.length) return null;
1661
+ return Object.fromEntries(entries);
1662
+ };
1663
+ var SubmissionContext = /*#__PURE__*/createContext({
1664
+ submit: function submit() {
1665
+ return __awaiter(void 0, void 0, void 0, function () {
1666
+ return __generator(this, function (_a) {
1667
+ return [2 /*return*/, null];
1668
+ });
1669
+ });
1670
+ },
1671
+ submissionStatus: SubmissionStatus.READY,
1672
+ submissionRequest: null,
1673
+ submissionResponse: null,
1674
+ submissionError: null,
1675
+ submissionEnvironment: 'prod',
1676
+ idFrontImage: null,
1677
+ idBackImage: null,
1678
+ passportImage: null,
1679
+ idFrontIrImage: null,
1680
+ idBackIrImage: null,
1262
1681
  idFrontUvImage: null,
1263
1682
  idBackUvImage: null,
1264
1683
  idBarcodeImage: null,
@@ -1900,6 +2319,8 @@ var SubmissionProvider = function SubmissionProvider(_a) {
1900
2319
  }
1901
2320
  if (ocrMetadata) {
1902
2321
  submissionRequest.additionalData.ocrMetadata = ocrMetadata;
2322
+ } else if (barcodeCaptureAttempts.length > 0) {
2323
+ submissionRequest.additionalData.ocrMetadata = JSON.stringify(buildNoBarcodeOCRMetadata(barcodeCaptureAttempts.length));
1903
2324
  }
1904
2325
  if (signatureData) {
1905
2326
  submissionRequest.customerData.signatureData = {
@@ -10562,412 +10983,6 @@ var Card = styled.div(templateObject_1$G || (templateObject_1$G = __makeTemplate
10562
10983
  var FlexCard = styled(Card)(templateObject_2$C || (templateObject_2$C = __makeTemplateObject(["\n display: flex;\n flex-direction: column;\n"], ["\n display: flex;\n flex-direction: column;\n"])));
10563
10984
  var templateObject_1$G, templateObject_2$C;
10564
10985
 
10565
- /**
10566
- * Parser for AAMVA (American Association of Motor Vehicle Administrators)
10567
- * PDF417 barcode data found on US and Canadian driver's licenses.
10568
- */
10569
- // AAMVA field codes to human-readable labels
10570
- var AAMVA_FIELD_LABELS = {
10571
- // Required fields
10572
- DCS: 'Last Name',
10573
- DAC: 'First Name',
10574
- DAD: 'Middle Name',
10575
- DAA: 'Full Name',
10576
- // Older AAMVA versions (1-3) use this instead of separate name fields
10577
- DBB: 'Date of Birth',
10578
- DBC: 'Sex',
10579
- DAY: 'Eye Color',
10580
- DAU: 'Height',
10581
- DAG: 'Street Address',
10582
- DAI: 'City',
10583
- DAJ: 'State',
10584
- DAK: 'ZIP Code',
10585
- DAQ: 'License Number',
10586
- DCF: 'Document Discriminator',
10587
- DCG: 'Country',
10588
- DDE: 'Last Name Truncation',
10589
- DDF: 'First Name Truncation',
10590
- DDG: 'Middle Name Truncation',
10591
- // Optional fields
10592
- DBD: 'Document Issue Date',
10593
- DBA: 'Document Expiration Date',
10594
- DCD: 'Jurisdiction Restriction Codes',
10595
- DCB: 'Jurisdiction Endorsement Codes',
10596
- DCA: 'Vehicle Class',
10597
- DCE: 'Weight Range',
10598
- DCL: 'Race/Ethnicity',
10599
- DCM: 'Standard Vehicle Classification',
10600
- DCN: 'Standard Endorsement Code',
10601
- DCO: 'Standard Restriction Code',
10602
- DCP: 'Jurisdiction Vehicle Class Description',
10603
- DCQ: 'Jurisdiction Endorsement Code Description',
10604
- DCR: 'Jurisdiction Restriction Code Description',
10605
- DDA: 'Compliance Type',
10606
- DDB: 'Card Revision Date',
10607
- DDC: 'HAZMAT Endorsement Expiration Date',
10608
- DDD: 'Limited Duration Document Indicator',
10609
- DDH: 'Under 18 Until',
10610
- DDI: 'Under 19 Until',
10611
- DDJ: 'Under 21 Until',
10612
- DDK: 'Organ Donor Indicator',
10613
- DDL: 'Veteran Indicator',
10614
- DAW: 'Weight (lbs)',
10615
- DAX: 'Weight (kg)',
10616
- DAZ: 'Hair Color',
10617
- DBN: 'Alias/AKA Last Name',
10618
- DBG: 'Alias/AKA First Name',
10619
- DBS: 'Alias/AKA Suffix',
10620
- DCU: 'Name Suffix',
10621
- DCT: 'First Name Alias',
10622
- DAH: 'Street Address Line 2',
10623
- DAL: 'Residence Street Address',
10624
- DAN: 'Residence City',
10625
- DAO: 'Residence State',
10626
- DAP: 'Residence ZIP'
10627
- };
10628
- // Sex code mappings
10629
- var SEX_CODES = {
10630
- '1': 'Male',
10631
- '2': 'Female',
10632
- '9': 'Not Specified'
10633
- };
10634
- // Eye color code mappings
10635
- var EYE_COLOR_CODES = {
10636
- BLK: 'Black',
10637
- BLU: 'Blue',
10638
- BRO: 'Brown',
10639
- GRY: 'Gray',
10640
- GRN: 'Green',
10641
- HAZ: 'Hazel',
10642
- MAR: 'Maroon',
10643
- PNK: 'Pink',
10644
- DIC: 'Dichromatic',
10645
- UNK: 'Unknown'
10646
- };
10647
- // Hair color code mappings
10648
- var HAIR_COLOR_CODES = {
10649
- BAL: 'Bald',
10650
- BLK: 'Black',
10651
- BLN: 'Blond',
10652
- BRO: 'Brown',
10653
- GRY: 'Gray',
10654
- RED: 'Red/Auburn',
10655
- SDY: 'Sandy',
10656
- WHI: 'White',
10657
- UNK: 'Unknown'
10658
- };
10659
- /**
10660
- * Formats a date string from AAMVA format to readable format (MM/DD/YYYY).
10661
- * AAMVA versions 1-3 use YYYYMMDD (CCYYMMDD) format.
10662
- * AAMVA versions 4+ use MMDDYYYY (MMDDCCYY) format.
10663
- */
10664
- function formatDate(value, aamvaVersion) {
10665
- if (value.length !== 8) return value;
10666
- // AAMVA versions 1-3 (pre-2000) use YYYYMMDD format
10667
- // AAMVA versions 4+ (2000 onwards) use MMDDYYYY format
10668
- var usesYYYYMMDD = aamvaVersion !== undefined && aamvaVersion <= 3;
10669
- var month, day, year;
10670
- if (usesYYYYMMDD) {
10671
- // YYYYMMDD format (older versions)
10672
- year = value.substring(0, 4);
10673
- month = value.substring(4, 6);
10674
- day = value.substring(6, 8);
10675
- } else {
10676
- // MMDDYYYY format (newer versions, default)
10677
- month = value.substring(0, 2);
10678
- day = value.substring(2, 4);
10679
- year = value.substring(4, 8);
10680
- }
10681
- return "".concat(month, "/").concat(day, "/").concat(year);
10682
- }
10683
- /**
10684
- * Formats a value based on its field code
10685
- */
10686
- function formatValue$1(code, value, aamvaVersion) {
10687
- // Date fields
10688
- if (['DBB', 'DBD', 'DBA', 'DDB', 'DDC', 'DDH', 'DDI', 'DDJ'].includes(code)) {
10689
- return formatDate(value, aamvaVersion);
10690
- }
10691
- // Sex field
10692
- if (code === 'DBC') {
10693
- return SEX_CODES[value] || value;
10694
- }
10695
- // Eye color
10696
- if (code === 'DAY') {
10697
- return EYE_COLOR_CODES[value] || value;
10698
- }
10699
- // Hair color
10700
- if (code === 'DAZ') {
10701
- return HAIR_COLOR_CODES[value] || value;
10702
- }
10703
- // Height - format as feet and inches
10704
- if (code === 'DAU') {
10705
- // Format is typically "XXX in" or "FT IN"
10706
- var match = value.match(/^(\d{3})/);
10707
- if (match) {
10708
- var inches = parseInt(match[1], 10);
10709
- var feet = Math.floor(inches / 12);
10710
- var remainingInches = inches % 12;
10711
- return "".concat(feet, "'").concat(remainingInches, "\"");
10712
- }
10713
- return value;
10714
- }
10715
- // ZIP code - format with hyphen if 9 digits
10716
- if (code === 'DAK' && value.length === 9) {
10717
- return "".concat(value.substring(0, 5), "-").concat(value.substring(5));
10718
- }
10719
- return value.trim();
10720
- }
10721
- /**
10722
- * Extracts the AAMVA version from the barcode header.
10723
- * Header format: ANSI <IIN(6 digits)><AAMVA Version(2 digits)><Jurisdiction Version(2 digits)>...
10724
- * Returns undefined if version cannot be determined.
10725
- */
10726
- function extractAAMVAVersion(rawData) {
10727
- // Match ANSI followed by at least 8 digits (6 for IIN + 2 for version)
10728
- var headerMatch = rawData.match(/ANSI\s*(\d{6})(\d{2})/);
10729
- if (headerMatch) {
10730
- var version = parseInt(headerMatch[2], 10);
10731
- // Valid AAMVA versions are 1-99 (realistically 1-10 as of 2024)
10732
- if (version >= 1 && version <= 99) {
10733
- return version;
10734
- }
10735
- }
10736
- return undefined;
10737
- }
10738
- /**
10739
- * Parses the DAA field (full name) used in older AAMVA versions.
10740
- * Format is typically "LAST,FIRST,MIDDLE" or "LAST,FIRST MIDDLE".
10741
- * Returns an array of ParsedAAMVAField for LastName, FirstName, and MiddleName.
10742
- */
10743
- function parseDAAField(value) {
10744
- var fields = [];
10745
- // DAA format is typically "LASTNAME,FIRSTNAME,MIDDLENAME" or "LASTNAME,FIRSTNAME MIDDLENAME"
10746
- // Some jurisdictions use spaces instead of commas
10747
- var parts = value.split(',').map(function (p) {
10748
- return p.trim();
10749
- });
10750
- if (parts.length >= 1 && parts[0]) {
10751
- fields.push({
10752
- code: 'DCS',
10753
- label: 'Last Name',
10754
- value: parts[0]
10755
- });
10756
- }
10757
- if (parts.length >= 2 && parts[1]) {
10758
- // The second part might contain "FIRSTNAME MIDDLENAME"
10759
- var nameParts = parts[1].split(/\s+/);
10760
- if (nameParts[0]) {
10761
- fields.push({
10762
- code: 'DAC',
10763
- label: 'First Name',
10764
- value: nameParts[0]
10765
- });
10766
- }
10767
- // If there's more after the first name, treat it as middle name
10768
- if (nameParts.length > 1) {
10769
- fields.push({
10770
- code: 'DAD',
10771
- label: 'Middle Name',
10772
- value: nameParts.slice(1).join(' ')
10773
- });
10774
- }
10775
- }
10776
- // If there's a third comma-separated part, it's the middle name
10777
- if (parts.length >= 3 && parts[2]) {
10778
- // Check if we already added a middle name from the second part
10779
- var existingMiddle = fields.find(function (f) {
10780
- return f.code === 'DAD';
10781
- });
10782
- if (!existingMiddle) {
10783
- fields.push({
10784
- code: 'DAD',
10785
- label: 'Middle Name',
10786
- value: parts[2]
10787
- });
10788
- }
10789
- }
10790
- return fields;
10791
- }
10792
- /**
10793
- * Parses AAMVA PDF417 barcode data into structured fields
10794
- */
10795
- function parseAAMVABarcode(rawData) {
10796
- var result = {
10797
- fields: [],
10798
- rawData: rawData
10799
- };
10800
- // Extract AAMVA version first - needed for date format detection
10801
- result.aamvaVersion = extractAAMVAVersion(rawData);
10802
- // Extract jurisdiction ID from header
10803
- var headerMatch = rawData.match(/ANSI\s*(\d{6})/);
10804
- if (headerMatch) {
10805
- result.jurisdictionId = headerMatch[1];
10806
- }
10807
- // Clean up the data - remove control characters but keep newlines for parsing
10808
- // AAMVA uses various separators: \n, \r, record separator (0x1E), etc.
10809
- // eslint-disable-next-line no-control-regex
10810
- var controlCharRegex = /[\x00-\x09\x0B\x0C\x0E-\x1F]/g;
10811
- var cleanData = rawData.replace(controlCharRegex, '\n') // Replace control chars with newlines
10812
- .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
10813
- // Split into lines
10814
- var lines = cleanData.split('\n').filter(function (line) {
10815
- return line.trim();
10816
- });
10817
- // Extract fields using regex - AAMVA fields are 3-letter codes followed by data
10818
- var fieldRegex = /^([A-Z]{3})(.*)$/;
10819
- // Track if we've seen separate name fields (DCS, DAC, DAD)
10820
- var hasSeenNameFields = {
10821
- DCS: false,
10822
- DAC: false,
10823
- DAD: false
10824
- };
10825
- var daaValue = null;
10826
- for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
10827
- var line = lines_1[_i];
10828
- var match = line.match(fieldRegex);
10829
- if (match) {
10830
- var code = match[1],
10831
- rawValue = match[2];
10832
- var value = rawValue.trim();
10833
- // Skip empty values and header/version info
10834
- if (!value || code === 'ANS') continue;
10835
- // Track name fields
10836
- if (code === 'DCS' || code === 'DAC' || code === 'DAD') {
10837
- hasSeenNameFields[code] = true;
10838
- }
10839
- // Store DAA value for later processing (in case no separate name fields exist)
10840
- if (code === 'DAA') {
10841
- daaValue = value;
10842
- continue; // Don't add DAA directly, we'll process it below
10843
- }
10844
- var label = AAMVA_FIELD_LABELS[code] || code;
10845
- var formattedValue = formatValue$1(code, value, result.aamvaVersion);
10846
- result.fields.push({
10847
- code: code,
10848
- label: label,
10849
- value: formattedValue
10850
- });
10851
- }
10852
- }
10853
- // If we have a DAA field and are missing any separate name fields, parse DAA
10854
- if (daaValue && (!hasSeenNameFields.DCS || !hasSeenNameFields.DAC)) {
10855
- var daaFields = parseDAAField(daaValue);
10856
- var _loop_1 = function _loop_1(field) {
10857
- // Only add if we don't already have this field
10858
- var existingField = result.fields.find(function (f) {
10859
- return f.code === field.code;
10860
- });
10861
- if (!existingField) {
10862
- result.fields.push(field);
10863
- }
10864
- };
10865
- for (var _a = 0, daaFields_1 = daaFields; _a < daaFields_1.length; _a++) {
10866
- var field = daaFields_1[_a];
10867
- _loop_1(field);
10868
- }
10869
- }
10870
- return result;
10871
- }
10872
- /**
10873
- * Gets a specific field value from parsed AAMVA data
10874
- */
10875
- function getAAMVAField(parsed, code) {
10876
- var field = parsed.fields.find(function (f) {
10877
- return f.code === code;
10878
- });
10879
- return field === null || field === void 0 ? void 0 : field.value;
10880
- }
10881
- /**
10882
- * Converts a date from MM/DD/YYYY format to DD/MM/YYYY format.
10883
- * Returns empty string if input is empty or invalid.
10884
- */
10885
- function convertToApiDateFormat(dateStr) {
10886
- if (!dateStr) return '';
10887
- var parts = dateStr.split('/');
10888
- if (parts.length !== 3) return dateStr;
10889
- var month = parts[0],
10890
- day = parts[1],
10891
- year = parts[2];
10892
- return "".concat(day, "/").concat(month, "/").concat(year);
10893
- }
10894
- /**
10895
- * Converts parsed AAMVA barcode data to ocrMetadata format for submission API.
10896
- * Returns the IDData object ready to be stringified for the ocrMetadata field.
10897
- */
10898
- function convertAAMVAToOCRMetadata(parsed) {
10899
- var getField = function getField(code) {
10900
- return getAAMVAField(parsed, code);
10901
- };
10902
- // Get state code for various fields
10903
- var state = getField('DAJ') || '';
10904
- // Determine gender from sex code
10905
- var sexCode = getField('DBC');
10906
- var gender = '';
10907
- if (sexCode === 'Male' || sexCode === '1') gender = 'M';else if (sexCode === 'Female' || sexCode === '2') gender = 'F';
10908
- // Get dates and convert from MM/DD/YYYY to DD/MM/YYYY format
10909
- var expiryDate = convertToApiDateFormat(getField('DBA') || '');
10910
- var issueDate = convertToApiDateFormat(getField('DBD') || '');
10911
- var dateOfBirth = convertToApiDateFormat(getField('DBB') || '');
10912
- // Calculate Under21 status based on DOB (using original MM/DD/YYYY format)
10913
- var under21 = 'N';
10914
- var rawDob = getField('DBB') || '';
10915
- if (rawDob) {
10916
- var parts = rawDob.split('/');
10917
- if (parts.length === 3) {
10918
- var month = parseInt(parts[0], 10);
10919
- var day = parseInt(parts[1], 10);
10920
- var year = parseInt(parts[2], 10);
10921
- var dob = new Date(year, month - 1, day);
10922
- var today = new Date();
10923
- var age = today.getFullYear() - dob.getFullYear();
10924
- var monthDiff = today.getMonth() - dob.getMonth();
10925
- if (monthDiff < 0 || monthDiff === 0 && today.getDate() < dob.getDate()) {
10926
- if (age - 1 < 21) under21 = 'Y';
10927
- } else {
10928
- if (age < 21) under21 = 'Y';
10929
- }
10930
- }
10931
- }
10932
- var idData = {
10933
- IDCountry: 'USA',
10934
- IDCountry_ISO2: 'US',
10935
- IssuingCountry: 'USA',
10936
- IDType: 'DL',
10937
- DocumentTypeCode: 'DL',
10938
- IDNumber: getField('DAQ') || '',
10939
- IDState: state,
10940
- State: state,
10941
- FirstName: getField('DAC') || '',
10942
- MiddleName: getField('DAD') || '',
10943
- LastName: getField('DCS') || '',
10944
- Gender: gender,
10945
- DateofBirth: dateOfBirth,
10946
- DateOfExpiry: expiryDate,
10947
- ExpiryDate: expiryDate,
10948
- DateOfIssue: issueDate,
10949
- IssueDate: issueDate,
10950
- AddressLine1: getField('DAG') || '',
10951
- City: getField('DAI') || '',
10952
- ZipCode: (getField('DAK') || '').replace(/-/g, ''),
10953
- // Remove hyphen for API
10954
- Country: 'USA',
10955
- Eyes: getField('DAY') || '',
10956
- Under21: under21,
10957
- BarcodeDataParsed: 'Y'
10958
- };
10959
- // Remove empty string values to keep the payload clean
10960
- var cleanedIdData = Object.fromEntries(Object.entries(idData).filter(function (_a) {
10961
- var value = _a[1];
10962
- return value !== '';
10963
- }));
10964
- // Always include BarcodeDataParsed
10965
- cleanedIdData.BarcodeDataParsed = 'Y';
10966
- return {
10967
- IDData: cleanedIdData
10968
- };
10969
- }
10970
-
10971
10986
  var imageDisplayOrder = ['idCardFront', 'idCardBack', 'idBarcodeImage', 'passport', 'singlePage', 'idFrontIrImage', 'idBackIrImage', 'idFrontUvImage', 'idBackUvImage'];
10972
10987
  var downloadImage = function downloadImage(dataUrl, filename) {
10973
10988
  var link = document.createElement('a');