@claritylabs/cl-sdk 3.0.7 → 3.0.9

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
@@ -9439,6 +9439,24 @@ function endorsementTitle(value) {
9439
9439
  function sourceNodeText(node) {
9440
9440
  return cleanText([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "), "");
9441
9441
  }
9442
+ function looksLikeEndorsementStart(node) {
9443
+ const title = cleanText(node.title, "");
9444
+ const body = cleanText([node.textExcerpt, node.description].filter(Boolean).join(" "), "");
9445
+ const start = body.slice(0, 260);
9446
+ if (/\bthis endorsement changes the policy\b/i.test(start) && endorsementReference(start)) return true;
9447
+ if (/^(?:[A-Z]{2,}-)?END\s+0*[0-9]{1,4}\b/i.test(start)) return true;
9448
+ if (/^endorsement\s+(?:no\.?|number|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i.test(start)) return true;
9449
+ return /^endorsement\s+(?:no\.?|number|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i.test(title) && /\bthis endorsement changes the policy\b/i.test(body);
9450
+ }
9451
+ function looksLikeEndorsementContinuation(node) {
9452
+ if (looksLikeEndorsementStart(node)) return false;
9453
+ const title = cleanText(node.title, "");
9454
+ const text = sourceNodeText(node);
9455
+ return /\bendorsement\b/i.test(text) || /\bcontinuation\b/i.test(title) || /\ball\s+other\s+terms\s+and\s+conditions\b/i.test(text);
9456
+ }
9457
+ function endorsementStartTitle(node) {
9458
+ return looksLikeEndorsementStart(node) ? endorsementTitle(sourceNodeText(node)) : void 0;
9459
+ }
9442
9460
  function semanticGroupNodeId(documentId, kind, title, childNodeIds) {
9443
9461
  return [
9444
9462
  documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
@@ -9504,8 +9522,7 @@ function groupAdjacentChildren(params) {
9504
9522
  function applySemanticPageGrouping(sourceTree) {
9505
9523
  const relabeled = sourceTree.map((node) => {
9506
9524
  if (node.kind === "document" || node.kind === "page_group") return node;
9507
- const text = sourceNodeText(node);
9508
- const endorsement = endorsementTitle(text);
9525
+ const endorsement = endorsementStartTitle(node);
9509
9526
  if (endorsement && node.kind === "page") {
9510
9527
  return {
9511
9528
  ...node,
@@ -9541,7 +9558,7 @@ function applySemanticPageGrouping(sourceTree) {
9541
9558
  const declarationIds = [];
9542
9559
  for (let index = declarationsStartIndex; index < children.length; index += 1) {
9543
9560
  const child = children[index];
9544
- if (index > declarationsStartIndex && (looksLikePolicyFormStart(child) || endorsementTitle(sourceNodeText(child)))) break;
9561
+ if (index > declarationsStartIndex && (looksLikePolicyFormStart(child) || looksLikeEndorsementStart(child))) break;
9545
9562
  if (!looksLikeDeclarationsContinuation(child)) break;
9546
9563
  declarationIds.push(child.id);
9547
9564
  }
@@ -9560,7 +9577,7 @@ function applySemanticPageGrouping(sourceTree) {
9560
9577
  const policyIds = [];
9561
9578
  for (let index = policyStartIndex; index < children.length; index += 1) {
9562
9579
  const child = children[index];
9563
- if (index > policyStartIndex && endorsementTitle(sourceNodeText(child))) break;
9580
+ if (index > policyStartIndex && looksLikeEndorsementStart(child)) break;
9564
9581
  if (!looksLikePolicyFormContinuation(child)) break;
9565
9582
  policyIds.push(child.id);
9566
9583
  }
@@ -9597,10 +9614,139 @@ function endorsementGroupNodeId(documentId, parentId) {
9597
9614
  parentId?.replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 48) ?? "root"
9598
9615
  ].join(":");
9599
9616
  }
9617
+ function isPolicyFormNode(node) {
9618
+ return node.title === "Policy Form" && (node.kind === "form" || node.kind === "page_group");
9619
+ }
9620
+ function isDeclarationsNode(node) {
9621
+ return node.kind === "page_group" && node.title === "Declarations";
9622
+ }
9623
+ function normalizePolicyFormStructure(sourceTree) {
9624
+ let nextTree = sourceTree;
9625
+ const byParent = nodesByParent(nextTree);
9626
+ const nodesToRemove = /* @__PURE__ */ new Set();
9627
+ for (const form of nextTree.filter((node) => node.kind === "form" && node.title === "Policy Form")) {
9628
+ const children = byParent.get(form.id) ?? [];
9629
+ const declarationsChildren = children.filter(isDeclarationsNode);
9630
+ const nestedPolicyForm = children.find((child) => child.id !== form.id && isPolicyFormNode(child));
9631
+ if (declarationsChildren.length === 0 && !nestedPolicyForm) continue;
9632
+ const declarationIds = new Set(declarationsChildren.map((child) => child.id));
9633
+ nextTree = nextTree.map((node) => {
9634
+ if (declarationIds.has(node.id)) {
9635
+ return {
9636
+ ...node,
9637
+ parentId: form.parentId,
9638
+ metadata: {
9639
+ ...node.metadata,
9640
+ organizerRepair: "promote_declarations_from_policy_form"
9641
+ }
9642
+ };
9643
+ }
9644
+ if (nestedPolicyForm && node.parentId === nestedPolicyForm.id) {
9645
+ return {
9646
+ ...node,
9647
+ parentId: form.id,
9648
+ metadata: {
9649
+ ...node.metadata,
9650
+ organizerRepair: "collapse_nested_policy_form"
9651
+ }
9652
+ };
9653
+ }
9654
+ return node;
9655
+ });
9656
+ if (nestedPolicyForm) nodesToRemove.add(nestedPolicyForm.id);
9657
+ }
9658
+ if (nodesToRemove.size > 0) {
9659
+ nextTree = nextTree.filter((node) => !nodesToRemove.has(node.id));
9660
+ }
9661
+ return nextTree;
9662
+ }
9663
+ function nestEndorsementContinuationPages(sourceTree) {
9664
+ const byParent = nodesByParent(sourceTree);
9665
+ const continuationParentById = /* @__PURE__ */ new Map();
9666
+ for (const group of sourceTree.filter(isEndorsementGroup)) {
9667
+ const children = byParent.get(group.id) ?? [];
9668
+ let currentEndorsement;
9669
+ for (const child of children) {
9670
+ if (child.kind === "endorsement" && endorsementStartTitle(child)) {
9671
+ currentEndorsement = child;
9672
+ continue;
9673
+ }
9674
+ if (!currentEndorsement || child.kind !== "page") continue;
9675
+ continuationParentById.set(child.id, currentEndorsement.id);
9676
+ }
9677
+ }
9678
+ if (continuationParentById.size === 0) return sourceTree;
9679
+ return sourceTree.map((node) => {
9680
+ const parentId = continuationParentById.get(node.id);
9681
+ if (!parentId) return node;
9682
+ return {
9683
+ ...node,
9684
+ parentId,
9685
+ metadata: {
9686
+ ...node.metadata,
9687
+ organizerRepair: "nest_endorsement_continuation"
9688
+ }
9689
+ };
9690
+ });
9691
+ }
9692
+ function nodeDepth(node) {
9693
+ return node.path ? node.path.split("/").filter(Boolean).length : 0;
9694
+ }
9695
+ function shouldUseOwnEvidenceForContainer(node) {
9696
+ return node.kind === "endorsement" || node.kind === "page" || node.kind === "table" || node.kind === "table_row" || node.kind === "table_cell" || node.kind === "text";
9697
+ }
9698
+ function normalizeContainerEvidenceFromChildren(sourceTree) {
9699
+ const byParent = nodesByParent(sourceTree);
9700
+ const byId = new Map(sourceTree.map((node) => [node.id, node]));
9701
+ const sorted = [...sourceTree].sort((left, right) => nodeDepth(right) - nodeDepth(left));
9702
+ for (const originalNode of sorted) {
9703
+ const children = (byParent.get(originalNode.id) ?? []).map((child) => byId.get(child.id)).filter((child) => Boolean(child));
9704
+ if (children.length === 0) continue;
9705
+ const currentNode = byId.get(originalNode.id) ?? originalNode;
9706
+ const evidenceNodes = shouldUseOwnEvidenceForContainer(currentNode) ? [currentNode, ...children] : children;
9707
+ const pageStarts = evidenceNodes.map((node) => node.pageStart).filter((page) => typeof page === "number");
9708
+ const pageEnds = evidenceNodes.map((node) => node.pageEnd ?? node.pageStart).filter((page) => typeof page === "number");
9709
+ const sourceSpanIds = [...new Set(evidenceNodes.flatMap((node) => node.sourceSpanIds))];
9710
+ const bbox = evidenceNodes.flatMap((node) => node.bbox ?? []).slice(0, 12);
9711
+ const childText = children.map((child) => child.textExcerpt ?? child.description).filter(Boolean).join("\n\n").slice(0, 1600);
9712
+ byId.set(currentNode.id, {
9713
+ ...currentNode,
9714
+ sourceSpanIds,
9715
+ pageStart: pageStarts.length ? Math.min(...pageStarts) : currentNode.pageStart,
9716
+ pageEnd: pageEnds.length ? Math.max(...pageEnds) : currentNode.pageEnd,
9717
+ bbox,
9718
+ order: Math.min(currentNode.order, ...children.map((child) => child.order)),
9719
+ textExcerpt: shouldUseOwnEvidenceForContainer(currentNode) ? currentNode.textExcerpt : childText || currentNode.textExcerpt
9720
+ });
9721
+ }
9722
+ return sourceTree.map((node) => byId.get(node.id) ?? node);
9723
+ }
9724
+ function normalizeSemanticHierarchy(sourceTree) {
9725
+ return normalizeDocumentSourceTreePaths(
9726
+ normalizeContainerEvidenceFromChildren(
9727
+ nestEndorsementContinuationPages(
9728
+ normalizePolicyFormStructure(
9729
+ normalizeDocumentSourceTreePaths(sourceTree)
9730
+ )
9731
+ )
9732
+ )
9733
+ );
9734
+ }
9600
9735
  function applyEndorsementGrouping(sourceTree) {
9601
9736
  const relabeledTree = sourceTree.map((node) => {
9602
- if (node.kind === "document" || isEndorsementGroup(node) || node.kind === "endorsement") return node;
9603
- const title = endorsementTitle([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "));
9737
+ if (node.kind === "document" || isEndorsementGroup(node)) return node;
9738
+ const title = endorsementStartTitle(node);
9739
+ if (!title && node.kind === "endorsement") {
9740
+ return {
9741
+ ...node,
9742
+ kind: "page",
9743
+ title: node.pageStart ? `Page ${node.pageStart}` : cleanText(node.title, "Page"),
9744
+ metadata: {
9745
+ ...node.metadata,
9746
+ organizerRepair: "demote_incidental_endorsement_reference"
9747
+ }
9748
+ };
9749
+ }
9604
9750
  if (!title) return node;
9605
9751
  return {
9606
9752
  ...node,
@@ -9640,7 +9786,7 @@ function applyEndorsementGrouping(sourceTree) {
9640
9786
  });
9641
9787
  nextTree = nextTree.map((node) => {
9642
9788
  if (!endorsementGroupIds.has(node.parentId ?? "")) return node;
9643
- const title = endorsementTitle([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "));
9789
+ const title = endorsementStartTitle(node);
9644
9790
  if (!title) return node;
9645
9791
  return {
9646
9792
  ...node,
@@ -9660,9 +9806,21 @@ function applyEndorsementGrouping(sourceTree) {
9660
9806
  if (endorsementGroupIds.has(parentId ?? "")) continue;
9661
9807
  const endorsementChildren = children.filter((child) => child.kind === "endorsement" && !isEndorsementGroup(child));
9662
9808
  if (endorsementChildren.length < 2) continue;
9809
+ const endorsementGroupChildren = [];
9810
+ let hasSeenEndorsementStart = false;
9811
+ for (const child of children) {
9812
+ if (child.kind === "endorsement" && !isEndorsementGroup(child)) {
9813
+ hasSeenEndorsementStart = true;
9814
+ endorsementGroupChildren.push(child);
9815
+ continue;
9816
+ }
9817
+ if (hasSeenEndorsementStart && child.kind === "page" && looksLikeEndorsementContinuation(child)) {
9818
+ endorsementGroupChildren.push(child);
9819
+ }
9820
+ }
9663
9821
  const documentId = endorsementChildren[0].documentId;
9664
- const pageStarts = endorsementChildren.map((child) => child.pageStart).filter((page) => typeof page === "number");
9665
- const pageEnds = endorsementChildren.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
9822
+ const pageStarts = endorsementGroupChildren.map((child) => child.pageStart).filter((page) => typeof page === "number");
9823
+ const pageEnds = endorsementGroupChildren.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
9666
9824
  const order = Math.min(...endorsementChildren.map((child) => child.order));
9667
9825
  const existingGroup = groupsByParent.get(parentId);
9668
9826
  const groupId = existingGroup?.id ?? endorsementGroupNodeId(documentId, parentId);
@@ -9677,12 +9835,12 @@ function applyEndorsementGrouping(sourceTree) {
9677
9835
  sourceSpanIds: [],
9678
9836
  pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
9679
9837
  pageEnd: pageEnds.length ? Math.max(...pageEnds) : void 0,
9680
- bbox: endorsementChildren.flatMap((child) => child.bbox ?? []).slice(0, 12),
9838
+ bbox: endorsementGroupChildren.flatMap((child) => child.bbox ?? []).slice(0, 12),
9681
9839
  order,
9682
9840
  path: "",
9683
9841
  metadata: { sourceTreeVersion: "v3", organizer: "endorsement_grouping" }
9684
9842
  };
9685
- const childSpanIds = [...new Set(endorsementChildren.flatMap((child) => child.sourceSpanIds))];
9843
+ const childSpanIds = [...new Set(endorsementGroupChildren.flatMap((child) => child.sourceSpanIds))];
9686
9844
  const normalizedGroup = {
9687
9845
  ...groupNode,
9688
9846
  sourceSpanIds: groupNode.sourceSpanIds.length ? groupNode.sourceSpanIds : childSpanIds,
@@ -9693,11 +9851,12 @@ function applyEndorsementGrouping(sourceTree) {
9693
9851
  groupsByParent.set(parentId, normalizedGroup);
9694
9852
  if (!existingGroup) nextTree.push(normalizedGroup);
9695
9853
  else nextTree = nextTree.map((node) => node.id === normalizedGroup.id ? normalizedGroup : node);
9854
+ const endorsementGroupChildIds = new Set(endorsementGroupChildren.map((child) => child.id));
9696
9855
  nextTree = nextTree.map(
9697
- (node) => endorsementChildren.some((child) => child.id === node.id) ? { ...node, parentId: groupId, order: node.order + 1e-3 } : node
9856
+ (node) => endorsementGroupChildIds.has(node.id) ? { ...node, parentId: groupId, order: node.order + 1e-3 } : node
9698
9857
  );
9699
9858
  }
9700
- return normalizeDocumentSourceTreePaths(nextTree);
9859
+ return normalizeSemanticHierarchy(nextTree);
9701
9860
  }
9702
9861
  function compactNode(node, maxText = 700) {
9703
9862
  return {