@claritylabs/cl-sdk 3.0.31 → 3.0.33

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/index.js CHANGED
@@ -3313,6 +3313,15 @@ function moneyValue(value) {
3313
3313
  if (/^\d{1,3}(?:,\d{3})*(?:\.\d{2})?$/.test(clean)) return `$${clean}`;
3314
3314
  return clean;
3315
3315
  }
3316
+ function premiumValue(value) {
3317
+ const clean = cleanValue(value);
3318
+ if (!clean) return void 0;
3319
+ if (/\$[A-Z0-9]/i.test(clean)) return clean;
3320
+ if (/\b(?:CAD|USD)\b/i.test(clean) && /(?:\d|X{2,})/i.test(clean)) return clean;
3321
+ if (/^\d{1,3}(?:,\d{3})+\.\d{2}$/.test(clean) || /^\d+\.\d{2}$/.test(clean)) return `$${clean}`;
3322
+ if (/^X{2,}(?:,X{3})*(?:\.X{2})$/i.test(clean)) return clean;
3323
+ return void 0;
3324
+ }
3316
3325
  function nodeText(node) {
3317
3326
  return normalizeWhitespace3([
3318
3327
  node.title,
@@ -3328,6 +3337,14 @@ function valueFromNode(node, value, confidence = "medium") {
3328
3337
  sourceSpanIds: node.sourceSpanIds
3329
3338
  };
3330
3339
  }
3340
+ function valueFromNodes(nodes, value, confidence = "medium", normalizedValue) {
3341
+ return {
3342
+ value,
3343
+ ...normalizedValue ? { normalizedValue } : {},
3344
+ confidence,
3345
+ ...sourceIds(nodes)
3346
+ };
3347
+ }
3331
3348
  function firstMatch(nodes, patterns) {
3332
3349
  for (const node of nodes) {
3333
3350
  const text = nodeText(node);
@@ -3394,27 +3411,96 @@ function firstCleanMatch(nodes, patterns, clean) {
3394
3411
  function cleanNamedInsured(value) {
3395
3412
  const clean = cleanValue(value.replace(/\bborn\s+on\b.*$/i, "").replace(/\bage\s+nearest\b.*$/i, "").replace(/\bbeneficiary\b.*$/i, ""));
3396
3413
  if (!clean || clean.length > 160) return void 0;
3414
+ if (!/[A-Za-z0-9]/.test(clean)) return void 0;
3397
3415
  if (/^(person|persons)\b/i.test(clean)) return void 0;
3416
+ if (/^(insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification)\b/i.test(clean)) {
3417
+ return void 0;
3418
+ }
3398
3419
  if (/\b(table of contents|policy wording|provided solely|convenience|not to be construed|actual policy issued)\b/i.test(clean)) {
3399
3420
  return void 0;
3400
3421
  }
3401
3422
  return clean;
3402
3423
  }
3424
+ var PARTY_LABEL_PATTERNS = {
3425
+ namedInsured: /^(?:item\s*\d+[.)]?\s*)?(?:named insured(?:\s+and\s+address)?|insured name)$/i,
3426
+ insurer: /^(?:carrier|insurer|security)$/i,
3427
+ broker: /^(?:broker(?:\s+of\s+record)?|producer|agent)$/i
3428
+ };
3429
+ function partyLabelKind(value) {
3430
+ const clean = cleanValue(value.replace(/^\s*column\s+\d+\s*:\s*/i, ""));
3431
+ if (!clean) return void 0;
3432
+ if (PARTY_LABEL_PATTERNS.namedInsured.test(clean)) return "namedInsured";
3433
+ if (PARTY_LABEL_PATTERNS.insurer.test(clean)) return "insurer";
3434
+ if (PARTY_LABEL_PATTERNS.broker.test(clean)) return "broker";
3435
+ return void 0;
3436
+ }
3437
+ function isRejectedPartyValue(value) {
3438
+ const clean = normalizeWhitespace3(value);
3439
+ return /^(?:and address|of record|insurer|carrier|security|broker|producer|agent|the|a|an|is|are|was|were|agrees?|means?|includes?|shall|will)\b/i.test(clean);
3440
+ }
3441
+ function identityWithoutAddress(value) {
3442
+ const clean = cleanValue(value.replace(/\b(?:phone|tel|telephone|email|e-mail)\b.*$/i, "").replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b.*$/i, ""));
3443
+ if (!clean) return void 0;
3444
+ const beforeStreet = clean.match(/^(.+?)\s+\d{1,6}\s+[A-Za-z0-9.'-]+(?:\s+[A-Za-z0-9.'-]+){0,5}\s+(?:street|st\.?|avenue|ave\.?|road|rd\.?|drive|dr\.?|lane|ln\.?|boulevard|blvd\.?|suite|ste\.?|floor|fl\.?|way|court|ct\.?)\b/i)?.[1];
3445
+ const beforeCityState = clean.match(/^(.+?)\s+[A-Z][A-Za-z.'-]+(?:\s+[A-Z][A-Za-z.'-]+)*,\s*[A-Z]{2}\s+\d{5}(?:-\d{4})?\b/)?.[1];
3446
+ return cleanValue(beforeStreet ?? beforeCityState ?? clean);
3447
+ }
3448
+ function cleanPartyIdentity(value, clean) {
3449
+ const raw = cleanValue(value);
3450
+ if (!raw || isRejectedPartyValue(raw)) return void 0;
3451
+ const identity = identityWithoutAddress(raw);
3452
+ const cleaned = identity ? clean(identity) : void 0;
3453
+ if (!cleaned || isRejectedPartyValue(cleaned)) return void 0;
3454
+ return {
3455
+ value: cleaned,
3456
+ ...cleaned !== raw ? { normalizedValue: cleaned } : {}
3457
+ };
3458
+ }
3459
+ function partyFromTableRows(nodes, wanted, clean) {
3460
+ const children = childMap(nodes);
3461
+ for (const row of compactFactNodes(nodes).filter((node) => node.kind === "table_row")) {
3462
+ const cells = cellRows(row, children);
3463
+ for (const [index, cell] of cells.entries()) {
3464
+ const labelKind = partyLabelKind(cell.value) ?? partyLabelKind(cell.label);
3465
+ if (labelKind !== wanted) continue;
3466
+ const valueCell = cells.slice(index + 1).find((candidate) => !partyLabelKind(candidate.value) && !partyLabelKind(candidate.label));
3467
+ if (!valueCell) continue;
3468
+ const cleaned = cleanPartyIdentity(valueCell.value, clean);
3469
+ if (cleaned) return valueFromNodes([row, valueCell.node], cleaned.value, "high", cleaned.normalizedValue);
3470
+ }
3471
+ const parts = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row)).split(/\s+\|\s+|\t/).map(cleanValue).filter((part) => Boolean(part));
3472
+ for (const [index, part] of parts.entries()) {
3473
+ const labelKind = partyLabelKind(part);
3474
+ if (labelKind !== wanted) continue;
3475
+ const cleaned = cleanPartyIdentity(parts[index + 1] ?? "", clean);
3476
+ if (cleaned) return valueFromNodes([row], cleaned.value, "high", cleaned.normalizedValue);
3477
+ }
3478
+ }
3479
+ return void 0;
3480
+ }
3403
3481
  function namedInsuredFromNodes(nodes) {
3404
- return firstCleanMatch(nodes, [
3405
- /\b(?:named insured|insured name|insured persons?|insured person)\s*:?\s*(.+?)(?=\s+(?:insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification|date this)\b|[|;\n]|$)/i,
3482
+ return partyFromTableRows(nodes, "namedInsured", cleanNamedInsured) ?? firstCleanMatch(nodes, [
3483
+ /\b(?:named insured|insured name)\s*:?\s*(.+?)(?=\s+(?:insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification|date this)\b|[|;\n]|$)/i,
3484
+ /\b(?:insured persons?|insured person)\s*:\s*(.+?)(?=\s+(?:insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification|date this)\b|[|;\n]|$)/i,
3406
3485
  /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
3407
3486
  ], cleanNamedInsured);
3408
3487
  }
3409
3488
  function cleanInsurer(value) {
3410
3489
  const clean = cleanValue(value);
3411
3490
  if (!clean || clean.length > 140) return void 0;
3412
- if (/^(mean|means|we|us|our)\b/i.test(clean)) return void 0;
3491
+ if (/^(mean|means|we|us|our|the|a|an|agrees?|shall|will)\b/i.test(clean)) return void 0;
3413
3492
  if (/\b(table of contents|policy wording|provided solely|convenience)\b/i.test(clean)) return void 0;
3414
3493
  const known = clean.match(/\b(Sun Life Assurance Company of Canada|Manulife|The Manufacturers Life Insurance Company)\b/i)?.[1];
3415
3494
  return known ?? clean;
3416
3495
  }
3417
3496
  function insurerFromNodes(nodes) {
3497
+ const tableValue = partyFromTableRows(nodes, "insurer", cleanInsurer);
3498
+ if (tableValue) return tableValue;
3499
+ for (const node of compactFactNodes(nodes)) {
3500
+ const text = nodeText(node);
3501
+ const value = cleanInsurer(text.match(/\b([A-Z][A-Za-z&.,' -]{2,120}?(?:Insurance|Assurance|Indemnity|Casualty|Underwriting|Mutual|Risk|Reinsurance)\s+Company(?:\s+of\s+[A-Z][A-Za-z .'-]+)?)\s*\(\s*the\s+["']?Insurer["']?\s*\)/i)?.[1] ?? "");
3502
+ if (value) return valueFromNode(node, value, "high");
3503
+ }
3418
3504
  return firstCleanMatch(nodes, [
3419
3505
  /\bunderwritten by\s+([^|;\n.]{3,160})/i,
3420
3506
  /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
@@ -3460,6 +3546,19 @@ function coverageNameFromRow(text) {
3460
3546
  );
3461
3547
  return cleanCoverageLabel(first);
3462
3548
  }
3549
+ function isDeclarationLimitLabel(value) {
3550
+ const label = normalizeLabel(value);
3551
+ return /\bitem\s*\d+[.)]?\s*limits?\s+of\s+liability\b/.test(label) || /^limits?\s+of\s+liability$/.test(label) || /^policy\s+limits?$/.test(label);
3552
+ }
3553
+ function declarationCoverageNameFromRow(row, children) {
3554
+ const cells = cellRows(row, children);
3555
+ const candidates = [
3556
+ row.title,
3557
+ ...cells.flatMap((cell) => [cell.label, cell.value])
3558
+ ];
3559
+ if (!candidates.some(isDeclarationLimitLabel)) return void 0;
3560
+ return candidates.some((candidate) => /\blimits?\s+of\s+liability\b/i.test(candidate ?? "")) ? "Limits of Liability" : "Policy Limits";
3561
+ }
3463
3562
  function limitFromText(text) {
3464
3563
  return moneyValue(
3465
3564
  text.match(/\b(?:limit|liability|aggregate|occurrence|claim)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?|\$?\d[\d,]*\s*(?:each|per|aggregate)[^|;]*)/i)?.[1] ?? text.match(/(\$\s?\d[\d,]*(?:\.\d{2})?)(?=.*\b(limit|aggregate|occurrence|claim|liability)\b)/i)?.[1]
@@ -3469,10 +3568,22 @@ function deductibleFromText(text) {
3469
3568
  return moneyValue(text.match(/\b(?:deductible|retention|sir)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3470
3569
  }
3471
3570
  function premiumFromText(text) {
3472
- return moneyValue(text.match(/\b(?:premium|total premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3571
+ return premiumValue(text.match(/\b(?:premium|total premium|total cost|amount due)\b(?:\s*(?:is|:|-))?\s*(\$?(?:\d[\d,]*(?:\.\d{2})?|X{2,}(?:,X{3})*(?:\.X{2})?))/i)?.[1]);
3572
+ }
3573
+ function premiumFromNodes(nodes) {
3574
+ for (const node of compactFactNodes(nodes)) {
3575
+ const value = premiumFromText(nodeText(node));
3576
+ if (value) return valueFromNode(node, value, "high");
3577
+ }
3578
+ return void 0;
3473
3579
  }
3474
3580
  function moneyAmount(value) {
3475
- const match = value?.match(/\$?\s*([0-9][0-9,]*(?:\.\d+)?)/);
3581
+ const clean = cleanValue(value);
3582
+ if (!clean) return void 0;
3583
+ const currency = clean.match(/\$\s*([0-9][0-9,]*(?:\.\d+)?)/);
3584
+ const percent = clean.match(/\b([0-9][0-9,]*(?:\.\d+)?)\s*%/);
3585
+ const explicitNumeric = !/\bitem\s*\d+\b/i.test(clean) && /\b(?:limit|aggregate|claim|occurrence|loss|retention|deductible|sir|premium|amount)\b/i.test(clean) ? clean.match(/\b([0-9][0-9,]*(?:\.\d+)?)\b/) : void 0;
3586
+ const match = currency ?? percent ?? explicitNumeric;
3476
3587
  if (!match) return void 0;
3477
3588
  const amount = Number(match[1].replace(/,/g, ""));
3478
3589
  return Number.isFinite(amount) ? amount : void 0;
@@ -3518,6 +3629,10 @@ function isValueCell(label, value) {
3518
3629
  }
3519
3630
  return /\b(limit|aggregate|retention|deductible|sir|retroactive|premium|amount)\b/.test(normalizedLabel) || moneyAmount(value) !== void 0 || /\b(full prior acts|none|included|not included|as stated)\b/.test(normalizedValue);
3520
3631
  }
3632
+ function isCoverageTermLabel(value) {
3633
+ const label = normalizeLabel(value);
3634
+ return /\b(limit|aggregate|retention|deductible|sir|retroactive|premium|amount|sub[-\s]?limit)\b/.test(label);
3635
+ }
3521
3636
  function isNameCell(label, value) {
3522
3637
  const normalizedLabel = normalizeLabel(label);
3523
3638
  const normalizedValue = normalizeLabel(value);
@@ -3619,6 +3734,16 @@ function termFromCell(params) {
3619
3734
  function termsFromRow(row, children) {
3620
3735
  const cells = cellRows(row, children);
3621
3736
  if (cells.length > 0) {
3737
+ const pairedTerms = [];
3738
+ for (const [index, cell] of cells.entries()) {
3739
+ const next = cells[index + 1];
3740
+ if (!next) continue;
3741
+ if (!isCoverageTermLabel(cell.value)) continue;
3742
+ if (isCoverageTermLabel(next.value) && moneyAmount(next.value) === void 0) continue;
3743
+ const term = termFromCell({ row, cell: next.node, label: cell.value, value: next.value });
3744
+ if (term) pairedTerms.push(term);
3745
+ }
3746
+ if (pairedTerms.length > 0) return pairedTerms;
3622
3747
  return cells.filter((cell) => isValueCell(cell.label, cell.value)).map((cell) => termFromCell({ row, cell: cell.node, label: cell.label, value: cell.value })).filter((term) => Boolean(term));
3623
3748
  }
3624
3749
  const text = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row));
@@ -3645,6 +3770,8 @@ function termsFromRow(row, children) {
3645
3770
  return terms;
3646
3771
  }
3647
3772
  function nameFromRow(row, children) {
3773
+ const declarationName = declarationCoverageNameFromRow(row, children);
3774
+ if (declarationName) return declarationName;
3648
3775
  const cells = cellRows(row, children);
3649
3776
  const named = cells.find((cell) => isNameCell(cell.label, cell.value));
3650
3777
  if (named) return cleanValue(named.value);
@@ -3822,7 +3949,7 @@ function buildParties(profile) {
3822
3949
  if (profile.namedInsured) {
3823
3950
  parties.push({
3824
3951
  role: "named_insured",
3825
- name: profile.namedInsured.value,
3952
+ name: profile.namedInsured.normalizedValue ?? profile.namedInsured.value,
3826
3953
  sourceNodeIds: profile.namedInsured.sourceNodeIds,
3827
3954
  sourceSpanIds: profile.namedInsured.sourceSpanIds
3828
3955
  });
@@ -3830,7 +3957,7 @@ function buildParties(profile) {
3830
3957
  if (profile.insurer) {
3831
3958
  parties.push({
3832
3959
  role: "insurer",
3833
- name: profile.insurer.value,
3960
+ name: profile.insurer.normalizedValue ?? profile.insurer.value,
3834
3961
  sourceNodeIds: profile.insurer.sourceNodeIds,
3835
3962
  sourceSpanIds: profile.insurer.sourceSpanIds
3836
3963
  });
@@ -3838,7 +3965,7 @@ function buildParties(profile) {
3838
3965
  if (profile.broker) {
3839
3966
  parties.push({
3840
3967
  role: "broker",
3841
- name: profile.broker.value,
3968
+ name: profile.broker.normalizedValue ?? profile.broker.value,
3842
3969
  sourceNodeIds: profile.broker.sourceNodeIds,
3843
3970
  sourceSpanIds: profile.broker.sourceSpanIds
3844
3971
  });
@@ -3891,9 +4018,7 @@ function buildDeterministicOperationalProfile(params) {
3891
4018
  retroactiveDate: firstMatch(nodes, [
3892
4019
  /\bretroactive date\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}|full prior acts|none)/i
3893
4020
  ]),
3894
- premium: firstMatch(nodes, [
3895
- /\b(?:total premium|premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i
3896
- ])
4021
+ premium: premiumFromNodes(nodes)
3897
4022
  };
3898
4023
  const coverages = buildCoverages(nodes);
3899
4024
  const coverageTypes = [...new Set(coverages.map((coverage) => coverage.name))];
@@ -11277,9 +11402,19 @@ function sourceTreeToOutline(sourceTree) {
11277
11402
  });
11278
11403
  return (byParent.get(root?.id) ?? []).map(visit);
11279
11404
  }
11405
+ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
11406
+ "policyNumber",
11407
+ "namedInsured",
11408
+ "insurer",
11409
+ "broker"
11410
+ ]);
11280
11411
  function valueOf(profile, key) {
11281
11412
  const value = profile[key];
11282
- return value && typeof value === "object" && !Array.isArray(value) && "value" in value ? String(value.value) : void 0;
11413
+ if (!value || typeof value !== "object" || Array.isArray(value) || !("value" in value)) return void 0;
11414
+ if (NORMALIZED_COMPATIBILITY_FIELDS.has(key) && "normalizedValue" in value && typeof value.normalizedValue === "string" && value.normalizedValue.trim()) {
11415
+ return value.normalizedValue;
11416
+ }
11417
+ return String(value.value);
11283
11418
  }
11284
11419
  function materializeDocument(params) {
11285
11420
  const profile = params.operationalProfile;