@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.mjs CHANGED
@@ -2957,6 +2957,15 @@ function moneyValue(value) {
2957
2957
  if (/^\d{1,3}(?:,\d{3})*(?:\.\d{2})?$/.test(clean)) return `$${clean}`;
2958
2958
  return clean;
2959
2959
  }
2960
+ function premiumValue(value) {
2961
+ const clean = cleanValue(value);
2962
+ if (!clean) return void 0;
2963
+ if (/\$[A-Z0-9]/i.test(clean)) return clean;
2964
+ if (/\b(?:CAD|USD)\b/i.test(clean) && /(?:\d|X{2,})/i.test(clean)) return clean;
2965
+ if (/^\d{1,3}(?:,\d{3})+\.\d{2}$/.test(clean) || /^\d+\.\d{2}$/.test(clean)) return `$${clean}`;
2966
+ if (/^X{2,}(?:,X{3})*(?:\.X{2})$/i.test(clean)) return clean;
2967
+ return void 0;
2968
+ }
2960
2969
  function nodeText(node) {
2961
2970
  return normalizeWhitespace3([
2962
2971
  node.title,
@@ -2972,6 +2981,14 @@ function valueFromNode(node, value, confidence = "medium") {
2972
2981
  sourceSpanIds: node.sourceSpanIds
2973
2982
  };
2974
2983
  }
2984
+ function valueFromNodes(nodes, value, confidence = "medium", normalizedValue) {
2985
+ return {
2986
+ value,
2987
+ ...normalizedValue ? { normalizedValue } : {},
2988
+ confidence,
2989
+ ...sourceIds(nodes)
2990
+ };
2991
+ }
2975
2992
  function firstMatch(nodes, patterns) {
2976
2993
  for (const node of nodes) {
2977
2994
  const text = nodeText(node);
@@ -3038,27 +3055,96 @@ function firstCleanMatch(nodes, patterns, clean) {
3038
3055
  function cleanNamedInsured(value) {
3039
3056
  const clean = cleanValue(value.replace(/\bborn\s+on\b.*$/i, "").replace(/\bage\s+nearest\b.*$/i, "").replace(/\bbeneficiary\b.*$/i, ""));
3040
3057
  if (!clean || clean.length > 160) return void 0;
3058
+ if (!/[A-Za-z0-9]/.test(clean)) return void 0;
3041
3059
  if (/^(person|persons)\b/i.test(clean)) return void 0;
3060
+ if (/^(insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification)\b/i.test(clean)) {
3061
+ return void 0;
3062
+ }
3042
3063
  if (/\b(table of contents|policy wording|provided solely|convenience|not to be construed|actual policy issued)\b/i.test(clean)) {
3043
3064
  return void 0;
3044
3065
  }
3045
3066
  return clean;
3046
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
+ }
3047
3125
  function namedInsuredFromNodes(nodes) {
3048
- return firstCleanMatch(nodes, [
3049
- /\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,
3126
+ return partyFromTableRows(nodes, "namedInsured", cleanNamedInsured) ?? firstCleanMatch(nodes, [
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,
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,
3050
3129
  /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
3051
3130
  ], cleanNamedInsured);
3052
3131
  }
3053
3132
  function cleanInsurer(value) {
3054
3133
  const clean = cleanValue(value);
3055
3134
  if (!clean || clean.length > 140) return void 0;
3056
- 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;
3057
3136
  if (/\b(table of contents|policy wording|provided solely|convenience)\b/i.test(clean)) return void 0;
3058
3137
  const known = clean.match(/\b(Sun Life Assurance Company of Canada|Manulife|The Manufacturers Life Insurance Company)\b/i)?.[1];
3059
3138
  return known ?? clean;
3060
3139
  }
3061
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
+ }
3062
3148
  return firstCleanMatch(nodes, [
3063
3149
  /\bunderwritten by\s+([^|;\n.]{3,160})/i,
3064
3150
  /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
@@ -3104,6 +3190,19 @@ function coverageNameFromRow(text) {
3104
3190
  );
3105
3191
  return cleanCoverageLabel(first);
3106
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
+ }
3107
3206
  function limitFromText(text) {
3108
3207
  return moneyValue(
3109
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]
@@ -3113,10 +3212,22 @@ function deductibleFromText(text) {
3113
3212
  return moneyValue(text.match(/\b(?:deductible|retention|sir)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3114
3213
  }
3115
3214
  function premiumFromText(text) {
3116
- return moneyValue(text.match(/\b(?:premium|total premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3215
+ 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]);
3216
+ }
3217
+ function premiumFromNodes(nodes) {
3218
+ for (const node of compactFactNodes(nodes)) {
3219
+ const value = premiumFromText(nodeText(node));
3220
+ if (value) return valueFromNode(node, value, "high");
3221
+ }
3222
+ return void 0;
3117
3223
  }
3118
3224
  function moneyAmount(value) {
3119
- 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;
3120
3231
  if (!match) return void 0;
3121
3232
  const amount = Number(match[1].replace(/,/g, ""));
3122
3233
  return Number.isFinite(amount) ? amount : void 0;
@@ -3162,6 +3273,10 @@ function isValueCell(label, value) {
3162
3273
  }
3163
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);
3164
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
+ }
3165
3280
  function isNameCell(label, value) {
3166
3281
  const normalizedLabel = normalizeLabel(label);
3167
3282
  const normalizedValue = normalizeLabel(value);
@@ -3263,6 +3378,16 @@ function termFromCell(params) {
3263
3378
  function termsFromRow(row, children) {
3264
3379
  const cells = cellRows(row, children);
3265
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;
3266
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));
3267
3392
  }
3268
3393
  const text = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row));
@@ -3289,6 +3414,8 @@ function termsFromRow(row, children) {
3289
3414
  return terms;
3290
3415
  }
3291
3416
  function nameFromRow(row, children) {
3417
+ const declarationName = declarationCoverageNameFromRow(row, children);
3418
+ if (declarationName) return declarationName;
3292
3419
  const cells = cellRows(row, children);
3293
3420
  const named = cells.find((cell) => isNameCell(cell.label, cell.value));
3294
3421
  if (named) return cleanValue(named.value);
@@ -3466,7 +3593,7 @@ function buildParties(profile) {
3466
3593
  if (profile.namedInsured) {
3467
3594
  parties.push({
3468
3595
  role: "named_insured",
3469
- name: profile.namedInsured.value,
3596
+ name: profile.namedInsured.normalizedValue ?? profile.namedInsured.value,
3470
3597
  sourceNodeIds: profile.namedInsured.sourceNodeIds,
3471
3598
  sourceSpanIds: profile.namedInsured.sourceSpanIds
3472
3599
  });
@@ -3474,7 +3601,7 @@ function buildParties(profile) {
3474
3601
  if (profile.insurer) {
3475
3602
  parties.push({
3476
3603
  role: "insurer",
3477
- name: profile.insurer.value,
3604
+ name: profile.insurer.normalizedValue ?? profile.insurer.value,
3478
3605
  sourceNodeIds: profile.insurer.sourceNodeIds,
3479
3606
  sourceSpanIds: profile.insurer.sourceSpanIds
3480
3607
  });
@@ -3482,7 +3609,7 @@ function buildParties(profile) {
3482
3609
  if (profile.broker) {
3483
3610
  parties.push({
3484
3611
  role: "broker",
3485
- name: profile.broker.value,
3612
+ name: profile.broker.normalizedValue ?? profile.broker.value,
3486
3613
  sourceNodeIds: profile.broker.sourceNodeIds,
3487
3614
  sourceSpanIds: profile.broker.sourceSpanIds
3488
3615
  });
@@ -3535,9 +3662,7 @@ function buildDeterministicOperationalProfile(params) {
3535
3662
  retroactiveDate: firstMatch(nodes, [
3536
3663
  /\bretroactive date\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}|full prior acts|none)/i
3537
3664
  ]),
3538
- premium: firstMatch(nodes, [
3539
- /\b(?:total premium|premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i
3540
- ])
3665
+ premium: premiumFromNodes(nodes)
3541
3666
  };
3542
3667
  const coverages = buildCoverages(nodes);
3543
3668
  const coverageTypes = [...new Set(coverages.map((coverage) => coverage.name))];
@@ -10929,9 +11054,19 @@ function sourceTreeToOutline(sourceTree) {
10929
11054
  });
10930
11055
  return (byParent.get(root?.id) ?? []).map(visit);
10931
11056
  }
11057
+ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
11058
+ "policyNumber",
11059
+ "namedInsured",
11060
+ "insurer",
11061
+ "broker"
11062
+ ]);
10932
11063
  function valueOf(profile, key) {
10933
11064
  const value = profile[key];
10934
- 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);
10935
11070
  }
10936
11071
  function materializeDocument(params) {
10937
11072
  const profile = params.operationalProfile;