@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.mjs CHANGED
@@ -9092,6 +9092,24 @@ function endorsementTitle(value) {
9092
9092
  function sourceNodeText(node) {
9093
9093
  return cleanText([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "), "");
9094
9094
  }
9095
+ function looksLikeEndorsementStart(node) {
9096
+ const title = cleanText(node.title, "");
9097
+ const body = cleanText([node.textExcerpt, node.description].filter(Boolean).join(" "), "");
9098
+ const start = body.slice(0, 260);
9099
+ if (/\bthis endorsement changes the policy\b/i.test(start) && endorsementReference(start)) return true;
9100
+ if (/^(?:[A-Z]{2,}-)?END\s+0*[0-9]{1,4}\b/i.test(start)) return true;
9101
+ if (/^endorsement\s+(?:no\.?|number|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i.test(start)) return true;
9102
+ 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);
9103
+ }
9104
+ function looksLikeEndorsementContinuation(node) {
9105
+ if (looksLikeEndorsementStart(node)) return false;
9106
+ const title = cleanText(node.title, "");
9107
+ const text = sourceNodeText(node);
9108
+ return /\bendorsement\b/i.test(text) || /\bcontinuation\b/i.test(title) || /\ball\s+other\s+terms\s+and\s+conditions\b/i.test(text);
9109
+ }
9110
+ function endorsementStartTitle(node) {
9111
+ return looksLikeEndorsementStart(node) ? endorsementTitle(sourceNodeText(node)) : void 0;
9112
+ }
9095
9113
  function semanticGroupNodeId(documentId, kind, title, childNodeIds) {
9096
9114
  return [
9097
9115
  documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
@@ -9157,8 +9175,7 @@ function groupAdjacentChildren(params) {
9157
9175
  function applySemanticPageGrouping(sourceTree) {
9158
9176
  const relabeled = sourceTree.map((node) => {
9159
9177
  if (node.kind === "document" || node.kind === "page_group") return node;
9160
- const text = sourceNodeText(node);
9161
- const endorsement = endorsementTitle(text);
9178
+ const endorsement = endorsementStartTitle(node);
9162
9179
  if (endorsement && node.kind === "page") {
9163
9180
  return {
9164
9181
  ...node,
@@ -9194,7 +9211,7 @@ function applySemanticPageGrouping(sourceTree) {
9194
9211
  const declarationIds = [];
9195
9212
  for (let index = declarationsStartIndex; index < children.length; index += 1) {
9196
9213
  const child = children[index];
9197
- if (index > declarationsStartIndex && (looksLikePolicyFormStart(child) || endorsementTitle(sourceNodeText(child)))) break;
9214
+ if (index > declarationsStartIndex && (looksLikePolicyFormStart(child) || looksLikeEndorsementStart(child))) break;
9198
9215
  if (!looksLikeDeclarationsContinuation(child)) break;
9199
9216
  declarationIds.push(child.id);
9200
9217
  }
@@ -9213,7 +9230,7 @@ function applySemanticPageGrouping(sourceTree) {
9213
9230
  const policyIds = [];
9214
9231
  for (let index = policyStartIndex; index < children.length; index += 1) {
9215
9232
  const child = children[index];
9216
- if (index > policyStartIndex && endorsementTitle(sourceNodeText(child))) break;
9233
+ if (index > policyStartIndex && looksLikeEndorsementStart(child)) break;
9217
9234
  if (!looksLikePolicyFormContinuation(child)) break;
9218
9235
  policyIds.push(child.id);
9219
9236
  }
@@ -9250,10 +9267,139 @@ function endorsementGroupNodeId(documentId, parentId) {
9250
9267
  parentId?.replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 48) ?? "root"
9251
9268
  ].join(":");
9252
9269
  }
9270
+ function isPolicyFormNode(node) {
9271
+ return node.title === "Policy Form" && (node.kind === "form" || node.kind === "page_group");
9272
+ }
9273
+ function isDeclarationsNode(node) {
9274
+ return node.kind === "page_group" && node.title === "Declarations";
9275
+ }
9276
+ function normalizePolicyFormStructure(sourceTree) {
9277
+ let nextTree = sourceTree;
9278
+ const byParent = nodesByParent(nextTree);
9279
+ const nodesToRemove = /* @__PURE__ */ new Set();
9280
+ for (const form of nextTree.filter((node) => node.kind === "form" && node.title === "Policy Form")) {
9281
+ const children = byParent.get(form.id) ?? [];
9282
+ const declarationsChildren = children.filter(isDeclarationsNode);
9283
+ const nestedPolicyForm = children.find((child) => child.id !== form.id && isPolicyFormNode(child));
9284
+ if (declarationsChildren.length === 0 && !nestedPolicyForm) continue;
9285
+ const declarationIds = new Set(declarationsChildren.map((child) => child.id));
9286
+ nextTree = nextTree.map((node) => {
9287
+ if (declarationIds.has(node.id)) {
9288
+ return {
9289
+ ...node,
9290
+ parentId: form.parentId,
9291
+ metadata: {
9292
+ ...node.metadata,
9293
+ organizerRepair: "promote_declarations_from_policy_form"
9294
+ }
9295
+ };
9296
+ }
9297
+ if (nestedPolicyForm && node.parentId === nestedPolicyForm.id) {
9298
+ return {
9299
+ ...node,
9300
+ parentId: form.id,
9301
+ metadata: {
9302
+ ...node.metadata,
9303
+ organizerRepair: "collapse_nested_policy_form"
9304
+ }
9305
+ };
9306
+ }
9307
+ return node;
9308
+ });
9309
+ if (nestedPolicyForm) nodesToRemove.add(nestedPolicyForm.id);
9310
+ }
9311
+ if (nodesToRemove.size > 0) {
9312
+ nextTree = nextTree.filter((node) => !nodesToRemove.has(node.id));
9313
+ }
9314
+ return nextTree;
9315
+ }
9316
+ function nestEndorsementContinuationPages(sourceTree) {
9317
+ const byParent = nodesByParent(sourceTree);
9318
+ const continuationParentById = /* @__PURE__ */ new Map();
9319
+ for (const group of sourceTree.filter(isEndorsementGroup)) {
9320
+ const children = byParent.get(group.id) ?? [];
9321
+ let currentEndorsement;
9322
+ for (const child of children) {
9323
+ if (child.kind === "endorsement" && endorsementStartTitle(child)) {
9324
+ currentEndorsement = child;
9325
+ continue;
9326
+ }
9327
+ if (!currentEndorsement || child.kind !== "page") continue;
9328
+ continuationParentById.set(child.id, currentEndorsement.id);
9329
+ }
9330
+ }
9331
+ if (continuationParentById.size === 0) return sourceTree;
9332
+ return sourceTree.map((node) => {
9333
+ const parentId = continuationParentById.get(node.id);
9334
+ if (!parentId) return node;
9335
+ return {
9336
+ ...node,
9337
+ parentId,
9338
+ metadata: {
9339
+ ...node.metadata,
9340
+ organizerRepair: "nest_endorsement_continuation"
9341
+ }
9342
+ };
9343
+ });
9344
+ }
9345
+ function nodeDepth(node) {
9346
+ return node.path ? node.path.split("/").filter(Boolean).length : 0;
9347
+ }
9348
+ function shouldUseOwnEvidenceForContainer(node) {
9349
+ return node.kind === "endorsement" || node.kind === "page" || node.kind === "table" || node.kind === "table_row" || node.kind === "table_cell" || node.kind === "text";
9350
+ }
9351
+ function normalizeContainerEvidenceFromChildren(sourceTree) {
9352
+ const byParent = nodesByParent(sourceTree);
9353
+ const byId = new Map(sourceTree.map((node) => [node.id, node]));
9354
+ const sorted = [...sourceTree].sort((left, right) => nodeDepth(right) - nodeDepth(left));
9355
+ for (const originalNode of sorted) {
9356
+ const children = (byParent.get(originalNode.id) ?? []).map((child) => byId.get(child.id)).filter((child) => Boolean(child));
9357
+ if (children.length === 0) continue;
9358
+ const currentNode = byId.get(originalNode.id) ?? originalNode;
9359
+ const evidenceNodes = shouldUseOwnEvidenceForContainer(currentNode) ? [currentNode, ...children] : children;
9360
+ const pageStarts = evidenceNodes.map((node) => node.pageStart).filter((page) => typeof page === "number");
9361
+ const pageEnds = evidenceNodes.map((node) => node.pageEnd ?? node.pageStart).filter((page) => typeof page === "number");
9362
+ const sourceSpanIds = [...new Set(evidenceNodes.flatMap((node) => node.sourceSpanIds))];
9363
+ const bbox = evidenceNodes.flatMap((node) => node.bbox ?? []).slice(0, 12);
9364
+ const childText = children.map((child) => child.textExcerpt ?? child.description).filter(Boolean).join("\n\n").slice(0, 1600);
9365
+ byId.set(currentNode.id, {
9366
+ ...currentNode,
9367
+ sourceSpanIds,
9368
+ pageStart: pageStarts.length ? Math.min(...pageStarts) : currentNode.pageStart,
9369
+ pageEnd: pageEnds.length ? Math.max(...pageEnds) : currentNode.pageEnd,
9370
+ bbox,
9371
+ order: Math.min(currentNode.order, ...children.map((child) => child.order)),
9372
+ textExcerpt: shouldUseOwnEvidenceForContainer(currentNode) ? currentNode.textExcerpt : childText || currentNode.textExcerpt
9373
+ });
9374
+ }
9375
+ return sourceTree.map((node) => byId.get(node.id) ?? node);
9376
+ }
9377
+ function normalizeSemanticHierarchy(sourceTree) {
9378
+ return normalizeDocumentSourceTreePaths(
9379
+ normalizeContainerEvidenceFromChildren(
9380
+ nestEndorsementContinuationPages(
9381
+ normalizePolicyFormStructure(
9382
+ normalizeDocumentSourceTreePaths(sourceTree)
9383
+ )
9384
+ )
9385
+ )
9386
+ );
9387
+ }
9253
9388
  function applyEndorsementGrouping(sourceTree) {
9254
9389
  const relabeledTree = sourceTree.map((node) => {
9255
- if (node.kind === "document" || isEndorsementGroup(node) || node.kind === "endorsement") return node;
9256
- const title = endorsementTitle([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "));
9390
+ if (node.kind === "document" || isEndorsementGroup(node)) return node;
9391
+ const title = endorsementStartTitle(node);
9392
+ if (!title && node.kind === "endorsement") {
9393
+ return {
9394
+ ...node,
9395
+ kind: "page",
9396
+ title: node.pageStart ? `Page ${node.pageStart}` : cleanText(node.title, "Page"),
9397
+ metadata: {
9398
+ ...node.metadata,
9399
+ organizerRepair: "demote_incidental_endorsement_reference"
9400
+ }
9401
+ };
9402
+ }
9257
9403
  if (!title) return node;
9258
9404
  return {
9259
9405
  ...node,
@@ -9293,7 +9439,7 @@ function applyEndorsementGrouping(sourceTree) {
9293
9439
  });
9294
9440
  nextTree = nextTree.map((node) => {
9295
9441
  if (!endorsementGroupIds.has(node.parentId ?? "")) return node;
9296
- const title = endorsementTitle([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "));
9442
+ const title = endorsementStartTitle(node);
9297
9443
  if (!title) return node;
9298
9444
  return {
9299
9445
  ...node,
@@ -9313,9 +9459,21 @@ function applyEndorsementGrouping(sourceTree) {
9313
9459
  if (endorsementGroupIds.has(parentId ?? "")) continue;
9314
9460
  const endorsementChildren = children.filter((child) => child.kind === "endorsement" && !isEndorsementGroup(child));
9315
9461
  if (endorsementChildren.length < 2) continue;
9462
+ const endorsementGroupChildren = [];
9463
+ let hasSeenEndorsementStart = false;
9464
+ for (const child of children) {
9465
+ if (child.kind === "endorsement" && !isEndorsementGroup(child)) {
9466
+ hasSeenEndorsementStart = true;
9467
+ endorsementGroupChildren.push(child);
9468
+ continue;
9469
+ }
9470
+ if (hasSeenEndorsementStart && child.kind === "page" && looksLikeEndorsementContinuation(child)) {
9471
+ endorsementGroupChildren.push(child);
9472
+ }
9473
+ }
9316
9474
  const documentId = endorsementChildren[0].documentId;
9317
- const pageStarts = endorsementChildren.map((child) => child.pageStart).filter((page) => typeof page === "number");
9318
- const pageEnds = endorsementChildren.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
9475
+ const pageStarts = endorsementGroupChildren.map((child) => child.pageStart).filter((page) => typeof page === "number");
9476
+ const pageEnds = endorsementGroupChildren.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
9319
9477
  const order = Math.min(...endorsementChildren.map((child) => child.order));
9320
9478
  const existingGroup = groupsByParent.get(parentId);
9321
9479
  const groupId = existingGroup?.id ?? endorsementGroupNodeId(documentId, parentId);
@@ -9330,12 +9488,12 @@ function applyEndorsementGrouping(sourceTree) {
9330
9488
  sourceSpanIds: [],
9331
9489
  pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
9332
9490
  pageEnd: pageEnds.length ? Math.max(...pageEnds) : void 0,
9333
- bbox: endorsementChildren.flatMap((child) => child.bbox ?? []).slice(0, 12),
9491
+ bbox: endorsementGroupChildren.flatMap((child) => child.bbox ?? []).slice(0, 12),
9334
9492
  order,
9335
9493
  path: "",
9336
9494
  metadata: { sourceTreeVersion: "v3", organizer: "endorsement_grouping" }
9337
9495
  };
9338
- const childSpanIds = [...new Set(endorsementChildren.flatMap((child) => child.sourceSpanIds))];
9496
+ const childSpanIds = [...new Set(endorsementGroupChildren.flatMap((child) => child.sourceSpanIds))];
9339
9497
  const normalizedGroup = {
9340
9498
  ...groupNode,
9341
9499
  sourceSpanIds: groupNode.sourceSpanIds.length ? groupNode.sourceSpanIds : childSpanIds,
@@ -9346,11 +9504,12 @@ function applyEndorsementGrouping(sourceTree) {
9346
9504
  groupsByParent.set(parentId, normalizedGroup);
9347
9505
  if (!existingGroup) nextTree.push(normalizedGroup);
9348
9506
  else nextTree = nextTree.map((node) => node.id === normalizedGroup.id ? normalizedGroup : node);
9507
+ const endorsementGroupChildIds = new Set(endorsementGroupChildren.map((child) => child.id));
9349
9508
  nextTree = nextTree.map(
9350
- (node) => endorsementChildren.some((child) => child.id === node.id) ? { ...node, parentId: groupId, order: node.order + 1e-3 } : node
9509
+ (node) => endorsementGroupChildIds.has(node.id) ? { ...node, parentId: groupId, order: node.order + 1e-3 } : node
9351
9510
  );
9352
9511
  }
9353
- return normalizeDocumentSourceTreePaths(nextTree);
9512
+ return normalizeSemanticHierarchy(nextTree);
9354
9513
  }
9355
9514
  function compactNode(node, maxText = 700) {
9356
9515
  return {