@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.mjs CHANGED
@@ -2981,6 +2981,14 @@ function valueFromNode(node, value, confidence = "medium") {
2981
2981
  sourceSpanIds: node.sourceSpanIds
2982
2982
  };
2983
2983
  }
2984
+ function valueFromNodes(nodes, value, confidence = "medium", normalizedValue) {
2985
+ return {
2986
+ value,
2987
+ ...normalizedValue ? { normalizedValue } : {},
2988
+ confidence,
2989
+ ...sourceIds(nodes)
2990
+ };
2991
+ }
2984
2992
  function firstMatch(nodes, patterns) {
2985
2993
  for (const node of nodes) {
2986
2994
  const text = nodeText(node);
@@ -3057,8 +3065,65 @@ function cleanNamedInsured(value) {
3057
3065
  }
3058
3066
  return clean;
3059
3067
  }
3068
+ var PARTY_LABEL_PATTERNS = {
3069
+ namedInsured: /^(?:item\s*\d+[.)]?\s*)?(?:named insured(?:\s+and\s+address)?|insured name)$/i,
3070
+ insurer: /^(?:carrier|insurer|security)$/i,
3071
+ broker: /^(?:broker(?:\s+of\s+record)?|producer|agent)$/i
3072
+ };
3073
+ function partyLabelKind(value) {
3074
+ const clean = cleanValue(value.replace(/^\s*column\s+\d+\s*:\s*/i, ""));
3075
+ if (!clean) return void 0;
3076
+ if (PARTY_LABEL_PATTERNS.namedInsured.test(clean)) return "namedInsured";
3077
+ if (PARTY_LABEL_PATTERNS.insurer.test(clean)) return "insurer";
3078
+ if (PARTY_LABEL_PATTERNS.broker.test(clean)) return "broker";
3079
+ return void 0;
3080
+ }
3081
+ function isRejectedPartyValue(value) {
3082
+ const clean = normalizeWhitespace3(value);
3083
+ 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);
3084
+ }
3085
+ function identityWithoutAddress(value) {
3086
+ 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, ""));
3087
+ if (!clean) return void 0;
3088
+ 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];
3089
+ 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];
3090
+ return cleanValue(beforeStreet ?? beforeCityState ?? clean);
3091
+ }
3092
+ function cleanPartyIdentity(value, clean) {
3093
+ const raw = cleanValue(value);
3094
+ if (!raw || isRejectedPartyValue(raw)) return void 0;
3095
+ const identity = identityWithoutAddress(raw);
3096
+ const cleaned = identity ? clean(identity) : void 0;
3097
+ if (!cleaned || isRejectedPartyValue(cleaned)) return void 0;
3098
+ return {
3099
+ value: cleaned,
3100
+ ...cleaned !== raw ? { normalizedValue: cleaned } : {}
3101
+ };
3102
+ }
3103
+ function partyFromTableRows(nodes, wanted, clean) {
3104
+ const children = childMap(nodes);
3105
+ for (const row of compactFactNodes(nodes).filter((node) => node.kind === "table_row")) {
3106
+ const cells = cellRows(row, children);
3107
+ for (const [index, cell] of cells.entries()) {
3108
+ const labelKind = partyLabelKind(cell.value) ?? partyLabelKind(cell.label);
3109
+ if (labelKind !== wanted) continue;
3110
+ const valueCell = cells.slice(index + 1).find((candidate) => !partyLabelKind(candidate.value) && !partyLabelKind(candidate.label));
3111
+ if (!valueCell) continue;
3112
+ const cleaned = cleanPartyIdentity(valueCell.value, clean);
3113
+ if (cleaned) return valueFromNodes([row, valueCell.node], cleaned.value, "high", cleaned.normalizedValue);
3114
+ }
3115
+ const parts = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row)).split(/\s+\|\s+|\t/).map(cleanValue).filter((part) => Boolean(part));
3116
+ for (const [index, part] of parts.entries()) {
3117
+ const labelKind = partyLabelKind(part);
3118
+ if (labelKind !== wanted) continue;
3119
+ const cleaned = cleanPartyIdentity(parts[index + 1] ?? "", clean);
3120
+ if (cleaned) return valueFromNodes([row], cleaned.value, "high", cleaned.normalizedValue);
3121
+ }
3122
+ }
3123
+ return void 0;
3124
+ }
3060
3125
  function namedInsuredFromNodes(nodes) {
3061
- return firstCleanMatch(nodes, [
3126
+ return partyFromTableRows(nodes, "namedInsured", cleanNamedInsured) ?? firstCleanMatch(nodes, [
3062
3127
  /\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,
3063
3128
  /\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,
3064
3129
  /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
@@ -3067,12 +3132,19 @@ function namedInsuredFromNodes(nodes) {
3067
3132
  function cleanInsurer(value) {
3068
3133
  const clean = cleanValue(value);
3069
3134
  if (!clean || clean.length > 140) return void 0;
3070
- if (/^(mean|means|we|us|our)\b/i.test(clean)) return void 0;
3135
+ if (/^(mean|means|we|us|our|the|a|an|agrees?|shall|will)\b/i.test(clean)) return void 0;
3071
3136
  if (/\b(table of contents|policy wording|provided solely|convenience)\b/i.test(clean)) return void 0;
3072
3137
  const known = clean.match(/\b(Sun Life Assurance Company of Canada|Manulife|The Manufacturers Life Insurance Company)\b/i)?.[1];
3073
3138
  return known ?? clean;
3074
3139
  }
3075
3140
  function insurerFromNodes(nodes) {
3141
+ const tableValue = partyFromTableRows(nodes, "insurer", cleanInsurer);
3142
+ if (tableValue) return tableValue;
3143
+ for (const node of compactFactNodes(nodes)) {
3144
+ const text = nodeText(node);
3145
+ 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] ?? "");
3146
+ if (value) return valueFromNode(node, value, "high");
3147
+ }
3076
3148
  return firstCleanMatch(nodes, [
3077
3149
  /\bunderwritten by\s+([^|;\n.]{3,160})/i,
3078
3150
  /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
@@ -3118,6 +3190,19 @@ function coverageNameFromRow(text) {
3118
3190
  );
3119
3191
  return cleanCoverageLabel(first);
3120
3192
  }
3193
+ function isDeclarationLimitLabel(value) {
3194
+ const label = normalizeLabel(value);
3195
+ 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);
3196
+ }
3197
+ function declarationCoverageNameFromRow(row, children) {
3198
+ const cells = cellRows(row, children);
3199
+ const candidates = [
3200
+ row.title,
3201
+ ...cells.flatMap((cell) => [cell.label, cell.value])
3202
+ ];
3203
+ if (!candidates.some(isDeclarationLimitLabel)) return void 0;
3204
+ return candidates.some((candidate) => /\blimits?\s+of\s+liability\b/i.test(candidate ?? "")) ? "Limits of Liability" : "Policy Limits";
3205
+ }
3121
3206
  function limitFromText(text) {
3122
3207
  return moneyValue(
3123
3208
  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]
@@ -3137,7 +3222,12 @@ function premiumFromNodes(nodes) {
3137
3222
  return void 0;
3138
3223
  }
3139
3224
  function moneyAmount(value) {
3140
- const match = value?.match(/\$?\s*([0-9][0-9,]*(?:\.\d+)?)/);
3225
+ const clean = cleanValue(value);
3226
+ if (!clean) return void 0;
3227
+ const currency = clean.match(/\$\s*([0-9][0-9,]*(?:\.\d+)?)/);
3228
+ const percent = clean.match(/\b([0-9][0-9,]*(?:\.\d+)?)\s*%/);
3229
+ 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;
3230
+ const match = currency ?? percent ?? explicitNumeric;
3141
3231
  if (!match) return void 0;
3142
3232
  const amount = Number(match[1].replace(/,/g, ""));
3143
3233
  return Number.isFinite(amount) ? amount : void 0;
@@ -3183,6 +3273,10 @@ function isValueCell(label, value) {
3183
3273
  }
3184
3274
  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);
3185
3275
  }
3276
+ function isCoverageTermLabel(value) {
3277
+ const label = normalizeLabel(value);
3278
+ return /\b(limit|aggregate|retention|deductible|sir|retroactive|premium|amount|sub[-\s]?limit)\b/.test(label);
3279
+ }
3186
3280
  function isNameCell(label, value) {
3187
3281
  const normalizedLabel = normalizeLabel(label);
3188
3282
  const normalizedValue = normalizeLabel(value);
@@ -3284,6 +3378,16 @@ function termFromCell(params) {
3284
3378
  function termsFromRow(row, children) {
3285
3379
  const cells = cellRows(row, children);
3286
3380
  if (cells.length > 0) {
3381
+ const pairedTerms = [];
3382
+ for (const [index, cell] of cells.entries()) {
3383
+ const next = cells[index + 1];
3384
+ if (!next) continue;
3385
+ if (!isCoverageTermLabel(cell.value)) continue;
3386
+ if (isCoverageTermLabel(next.value) && moneyAmount(next.value) === void 0) continue;
3387
+ const term = termFromCell({ row, cell: next.node, label: cell.value, value: next.value });
3388
+ if (term) pairedTerms.push(term);
3389
+ }
3390
+ if (pairedTerms.length > 0) return pairedTerms;
3287
3391
  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));
3288
3392
  }
3289
3393
  const text = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row));
@@ -3310,6 +3414,8 @@ function termsFromRow(row, children) {
3310
3414
  return terms;
3311
3415
  }
3312
3416
  function nameFromRow(row, children) {
3417
+ const declarationName = declarationCoverageNameFromRow(row, children);
3418
+ if (declarationName) return declarationName;
3313
3419
  const cells = cellRows(row, children);
3314
3420
  const named = cells.find((cell) => isNameCell(cell.label, cell.value));
3315
3421
  if (named) return cleanValue(named.value);
@@ -3487,7 +3593,7 @@ function buildParties(profile) {
3487
3593
  if (profile.namedInsured) {
3488
3594
  parties.push({
3489
3595
  role: "named_insured",
3490
- name: profile.namedInsured.value,
3596
+ name: profile.namedInsured.normalizedValue ?? profile.namedInsured.value,
3491
3597
  sourceNodeIds: profile.namedInsured.sourceNodeIds,
3492
3598
  sourceSpanIds: profile.namedInsured.sourceSpanIds
3493
3599
  });
@@ -3495,7 +3601,7 @@ function buildParties(profile) {
3495
3601
  if (profile.insurer) {
3496
3602
  parties.push({
3497
3603
  role: "insurer",
3498
- name: profile.insurer.value,
3604
+ name: profile.insurer.normalizedValue ?? profile.insurer.value,
3499
3605
  sourceNodeIds: profile.insurer.sourceNodeIds,
3500
3606
  sourceSpanIds: profile.insurer.sourceSpanIds
3501
3607
  });
@@ -3503,7 +3609,7 @@ function buildParties(profile) {
3503
3609
  if (profile.broker) {
3504
3610
  parties.push({
3505
3611
  role: "broker",
3506
- name: profile.broker.value,
3612
+ name: profile.broker.normalizedValue ?? profile.broker.value,
3507
3613
  sourceNodeIds: profile.broker.sourceNodeIds,
3508
3614
  sourceSpanIds: profile.broker.sourceSpanIds
3509
3615
  });
@@ -10948,9 +11054,19 @@ function sourceTreeToOutline(sourceTree) {
10948
11054
  });
10949
11055
  return (byParent.get(root?.id) ?? []).map(visit);
10950
11056
  }
11057
+ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
11058
+ "policyNumber",
11059
+ "namedInsured",
11060
+ "insurer",
11061
+ "broker"
11062
+ ]);
10951
11063
  function valueOf(profile, key) {
10952
11064
  const value = profile[key];
10953
- return value && typeof value === "object" && !Array.isArray(value) && "value" in value ? String(value.value) : void 0;
11065
+ if (!value || typeof value !== "object" || Array.isArray(value) || !("value" in value)) return void 0;
11066
+ if (NORMALIZED_COMPATIBILITY_FIELDS.has(key) && "normalizedValue" in value && typeof value.normalizedValue === "string" && value.normalizedValue.trim()) {
11067
+ return value.normalizedValue;
11068
+ }
11069
+ return String(value.value);
10954
11070
  }
10955
11071
  function materializeDocument(params) {
10956
11072
  const profile = params.operationalProfile;