@claritylabs/cl-sdk 3.0.32 → 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
@@ -3337,6 +3337,14 @@ function valueFromNode(node, value, confidence = "medium") {
3337
3337
  sourceSpanIds: node.sourceSpanIds
3338
3338
  };
3339
3339
  }
3340
+ function valueFromNodes(nodes, value, confidence = "medium", normalizedValue) {
3341
+ return {
3342
+ value,
3343
+ ...normalizedValue ? { normalizedValue } : {},
3344
+ confidence,
3345
+ ...sourceIds(nodes)
3346
+ };
3347
+ }
3340
3348
  function firstMatch(nodes, patterns) {
3341
3349
  for (const node of nodes) {
3342
3350
  const text = nodeText(node);
@@ -3413,8 +3421,65 @@ function cleanNamedInsured(value) {
3413
3421
  }
3414
3422
  return clean;
3415
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
+ }
3416
3481
  function namedInsuredFromNodes(nodes) {
3417
- return firstCleanMatch(nodes, [
3482
+ return partyFromTableRows(nodes, "namedInsured", cleanNamedInsured) ?? firstCleanMatch(nodes, [
3418
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,
3419
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,
3420
3485
  /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
@@ -3423,12 +3488,19 @@ function namedInsuredFromNodes(nodes) {
3423
3488
  function cleanInsurer(value) {
3424
3489
  const clean = cleanValue(value);
3425
3490
  if (!clean || clean.length > 140) return void 0;
3426
- 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;
3427
3492
  if (/\b(table of contents|policy wording|provided solely|convenience)\b/i.test(clean)) return void 0;
3428
3493
  const known = clean.match(/\b(Sun Life Assurance Company of Canada|Manulife|The Manufacturers Life Insurance Company)\b/i)?.[1];
3429
3494
  return known ?? clean;
3430
3495
  }
3431
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
+ }
3432
3504
  return firstCleanMatch(nodes, [
3433
3505
  /\bunderwritten by\s+([^|;\n.]{3,160})/i,
3434
3506
  /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
@@ -3474,6 +3546,19 @@ function coverageNameFromRow(text) {
3474
3546
  );
3475
3547
  return cleanCoverageLabel(first);
3476
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
+ }
3477
3562
  function limitFromText(text) {
3478
3563
  return moneyValue(
3479
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]
@@ -3493,7 +3578,12 @@ function premiumFromNodes(nodes) {
3493
3578
  return void 0;
3494
3579
  }
3495
3580
  function moneyAmount(value) {
3496
- 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;
3497
3587
  if (!match) return void 0;
3498
3588
  const amount = Number(match[1].replace(/,/g, ""));
3499
3589
  return Number.isFinite(amount) ? amount : void 0;
@@ -3539,6 +3629,10 @@ function isValueCell(label, value) {
3539
3629
  }
3540
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);
3541
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
+ }
3542
3636
  function isNameCell(label, value) {
3543
3637
  const normalizedLabel = normalizeLabel(label);
3544
3638
  const normalizedValue = normalizeLabel(value);
@@ -3640,6 +3734,16 @@ function termFromCell(params) {
3640
3734
  function termsFromRow(row, children) {
3641
3735
  const cells = cellRows(row, children);
3642
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;
3643
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));
3644
3748
  }
3645
3749
  const text = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row));
@@ -3666,6 +3770,8 @@ function termsFromRow(row, children) {
3666
3770
  return terms;
3667
3771
  }
3668
3772
  function nameFromRow(row, children) {
3773
+ const declarationName = declarationCoverageNameFromRow(row, children);
3774
+ if (declarationName) return declarationName;
3669
3775
  const cells = cellRows(row, children);
3670
3776
  const named = cells.find((cell) => isNameCell(cell.label, cell.value));
3671
3777
  if (named) return cleanValue(named.value);
@@ -3843,7 +3949,7 @@ function buildParties(profile) {
3843
3949
  if (profile.namedInsured) {
3844
3950
  parties.push({
3845
3951
  role: "named_insured",
3846
- name: profile.namedInsured.value,
3952
+ name: profile.namedInsured.normalizedValue ?? profile.namedInsured.value,
3847
3953
  sourceNodeIds: profile.namedInsured.sourceNodeIds,
3848
3954
  sourceSpanIds: profile.namedInsured.sourceSpanIds
3849
3955
  });
@@ -3851,7 +3957,7 @@ function buildParties(profile) {
3851
3957
  if (profile.insurer) {
3852
3958
  parties.push({
3853
3959
  role: "insurer",
3854
- name: profile.insurer.value,
3960
+ name: profile.insurer.normalizedValue ?? profile.insurer.value,
3855
3961
  sourceNodeIds: profile.insurer.sourceNodeIds,
3856
3962
  sourceSpanIds: profile.insurer.sourceSpanIds
3857
3963
  });
@@ -3859,7 +3965,7 @@ function buildParties(profile) {
3859
3965
  if (profile.broker) {
3860
3966
  parties.push({
3861
3967
  role: "broker",
3862
- name: profile.broker.value,
3968
+ name: profile.broker.normalizedValue ?? profile.broker.value,
3863
3969
  sourceNodeIds: profile.broker.sourceNodeIds,
3864
3970
  sourceSpanIds: profile.broker.sourceSpanIds
3865
3971
  });
@@ -11296,9 +11402,19 @@ function sourceTreeToOutline(sourceTree) {
11296
11402
  });
11297
11403
  return (byParent.get(root?.id) ?? []).map(visit);
11298
11404
  }
11405
+ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
11406
+ "policyNumber",
11407
+ "namedInsured",
11408
+ "insurer",
11409
+ "broker"
11410
+ ]);
11299
11411
  function valueOf(profile, key) {
11300
11412
  const value = profile[key];
11301
- 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);
11302
11418
  }
11303
11419
  function materializeDocument(params) {
11304
11420
  const profile = params.operationalProfile;