@claritylabs/cl-sdk 3.0.14 → 3.0.16

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
@@ -2700,7 +2700,7 @@ function isDiscardableBoilerplate(text, unit) {
2700
2700
  }
2701
2701
  function isBoilerplateLine(line) {
2702
2702
  const cleaned = normalizeWhitespace(line.replace(/\bColumn\s+\d+:\s*/gi, ""));
2703
- return isDiscardableBoilerplate(cleaned) || /^IMPORTANT NOTICE\s*[-—]\s*/i.test(cleaned) || /^THIS IS A CLAIMS-MADE AND REPORTED POLICY\.? PLEASE READ IT CAREFULLY\.?$/i.test(cleaned);
2703
+ return isDiscardableBoilerplate(cleaned) || /^THIS IS A CLAIMS-MADE AND REPORTED POLICY\.? PLEASE READ IT CAREFULLY\.?$/i.test(cleaned);
2704
2704
  }
2705
2705
  function removedBoilerplateLines(text) {
2706
2706
  return text.split(/\s{2,}|\r?\n/).map(normalizeWhitespace).filter((line) => line && isBoilerplateLine(line));
@@ -2999,7 +2999,19 @@ function metadataString(metadata, key) {
2999
2999
  }
3000
3000
  function isTitleContentNode(node) {
3001
3001
  if (node.kind !== "text") return false;
3002
- return metadataString(node.metadata, "elementType") === "title" || metadataString(node.metadata, "sourceUnit") === "title";
3002
+ const isMarkedTitle = metadataString(node.metadata, "elementType") === "title" || metadataString(node.metadata, "sourceUnit") === "title";
3003
+ if (!isMarkedTitle) return false;
3004
+ const text = normalizeWhitespace2(node.textExcerpt ?? node.title);
3005
+ if (!text || text.length > 140) return false;
3006
+ const words = text.split(/\s+/);
3007
+ if (words.length > 14) return false;
3008
+ const startsWithStructuredHeading = /^(section|item|part|endorsement|schedule|article)\b|^[A-Z]\.\s|\b[IVX]+\.\s/i.test(text);
3009
+ const uppercaseLetters = [...text].filter((char) => /[A-Z]/.test(char)).length;
3010
+ const lowercaseLetters = [...text].filter((char) => /[a-z]/.test(char)).length;
3011
+ const mostlyUppercase = uppercaseLetters > 0 && uppercaseLetters >= lowercaseLetters * 1.6;
3012
+ const containsSentencePunctuation = /[.;:]\s+\S/.test(text) || /[.;:]$/.test(text);
3013
+ const sentenceLike = /\b(is|are|was|were|will|shall|may|must|means|includes|provided|subject|available|attached|remain|constitutes)\b/i.test(text) && /[a-z]/.test(text);
3014
+ return startsWithStructuredHeading || mostlyUppercase && !sentenceLike && !containsSentencePunctuation;
3003
3015
  }
3004
3016
  function nodePageEnd(node) {
3005
3017
  return node.pageEnd ?? node.pageStart;
@@ -3039,10 +3051,10 @@ function groupPageContentByTitles(nodes) {
3039
3051
  const bbox = evidenceNodes.flatMap((node) => node.bbox ?? []).slice(0, 12);
3040
3052
  byId.set(activeTitle.id, {
3041
3053
  ...activeTitle,
3042
- kind: "section",
3054
+ kind: "text",
3043
3055
  title,
3044
3056
  description: nodeTextDescription({
3045
- kind: "section",
3057
+ kind: "text",
3046
3058
  title,
3047
3059
  text: evidenceNodes.map((node) => node.textExcerpt).filter(Boolean).join("\n\n"),
3048
3060
  page: activeTitle.pageStart
@@ -9563,12 +9575,28 @@ function nodePageEnd2(node) {
9563
9575
  return node.pageEnd ?? node.pageStart;
9564
9576
  }
9565
9577
  function pageRangeForNodes(nodes) {
9566
- const pageStarts = nodes.map((node) => node.pageStart).filter((page) => typeof page === "number");
9567
- const pageEnds = nodes.map(nodePageEnd2).filter((page) => typeof page === "number");
9568
- if (pageStarts.length === 0 || pageEnds.length === 0) return void 0;
9569
- const start = Math.min(...pageStarts);
9570
- const end = Math.max(...pageEnds);
9571
- return start === end ? `page ${start}` : `pages ${start}-${end}`;
9578
+ const pages = [...new Set(nodes.flatMap((node) => {
9579
+ if (typeof node.pageStart !== "number") return [];
9580
+ const end = nodePageEnd2(node) ?? node.pageStart;
9581
+ const values = [];
9582
+ for (let page = node.pageStart; page <= end; page += 1) values.push(page);
9583
+ return values;
9584
+ }))].sort((left, right) => left - right);
9585
+ if (pages.length === 0) return void 0;
9586
+ const ranges = [];
9587
+ let start = pages[0];
9588
+ let previous = pages[0];
9589
+ for (const page of pages.slice(1)) {
9590
+ if (page === previous + 1) {
9591
+ previous = page;
9592
+ continue;
9593
+ }
9594
+ ranges.push(start === previous ? String(start) : `${start}-${previous}`);
9595
+ start = page;
9596
+ previous = page;
9597
+ }
9598
+ ranges.push(start === previous ? String(start) : `${start}-${previous}`);
9599
+ return ranges.length === 1 && !ranges[0].includes("-") ? `page ${ranges[0]}` : `pages ${ranges.join(", ")}`;
9572
9600
  }
9573
9601
  function descriptionWithPages(description, nodes) {
9574
9602
  const range = pageRangeForNodes(nodes);
@@ -9599,6 +9627,7 @@ function looksLikeDeclarationsContinuation(node) {
9599
9627
  function looksLikePolicyFormStart(node) {
9600
9628
  const text = sourceNodeText(node);
9601
9629
  const excerpt = cleanText(node.textExcerpt, "");
9630
+ if (isAdministrativeNoticeNode(node) || looksLikeDeclarationsStart(node)) return false;
9602
9631
  return /\bpolicy form\b/i.test(node.title) || /^policy\s+form\b/i.test(excerpt) || /\btechnology errors?\s*&?\s*omissions\b/i.test(text) && /\bplease read this entire policy carefully\b/i.test(text) || /\bform\s+[A-Z]{2,}-[A-Z0-9-]+\s+\d{2}\s+\d{2}\b/i.test(text);
9603
9632
  }
9604
9633
  function looksLikePolicyFormContinuation(node) {
@@ -9607,9 +9636,9 @@ function looksLikePolicyFormContinuation(node) {
9607
9636
  return /\b(insuring agreement|definitions?|exclusions?|conditions?|claim means|insured means|wrongful act means|limits of liability|notice of claim|cancellation by|action against the company)\b/i.test(text);
9608
9637
  }
9609
9638
  function groupAdjacentChildren(params) {
9610
- if (params.childIds.length < 2) return params.sourceTree;
9639
+ if (params.childIds.length < 1) return params.sourceTree;
9611
9640
  const children = params.childIds.map((id2) => params.children.find((child) => child.id === id2)).filter((child) => Boolean(child));
9612
- if (children.length < 2) return params.sourceTree;
9641
+ if (children.length < 1) return params.sourceTree;
9613
9642
  const parentId = children[0].parentId;
9614
9643
  if (!children.every((child) => child.parentId === parentId)) return params.sourceTree;
9615
9644
  const documentId = children[0].documentId;
@@ -9718,6 +9747,11 @@ function applySemanticPageGrouping(sourceTree) {
9718
9747
  for (let index = policyStartIndex; index < children.length; index += 1) {
9719
9748
  const child = children[index];
9720
9749
  if (index > policyStartIndex && looksLikeEndorsementStart(child)) break;
9750
+ if (isAdministrativeNoticeNode(child) || looksLikeDeclarationsStart(child)) break;
9751
+ if (index > policyStartIndex && child.kind === "page") {
9752
+ policyIds.push(child.id);
9753
+ continue;
9754
+ }
9721
9755
  if (!looksLikePolicyFormContinuation(child)) break;
9722
9756
  policyIds.push(child.id);
9723
9757
  }
@@ -9909,6 +9943,10 @@ function normalizeContainerEvidenceFromChildren(sourceTree) {
9909
9943
  pageEnd: pageEnds.length ? Math.max(...pageEnds) : currentNode.pageEnd,
9910
9944
  bbox,
9911
9945
  order: Math.min(currentNode.order, ...children.map((child) => child.order)),
9946
+ description: descriptionWithPages(
9947
+ currentNode.description.replace(/;\s*pages?\s+[0-9,\s-]+$/i, ""),
9948
+ evidenceNodes
9949
+ ),
9912
9950
  textExcerpt: shouldUseOwnEvidenceForContainer(currentNode) ? currentNode.textExcerpt : childText || currentNode.textExcerpt
9913
9951
  });
9914
9952
  }