eyecite-ts 0.12.0 → 0.13.1

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.
@@ -1,4 +1,4 @@
1
- import { t as Citation, x as ShortFormCitation } from "./citation-ChDj3ZlY.cjs";
1
+ import { S as ShortFormCitation, t as Citation } from "./citation-BaRqFZnV.cjs";
2
2
 
3
3
  //#region src/footnotes/types.d.ts
4
4
  /**
@@ -106,4 +106,4 @@ type ResolvedCitation<C extends Citation = Citation> = C extends ShortFormCitati
106
106
  };
107
107
  //#endregion
108
108
  export { FootnoteMap as a, ScopeStrategy as i, ResolutionResult as n, FootnoteZone as o, ResolvedCitation as r, ResolutionOptions as t };
109
- //# sourceMappingURL=types-C7IqHYnj.d.cts.map
109
+ //# sourceMappingURL=types-BgmSZol5.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-C7IqHYnj.d.cts","names":[],"sources":["../src/footnotes/types.ts","../src/resolve/types.ts"],"mappings":";;;;;;AAIA;UAAiB,YAAA;;EAEf,KAAA;;EAEA,GAAA;;EAEA,cAAA;AAAA;AAMF;;;AAAA,KAAY,WAAA,GAAc,YAAA;;;;;;;KCFd,aAAA;;;;UAKK,iBAAA;;;;AALjB;;;;EAaE,aAAA,GAAgB,aAAA;EARlB;;;;EAcE,oBAAA;;;;;EAMA,wBAAA,GAA2B,MAAA;;;;;EAM3B,kBAAA;;;;;;EAOA,mBAAA;EAmBe;;;;EAbf,gBAAA;;;;;AA4CF;EArCE,WAAA,GAAc,WAAA;AAAA;;;;UAMC,gBAAA;;;;;EAKf,UAAA;;;;EAKA,aAAA;;;;EAKA,QAAA;;;;;EAMA,UAAA;AAAA;;;;;;;;KAUU,gBAAA,WAA2B,QAAA,GAAW,QAAA,IAAY,CAAA,SAAU,iBAAA,GACpE,CAAA;EAAM,UAAA,EAAY,gBAAA;AAAA,IAClB,CAAA;EAAM,UAAA;AAAA"}
1
+ {"version":3,"file":"types-BgmSZol5.d.cts","names":[],"sources":["../src/footnotes/types.ts","../src/resolve/types.ts"],"mappings":";;;;;;AAIA;UAAiB,YAAA;;EAEf,KAAA;;EAEA,GAAA;;EAEA,cAAA;AAAA;AAMF;;;AAAA,KAAY,WAAA,GAAc,YAAA;;;;;;;KCFd,aAAA;;;;UAKK,iBAAA;;;;AALjB;;;;EAaE,aAAA,GAAgB,aAAA;EARlB;;;;EAcE,oBAAA;;;;;EAMA,wBAAA,GAA2B,MAAA;;;;;EAM3B,kBAAA;;;;;;EAOA,mBAAA;EAmBe;;;;EAbf,gBAAA;;;;;AA4CF;EArCE,WAAA,GAAc,WAAA;AAAA;;;;UAMC,gBAAA;;;;;EAKf,UAAA;;;;EAKA,aAAA;;;;EAKA,QAAA;;;;;EAMA,UAAA;AAAA;;;;;;;;KAUU,gBAAA,WAA2B,QAAA,GAAW,QAAA,IAAY,CAAA,SAAU,iBAAA,GACpE,CAAA;EAAM,UAAA,EAAY,gBAAA;AAAA,IAClB,CAAA;EAAM,UAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- import { t as Citation, x as ShortFormCitation } from "./citation-C-GPQmt3.mjs";
1
+ import { S as ShortFormCitation, t as Citation } from "./citation-CVm_rp15.mjs";
2
2
 
3
3
  //#region src/footnotes/types.d.ts
4
4
  /**
@@ -106,4 +106,4 @@ type ResolvedCitation<C extends Citation = Citation> = C extends ShortFormCitati
106
106
  };
107
107
  //#endregion
108
108
  export { FootnoteMap as a, ScopeStrategy as i, ResolutionResult as n, FootnoteZone as o, ResolvedCitation as r, ResolutionOptions as t };
109
- //# sourceMappingURL=types-lHL_1ON_.d.mts.map
109
+ //# sourceMappingURL=types-DQzZmylJ.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-lHL_1ON_.d.mts","names":[],"sources":["../src/footnotes/types.ts","../src/resolve/types.ts"],"mappings":";;;;;;AAIA;UAAiB,YAAA;;EAEf,KAAA;;EAEA,GAAA;;EAEA,cAAA;AAAA;AAMF;;;AAAA,KAAY,WAAA,GAAc,YAAA;;;;;;;KCFd,aAAA;;;;UAKK,iBAAA;;;;AALjB;;;;EAaE,aAAA,GAAgB,aAAA;EARlB;;;;EAcE,oBAAA;;;;;EAMA,wBAAA,GAA2B,MAAA;;;;;EAM3B,kBAAA;;;;;;EAOA,mBAAA;EAmBe;;;;EAbf,gBAAA;;;;;AA4CF;EArCE,WAAA,GAAc,WAAA;AAAA;;;;UAMC,gBAAA;;;;;EAKf,UAAA;;;;EAKA,aAAA;;;;EAKA,QAAA;;;;;EAMA,UAAA;AAAA;;;;;;;;KAUU,gBAAA,WAA2B,QAAA,GAAW,QAAA,IAAY,CAAA,SAAU,iBAAA,GACpE,CAAA;EAAM,UAAA,EAAY,gBAAA;AAAA,IAClB,CAAA;EAAM,UAAA;AAAA"}
1
+ {"version":3,"file":"types-DQzZmylJ.d.mts","names":[],"sources":["../src/footnotes/types.ts","../src/resolve/types.ts"],"mappings":";;;;;;AAIA;UAAiB,YAAA;;EAEf,KAAA;;EAEA,GAAA;;EAEA,cAAA;AAAA;AAMF;;;AAAA,KAAY,WAAA,GAAc,YAAA;;;;;;;KCFd,aAAA;;;;UAKK,iBAAA;;;;AALjB;;;;EAaE,aAAA,GAAgB,aAAA;EARlB;;;;EAcE,oBAAA;;;;;EAMA,wBAAA,GAA2B,MAAA;;;;;EAM3B,kBAAA;;;;;;EAOA,mBAAA;EAmBe;;;;EAbf,gBAAA;;;;;AA4CF;EArCE,WAAA,GAAc,WAAA;AAAA;;;;UAMC,gBAAA;;;;;EAKf,UAAA;;;;EAKA,aAAA;;;;EAKA,QAAA;;;;;EAMA,UAAA;AAAA;;;;;;;;KAUU,gBAAA,WAA2B,QAAA,GAAW,QAAA,IAAY,CAAA,SAAU,iBAAA,GACpE,CAAA;EAAM,UAAA,EAAY,gBAAA;AAAA,IAClB,CAAA;EAAM,UAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t,n){return n===void 0?`${e} ${t}`:`${e} ${t} ${n}`}function t(t){let n=t.normalizedReporter??t.reporter,r=t.hasBlankPage?void 0:t.page;return e(t.volume,n,r)}function n(n){let r=[t(n)];if(n.parallelCitations?.length)for(let t of n.parallelCitations)r.push(e(t.volume,t.reporter,t.page));return r}function r(e){let t=[[10,`X`],[9,`IX`],[5,`V`],[4,`IV`],[1,`I`]],n=``,r=e;for(let[e,i]of t)for(;r>=e;)n+=i,r-=e;return n}function i(e){switch(e.type){case`case`:{let t=e.normalizedReporter??e.reporter,n;n=e.hasBlankPage?` ___`:e.page===void 0?``:` ${e.page}`;let r=`${e.volume} ${t}${n}`,i=e.pincite===void 0?``:`, ${e.pincite}`,a=e.year===void 0?``:` (${e.year})`;return`${e.caseName?`${e.caseName}, `:``}${r}${i}${a}`}case`statute`:{let t=e.title===void 0?``:`${e.title} `,n=`\u00A7 ${e.section}`,r=e.subsection??``,i=e.hasEtSeq?` et seq.`:``;return`${t}${e.code} ${n}${r}${i}`}case`constitutional`:{let t=`${e.jurisdiction===`US`?`U.S.`:e.jurisdiction??``} Const.`,n=``;return e.article!==void 0&&(n+=` art. ${r(e.article)}`),e.amendment!==void 0&&(n+=` amend. ${r(e.amendment)}`),e.section!==void 0&&(n+=`, \u00A7 ${e.section}`),e.clause!==void 0&&(n+=`, cl. ${e.clause}`),`${t}${n}`}case`journal`:{let t=e.volume===void 0?``:`${e.volume} `,n=e.page===void 0?``:` ${e.page}`,r=e.pincite===void 0?``:`, ${e.pincite}`,i=e.year===void 0?``:` (${e.year})`;return`${t}${e.abbreviation}${n}${r}${i}`}case`neutral`:return`${e.year} ${e.court} ${e.documentNumber}`;case`publicLaw`:return`Pub. L. No. ${e.congress}-${e.lawNumber}`;case`federalRegister`:{let t=e.year===void 0?``:` (${e.year})`;return`${e.volume} Fed. Reg. ${e.page}${t}`}case`statutesAtLarge`:return`${e.volume} Stat. ${e.page}`;case`id`:return e.pincite===void 0?`Id.`:`Id. at ${e.pincite}`;case`supra`:return e.pincite===void 0?`${e.partyName}, supra`:`${e.partyName}, supra, at ${e.pincite}`;case`shortFormCase`:{let t=e.reporter;if(e.pincite!==void 0)return`${e.volume} ${t} at ${e.pincite}`;let n=e.page===void 0?``:` ${e.page}`;return`${e.volume} ${t}${n}`}}}function a(e){return`${e.volume}-${e.reporter}-${e.page??`blank`}`}function o(e){return e.type===`case`}function s(e){return e.resolution}function c(e){let t=new Map,r=new Map,i=[];for(let s=0;s<e.length;s++){let c=e[s];if(!o(c))continue;let l=c.groupId,u=a(c),d=(l?r.get(l):void 0)??r.get(u);if(d!==void 0)i[d].mentions.push(c),t.set(s,d);else{let e=i.length,a={primaryCitation:c,mentions:[c],parallelCitations:n(c)};i.push(a),t.set(s,e),r.set(u,e),l&&r.set(l,e)}}for(let n=0;n<e.length;n++){let r=e[n];if(r.type!==`id`&&r.type!==`supra`&&r.type!==`shortFormCase`)continue;let a=s(r);if(a?.resolvedTo===void 0)continue;let o=t.get(a.resolvedTo);o!==void 0&&(i[o].mentions.push(r),t.set(n,o))}for(let e of i)e.mentions.sort((e,t)=>e.span.originalStart-t.span.originalStart);return i}const l=new Set(`v,vs,U.S,S.Ct,S. Ct,L.Ed,L. Ed,F,F.2d,F.3d,F.4th,F.Supp,F. Supp,A.2d,A.3d,N.E,N.E.2d,N.W,N.W.2d,S.E,S.E.2d,S.W,S.W.2d,S.W.3d,So,So.2d,So.3d,P,P.2d,P.3d,No,Nos,Inc,Corp,Ltd,Co,Ass'n,Dept,Dist,Cir,App,Supp,Rev,Stat,Const,Mr,Mrs,Ms,Dr,Jr,Sr,St,Ct,Atl,Cal,Fla,Ill,Tex,Pa,Md,Va,Wis,Minn,Mich,Mass,Conn,Colo,Ariz,Ark,Ga,La,Ind,Kan,Ky,Miss,Mo,Neb,Nev,Okla,Or,Tenn,Vt,Wash,Wyo,Del,Haw,Ida,Me,Mont,R.I,S.C,S.D,N.C,N.D,N.J,N.M,N.Y,W.Va,U.S.C,C.F.R,Fed,Reg,Pub,Amend,Sec,Art,Cl,Ch,Pt,Vol,Ed,Harv,Yale,Stan,Colum,Geo`.split(`,`));function u(e,t){let n=t;for(;n>0&&e[n-1]!==` `&&e[n-1]!==`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t,n){return n===void 0?`${e} ${t}`:`${e} ${t} ${n}`}function t(t){let n=t.normalizedReporter??t.reporter,r=t.hasBlankPage?void 0:t.page;return e(t.volume,n,r)}function n(n){let r=[t(n)];if(n.parallelCitations?.length)for(let t of n.parallelCitations)r.push(e(t.volume,t.reporter,t.page));return r}function r(e){let t=[[10,`X`],[9,`IX`],[5,`V`],[4,`IV`],[1,`I`]],n=``,r=e;for(let[e,i]of t)for(;r>=e;)n+=i,r-=e;return n}function i(e){switch(e.type){case`case`:{let t=e.normalizedReporter??e.reporter,n;n=e.hasBlankPage?` ___`:e.page===void 0?``:` ${e.page}`;let r=`${e.volume} ${t}${n}`,i=e.pincite===void 0?``:`, ${e.pincite}`,a=e.year===void 0?``:` (${e.year})`;return`${e.caseName?`${e.caseName}, `:``}${r}${i}${a}`}case`statute`:{let t=e.title===void 0?``:`${e.title} `,n=`\u00A7 ${e.section}`,r=e.subsection??``,i=e.hasEtSeq?` et seq.`:``;return`${t}${e.code} ${n}${r}${i}`}case`constitutional`:{let t=`${e.jurisdiction===`US`?`U.S.`:e.jurisdiction??``} Const.`,n=``;return e.article!==void 0&&(n+=` art. ${r(e.article)}`),e.amendment!==void 0&&(n+=` amend. ${r(e.amendment)}`),e.section!==void 0&&(n+=`, \u00A7 ${e.section}`),e.clause!==void 0&&(n+=`, cl. ${e.clause}`),`${t}${n}`}case`journal`:{let t=e.volume===void 0?``:`${e.volume} `,n=e.page===void 0?``:` ${e.page}`,r=e.pincite===void 0?``:`, ${e.pincite}`,i=e.year===void 0?``:` (${e.year})`;return`${t}${e.abbreviation}${n}${r}${i}`}case`docket`:{let t=e.caseName?`${e.caseName}, `:``,n=e.court&&e.year?` (${e.court} ${e.year})`:e.court?` (${e.court})`:e.year?` (${e.year})`:``;return`${t}No. ${e.docketNumber}${n}`}case`neutral`:return`${e.year} ${e.court} ${e.documentNumber}`;case`publicLaw`:return`Pub. L. No. ${e.congress}-${e.lawNumber}`;case`federalRegister`:{let t=e.year===void 0?``:` (${e.year})`;return`${e.volume} Fed. Reg. ${e.page}${t}`}case`statutesAtLarge`:return`${e.volume} Stat. ${e.page}`;case`id`:return e.pincite===void 0?`Id.`:`Id. at ${e.pincite}`;case`supra`:return e.pincite===void 0?`${e.partyName}, supra`:`${e.partyName}, supra, at ${e.pincite}`;case`shortFormCase`:{let t=e.reporter;if(e.pincite!==void 0)return`${e.volume} ${t} at ${e.pincite}`;let n=e.page===void 0?``:` ${e.page}`;return`${e.volume} ${t}${n}`}}}function a(e){return`${e.volume}-${e.reporter}-${e.page??`blank`}`}function o(e){return e.type===`case`}function s(e){return e.resolution}function c(e){let t=new Map,r=new Map,i=[];for(let s=0;s<e.length;s++){let c=e[s];if(!o(c))continue;let l=c.groupId,u=a(c),d=(l?r.get(l):void 0)??r.get(u);if(d!==void 0)i[d].mentions.push(c),t.set(s,d);else{let e=i.length,a={primaryCitation:c,mentions:[c],parallelCitations:n(c)};i.push(a),t.set(s,e),r.set(u,e),l&&r.set(l,e)}}for(let n=0;n<e.length;n++){let r=e[n];if(r.type!==`id`&&r.type!==`supra`&&r.type!==`shortFormCase`)continue;let a=s(r);if(a?.resolvedTo===void 0)continue;let o=t.get(a.resolvedTo);o!==void 0&&(i[o].mentions.push(r),t.set(n,o))}for(let e of i)e.mentions.sort((e,t)=>e.span.originalStart-t.span.originalStart);return i}const l=new Set(`v,vs,U.S,S.Ct,S. Ct,L.Ed,L. Ed,F,F.2d,F.3d,F.4th,F.Supp,F. Supp,A.2d,A.3d,N.E,N.E.2d,N.W,N.W.2d,S.E,S.E.2d,S.W,S.W.2d,S.W.3d,So,So.2d,So.3d,P,P.2d,P.3d,No,Nos,Inc,Corp,Ltd,Co,Ass'n,Dept,Dist,Cir,App,Supp,Rev,Stat,Const,Mr,Mrs,Ms,Dr,Jr,Sr,St,Ct,Atl,Cal,Fla,Ill,Tex,Pa,Md,Va,Wis,Minn,Mich,Mass,Conn,Colo,Ariz,Ark,Ga,La,Ind,Kan,Ky,Miss,Mo,Neb,Nev,Okla,Or,Tenn,Vt,Wash,Wyo,Del,Haw,Ida,Me,Mont,R.I,S.C,S.D,N.C,N.D,N.J,N.M,N.Y,W.Va,U.S.C,C.F.R,Fed,Reg,Pub,Amend,Sec,Art,Cl,Ch,Pt,Vol,Ed,Harv,Yale,Stan,Colum,Geo`.split(`,`));function u(e,t){let n=t;for(;n>0&&e[n-1]!==` `&&e[n-1]!==`
2
2
  `;)n--;let r=e.slice(n,t);if(r.length===1&&/[A-Z]/.test(r)){let r=t+1<e.length?e[t+1]:``;if((n>0?e[n-1]:``)===`.`||/[A-Za-z0-9]/.test(r))return!0}let i=r.replace(/\.$/g,``);return!!(l.has(i)||l.has(r))}function d(e,t){for(let n=t-1;n>=0;n--){let r=e[n];if(r===`.`||r===`?`||r===`!`){if(r===`.`&&u(e,n))continue;let i=n+1;if(i<e.length&&/\s/.test(e[i])){let n=i;for(;n<t&&/\s/.test(e[n]);)n++;return n}}}return 0}function f(e,t){for(let n=t;n<e.length;n++){let t=e[n];if(t===`.`||t===`?`||t===`!`){if(t===`.`&&u(e,n))continue;return n+1}}return e.length}function p(e,t,n){let r=n?.type??`sentence`,i=n?.maxLength,a,o;if(r===`paragraph`){let n=e.lastIndexOf(`
3
3
 
4
4
  `,t.start);a=n===-1?0:n+2;let r=e.indexOf(`
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/utils/reporterKey.ts","../../src/utils/bluebook.ts","../../src/utils/groupByCase.ts","../../src/utils/context.ts"],"sourcesContent":["import type { FullCaseCitation } from \"../types/citation\"\n\n/**\n * Format a volume-reporter-page key from citation fields.\n */\nfunction formatKey(volume: number | string, reporter: string, page: number | undefined): string {\n if (page === undefined) {\n return `${volume} ${reporter}`\n }\n return `${volume} ${reporter} ${page}`\n}\n\n/**\n * Extract the volume-reporter-page lookup key from a case citation.\n *\n * Strips case name, pincite, year, and parenthetical.\n * Uses `normalizedReporter` when available, falls back to `reporter`.\n * Omits the page for blank-page citations.\n *\n * @example\n * ```typescript\n * toReporterKey(citation) // \"550 U.S. 544\"\n * ```\n */\nexport function toReporterKey(citation: FullCaseCitation): string {\n const reporter = citation.normalizedReporter ?? citation.reporter\n const page = citation.hasBlankPage ? undefined : citation.page\n return formatKey(citation.volume, reporter, page)\n}\n\n/**\n * Extract all volume-reporter-page lookup keys from a case citation,\n * including parallel citations.\n *\n * Returns the primary key first, followed by any parallel citation keys.\n *\n * @example\n * ```typescript\n * toReporterKeys(citation) // [\"410 U.S. 113\", \"93 S. Ct. 705\"]\n * ```\n */\nexport function toReporterKeys(citation: FullCaseCitation): string[] {\n const keys = [toReporterKey(citation)]\n\n if (citation.parallelCitations?.length) {\n for (const p of citation.parallelCitations) {\n keys.push(formatKey(p.volume, p.reporter, p.page))\n }\n }\n\n return keys\n}\n","import type { Citation } from \"../types/citation\"\n\n/** Convert an integer to a Roman numeral (1-27 covers all amendments + articles). */\nfunction toRoman(n: number): string {\n const numerals: Array<[number, string]> = [\n [10, \"X\"],\n [9, \"IX\"],\n [5, \"V\"],\n [4, \"IV\"],\n [1, \"I\"],\n ]\n let result = \"\"\n let remaining = n\n for (const [value, numeral] of numerals) {\n while (remaining >= value) {\n result += numeral\n remaining -= value\n }\n }\n return result\n}\n\n/**\n * Reconstruct a canonical Bluebook-style citation string from structured fields.\n *\n * Works across all 11 citation types via the discriminated union.\n * Best-effort: uses whatever fields are available on the citation object.\n *\n * @example\n * ```typescript\n * toBluebook(caseCitation) // \"Bell Atl. Corp. v. Twombly, 550 U.S. 544 (2007)\"\n * toBluebook(statuteCite) // \"42 U.S.C. § 1983\"\n * toBluebook(idCite) // \"Id. at 570\"\n * ```\n */\nexport function toBluebook(citation: Citation): string {\n switch (citation.type) {\n case \"case\": {\n const reporter = citation.normalizedReporter ?? citation.reporter\n let pageStr: string\n if (citation.hasBlankPage) {\n pageStr = \" ___\"\n } else if (citation.page !== undefined) {\n pageStr = ` ${citation.page}`\n } else {\n pageStr = \"\"\n }\n\n const core = `${citation.volume} ${reporter}${pageStr}`\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n const caseName = citation.caseName ? `${citation.caseName}, ` : \"\"\n\n return `${caseName}${core}${pincite}${year}`\n }\n\n case \"statute\": {\n const title = citation.title !== undefined ? `${citation.title} ` : \"\"\n const section = `\\u00A7 ${citation.section}`\n const subsection = citation.subsection ?? \"\"\n const etSeq = citation.hasEtSeq ? \" et seq.\" : \"\"\n return `${title}${citation.code} ${section}${subsection}${etSeq}`\n }\n\n case \"constitutional\": {\n const jurisdiction = citation.jurisdiction === \"US\" ? \"U.S.\" : (citation.jurisdiction ?? \"\")\n const prefix = `${jurisdiction} Const.`\n\n let body = \"\"\n if (citation.article !== undefined) {\n body += ` art. ${toRoman(citation.article)}`\n }\n if (citation.amendment !== undefined) {\n body += ` amend. ${toRoman(citation.amendment)}`\n }\n if (citation.section !== undefined) {\n body += `, \\u00A7 ${citation.section}`\n }\n if (citation.clause !== undefined) {\n body += `, cl. ${citation.clause}`\n }\n return `${prefix}${body}`\n }\n\n case \"journal\": {\n const vol = citation.volume !== undefined ? `${citation.volume} ` : \"\"\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${vol}${citation.abbreviation}${page}${pincite}${year}`\n }\n\n case \"neutral\":\n return `${citation.year} ${citation.court} ${citation.documentNumber}`\n\n case \"publicLaw\":\n return `Pub. L. No. ${citation.congress}-${citation.lawNumber}`\n\n case \"federalRegister\": {\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${citation.volume} Fed. Reg. ${citation.page}${year}`\n }\n\n case \"statutesAtLarge\":\n return `${citation.volume} Stat. ${citation.page}`\n\n case \"id\":\n return citation.pincite !== undefined ? `Id. at ${citation.pincite}` : \"Id.\"\n\n case \"supra\":\n return citation.pincite !== undefined\n ? `${citation.partyName}, supra, at ${citation.pincite}`\n : `${citation.partyName}, supra`\n\n case \"shortFormCase\": {\n const reporter = citation.reporter\n if (citation.pincite !== undefined) {\n return `${citation.volume} ${reporter} at ${citation.pincite}`\n }\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n return `${citation.volume} ${reporter}${page}`\n }\n }\n}\n","import type { Citation, FullCaseCitation } from \"../types/citation\"\nimport type { ResolvedCitation, ResolutionResult } from \"../resolve/types\"\nimport type { CaseGroup } from \"./types\"\nimport { toReporterKeys } from \"./reporterKey\"\n\n/**\n * Build a lookup key for a full case citation: \"volume-reporter-page\".\n * Used to group duplicate full citations that lack a parallel groupId.\n */\nfunction citeKey(c: FullCaseCitation): string {\n return `${c.volume}-${c.reporter}-${c.page ?? \"blank\"}`\n}\n\n/** Type guard: narrow a Citation to FullCaseCitation after checking type === \"case\". */\nfunction isFullCase(cite: Citation): cite is FullCaseCitation {\n return cite.type === \"case\"\n}\n\n/** Extract resolution from a resolved short-form citation. */\nfunction getResolution(cite: ResolvedCitation): ResolutionResult | undefined {\n return (cite as ResolvedCitation & { resolution?: ResolutionResult }).resolution\n}\n\n/**\n * Group resolved citations by underlying case.\n *\n * Composes parallel linking, resolution, and volume/reporter/page identity\n * into `CaseGroup` objects. Non-case citations are ignored. Unresolved\n * short-form citations are excluded.\n *\n * @example\n * ```typescript\n * const citations = extractCitations(text)\n * const resolved = resolveCitations(citations, text)\n * const groups = groupByCase(resolved)\n * ```\n */\nexport function groupByCase(citations: ResolvedCitation[]): CaseGroup[] {\n // Map from citation index -> group index (for short-form resolution lookup)\n const indexToGroup = new Map<number, number>()\n // Map from groupId or citeKey -> group index (for dedup)\n const keyToGroup = new Map<string, number>()\n const groups: CaseGroup[] = []\n\n // First pass: assign full case citations to groups\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (!isFullCase(cite)) continue\n\n // Check if this citation belongs to an existing group\n const gid = cite.groupId\n const key = citeKey(cite)\n const existingIdx = (gid ? keyToGroup.get(gid) : undefined) ?? keyToGroup.get(key)\n\n if (existingIdx !== undefined) {\n // Add to existing group\n groups[existingIdx].mentions.push(cite)\n indexToGroup.set(i, existingIdx)\n } else {\n // Create new group\n const groupIdx = groups.length\n const group: CaseGroup = {\n primaryCitation: cite,\n mentions: [cite],\n parallelCitations: toReporterKeys(cite),\n }\n groups.push(group)\n indexToGroup.set(i, groupIdx)\n keyToGroup.set(key, groupIdx)\n if (gid) {\n keyToGroup.set(gid, groupIdx)\n }\n }\n }\n\n // Second pass: assign short-form citations to their resolved group\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (cite.type !== \"id\" && cite.type !== \"supra\" && cite.type !== \"shortFormCase\") continue\n\n const resolution = getResolution(cite)\n if (resolution?.resolvedTo === undefined) continue\n\n const groupIdx = indexToGroup.get(resolution.resolvedTo)\n if (groupIdx === undefined) continue\n\n groups[groupIdx].mentions.push(cite)\n indexToGroup.set(i, groupIdx)\n }\n\n // Sort mentions within each group by document position\n for (const group of groups) {\n group.mentions.sort((a, b) => a.span.originalStart - b.span.originalStart)\n }\n\n return groups\n}\n","import type { ContextOptions, SurroundingContext } from \"./types\"\n\n/**\n * Legal abbreviations that contain periods but are NOT sentence boundaries.\n * Kept as a static set in this file — does NOT import from src/data/\n * to preserve tree-shaking of the utils entry point.\n */\nconst LEGAL_ABBREVIATIONS = new Set([\n // Court and case abbreviations\n \"v\",\n \"vs\",\n // Reporter abbreviations (common ones)\n \"U.S\",\n \"S.Ct\",\n \"S. Ct\",\n \"L.Ed\",\n \"L. Ed\",\n \"F\",\n \"F.2d\",\n \"F.3d\",\n \"F.4th\",\n \"F.Supp\",\n \"F. Supp\",\n \"A.2d\",\n \"A.3d\",\n \"N.E\",\n \"N.E.2d\",\n \"N.W\",\n \"N.W.2d\",\n \"S.E\",\n \"S.E.2d\",\n \"S.W\",\n \"S.W.2d\",\n \"S.W.3d\",\n \"So\",\n \"So.2d\",\n \"So.3d\",\n \"P\",\n \"P.2d\",\n \"P.3d\",\n // Titles and procedural terms\n \"No\",\n \"Nos\",\n \"Inc\",\n \"Corp\",\n \"Ltd\",\n \"Co\",\n \"Ass'n\",\n \"Dept\",\n \"Dist\",\n \"Cir\",\n \"App\",\n \"Supp\",\n \"Rev\",\n \"Stat\",\n \"Const\",\n // General legal abbreviations\n \"Mr\",\n \"Mrs\",\n \"Ms\",\n \"Dr\",\n \"Jr\",\n \"Sr\",\n \"St\",\n \"Ct\",\n \"Atl\",\n \"Cal\",\n \"Fla\",\n \"Ill\",\n \"Tex\",\n \"Pa\",\n \"Md\",\n \"Va\",\n \"Wis\",\n \"Minn\",\n \"Mich\",\n \"Mass\",\n \"Conn\",\n \"Colo\",\n \"Ariz\",\n \"Ark\",\n \"Ga\",\n \"La\",\n \"Ind\",\n \"Kan\",\n \"Ky\",\n \"Miss\",\n \"Mo\",\n \"Neb\",\n \"Nev\",\n \"Okla\",\n \"Or\",\n \"Tenn\",\n \"Vt\",\n \"Wash\",\n \"Wyo\",\n \"Del\",\n \"Haw\",\n \"Ida\",\n \"Me\",\n \"Mont\",\n \"R.I\",\n \"S.C\",\n \"S.D\",\n \"N.C\",\n \"N.D\",\n \"N.J\",\n \"N.M\",\n \"N.Y\",\n \"W.Va\",\n // Federal abbreviations\n \"U.S.C\",\n \"C.F.R\",\n \"Fed\",\n \"Reg\",\n \"Pub\",\n \"Amend\",\n \"Sec\",\n \"Art\",\n \"Cl\",\n \"Ch\",\n \"Pt\",\n \"Vol\",\n \"Ed\",\n \"Harv\",\n \"Yale\",\n \"Stan\",\n \"Colum\",\n \"Geo\",\n])\n\n/**\n * Check if a period at the given position is likely an abbreviation,\n * not a sentence boundary.\n */\nfunction isAbbreviationPeriod(text: string, dotIndex: number): boolean {\n // Look backwards from the dot to find the word\n let wordStart = dotIndex\n while (wordStart > 0 && text[wordStart - 1] !== \" \" && text[wordStart - 1] !== \"\\n\") {\n wordStart--\n }\n\n const word = text.slice(wordStart, dotIndex)\n\n // Single letter followed by period (e.g., \"U.\", \"S.\", \"F.\")\n // Only treat as abbreviation if part of a dotted sequence:\n // - preceded by a period (like the \"S\" in \"U.S.\")\n // - followed by a letter/digit (like the \"F\" in \"F.2d\")\n if (word.length === 1 && /[A-Z]/.test(word)) {\n const charAfterDot = dotIndex + 1 < text.length ? text[dotIndex + 1] : \"\"\n const charBeforeWord = wordStart > 0 ? text[wordStart - 1] : \"\"\n if (charBeforeWord === \".\" || /[A-Za-z0-9]/.test(charAfterDot)) return true\n }\n\n // Check multi-character abbreviations (strip any trailing dots for lookup)\n const stripped = word.replace(/\\.$/g, \"\")\n if (LEGAL_ABBREVIATIONS.has(stripped)) return true\n\n // Check if the word itself (with internal dots) is known: \"U.S\", \"F.2d\", etc.\n if (LEGAL_ABBREVIATIONS.has(word)) return true\n\n // Number followed by period (ordinals like \"1.\" in list context — not sentence end if no space+uppercase follows)\n // This is handled by the caller's space+uppercase check\n\n return false\n}\n\n/**\n * Find the start of the sentence containing the given position.\n */\nfunction findSentenceStart(text: string, pos: number): number {\n for (let i = pos - 1; i >= 0; i--) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n\n // Check if followed by whitespace (the char after this terminator)\n const next = i + 1\n if (next < text.length && /\\s/.test(text[next])) {\n // Skip whitespace to find the start of the next sentence\n let start = next\n while (start < pos && /\\s/.test(text[start])) start++\n return start\n }\n }\n }\n return 0\n}\n\n/**\n * Find the end of the sentence containing the given position.\n */\nfunction findSentenceEnd(text: string, pos: number): number {\n for (let i = pos; i < text.length; i++) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n return i + 1\n }\n }\n return text.length\n}\n\n/**\n * Find the enclosing sentence or paragraph around a citation span.\n *\n * Legal-text-aware: periods in reporter abbreviations, court names,\n * and procedural terms (Corp., U.S., F.3d, No., v.) are not treated\n * as sentence boundaries.\n *\n * @example\n * ```typescript\n * const ctx = getSurroundingContext(text, { start: 33, end: 52 })\n * // ctx.text: \"In Smith v. Doe, 500 F.2d 123 (2020), the Court held X.\"\n * // ctx.span: { start: 16, end: 71 }\n * ```\n */\nexport function getSurroundingContext(\n text: string,\n span: { start: number; end: number },\n options?: ContextOptions,\n): SurroundingContext {\n const type = options?.type ?? \"sentence\"\n const maxLength = options?.maxLength\n\n let start: number\n let end: number\n\n if (type === \"paragraph\") {\n // Find paragraph boundaries (double newline)\n const beforeSpan = text.lastIndexOf(\"\\n\\n\", span.start)\n start = beforeSpan === -1 ? 0 : beforeSpan + 2\n const afterSpan = text.indexOf(\"\\n\\n\", span.end)\n end = afterSpan === -1 ? text.length : afterSpan\n } else {\n start = findSentenceStart(text, span.start)\n end = findSentenceEnd(text, span.end)\n }\n\n const raw = text.slice(start, end)\n const resultText = raw.trim()\n const trimmedStart = start + (raw.length - raw.trimStart().length)\n const trimmedEnd = end - (raw.length - raw.trimEnd().length)\n\n if (maxLength && resultText.length > maxLength) {\n const truncated = resultText.slice(0, maxLength)\n return {\n text: truncated,\n span: { start: trimmedStart, end: trimmedStart + truncated.length },\n }\n }\n\n return {\n text: resultText,\n span: { start: trimmedStart, end: trimmedEnd },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/utils/reporterKey.ts","../../src/utils/bluebook.ts","../../src/utils/groupByCase.ts","../../src/utils/context.ts"],"sourcesContent":["import type { FullCaseCitation } from \"../types/citation\"\n\n/**\n * Format a volume-reporter-page key from citation fields.\n */\nfunction formatKey(volume: number | string, reporter: string, page: number | undefined): string {\n if (page === undefined) {\n return `${volume} ${reporter}`\n }\n return `${volume} ${reporter} ${page}`\n}\n\n/**\n * Extract the volume-reporter-page lookup key from a case citation.\n *\n * Strips case name, pincite, year, and parenthetical.\n * Uses `normalizedReporter` when available, falls back to `reporter`.\n * Omits the page for blank-page citations.\n *\n * @example\n * ```typescript\n * toReporterKey(citation) // \"550 U.S. 544\"\n * ```\n */\nexport function toReporterKey(citation: FullCaseCitation): string {\n const reporter = citation.normalizedReporter ?? citation.reporter\n const page = citation.hasBlankPage ? undefined : citation.page\n return formatKey(citation.volume, reporter, page)\n}\n\n/**\n * Extract all volume-reporter-page lookup keys from a case citation,\n * including parallel citations.\n *\n * Returns the primary key first, followed by any parallel citation keys.\n *\n * @example\n * ```typescript\n * toReporterKeys(citation) // [\"410 U.S. 113\", \"93 S. Ct. 705\"]\n * ```\n */\nexport function toReporterKeys(citation: FullCaseCitation): string[] {\n const keys = [toReporterKey(citation)]\n\n if (citation.parallelCitations?.length) {\n for (const p of citation.parallelCitations) {\n keys.push(formatKey(p.volume, p.reporter, p.page))\n }\n }\n\n return keys\n}\n","import type { Citation } from \"../types/citation\"\n\n/** Convert an integer to a Roman numeral (1-27 covers all amendments + articles). */\nfunction toRoman(n: number): string {\n const numerals: Array<[number, string]> = [\n [10, \"X\"],\n [9, \"IX\"],\n [5, \"V\"],\n [4, \"IV\"],\n [1, \"I\"],\n ]\n let result = \"\"\n let remaining = n\n for (const [value, numeral] of numerals) {\n while (remaining >= value) {\n result += numeral\n remaining -= value\n }\n }\n return result\n}\n\n/**\n * Reconstruct a canonical Bluebook-style citation string from structured fields.\n *\n * Works across all 11 citation types via the discriminated union.\n * Best-effort: uses whatever fields are available on the citation object.\n *\n * @example\n * ```typescript\n * toBluebook(caseCitation) // \"Bell Atl. Corp. v. Twombly, 550 U.S. 544 (2007)\"\n * toBluebook(statuteCite) // \"42 U.S.C. § 1983\"\n * toBluebook(idCite) // \"Id. at 570\"\n * ```\n */\nexport function toBluebook(citation: Citation): string {\n switch (citation.type) {\n case \"case\": {\n const reporter = citation.normalizedReporter ?? citation.reporter\n let pageStr: string\n if (citation.hasBlankPage) {\n pageStr = \" ___\"\n } else if (citation.page !== undefined) {\n pageStr = ` ${citation.page}`\n } else {\n pageStr = \"\"\n }\n\n const core = `${citation.volume} ${reporter}${pageStr}`\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n const caseName = citation.caseName ? `${citation.caseName}, ` : \"\"\n\n return `${caseName}${core}${pincite}${year}`\n }\n\n case \"statute\": {\n const title = citation.title !== undefined ? `${citation.title} ` : \"\"\n const section = `\\u00A7 ${citation.section}`\n const subsection = citation.subsection ?? \"\"\n const etSeq = citation.hasEtSeq ? \" et seq.\" : \"\"\n return `${title}${citation.code} ${section}${subsection}${etSeq}`\n }\n\n case \"constitutional\": {\n const jurisdiction = citation.jurisdiction === \"US\" ? \"U.S.\" : (citation.jurisdiction ?? \"\")\n const prefix = `${jurisdiction} Const.`\n\n let body = \"\"\n if (citation.article !== undefined) {\n body += ` art. ${toRoman(citation.article)}`\n }\n if (citation.amendment !== undefined) {\n body += ` amend. ${toRoman(citation.amendment)}`\n }\n if (citation.section !== undefined) {\n body += `, \\u00A7 ${citation.section}`\n }\n if (citation.clause !== undefined) {\n body += `, cl. ${citation.clause}`\n }\n return `${prefix}${body}`\n }\n\n case \"journal\": {\n const vol = citation.volume !== undefined ? `${citation.volume} ` : \"\"\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${vol}${citation.abbreviation}${page}${pincite}${year}`\n }\n\n case \"docket\": {\n const caseName = citation.caseName ? `${citation.caseName}, ` : \"\"\n const courtAndYear =\n citation.court && citation.year\n ? ` (${citation.court} ${citation.year})`\n : citation.court\n ? ` (${citation.court})`\n : citation.year\n ? ` (${citation.year})`\n : \"\"\n return `${caseName}No. ${citation.docketNumber}${courtAndYear}`\n }\n\n case \"neutral\":\n return `${citation.year} ${citation.court} ${citation.documentNumber}`\n\n case \"publicLaw\":\n return `Pub. L. No. ${citation.congress}-${citation.lawNumber}`\n\n case \"federalRegister\": {\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${citation.volume} Fed. Reg. ${citation.page}${year}`\n }\n\n case \"statutesAtLarge\":\n return `${citation.volume} Stat. ${citation.page}`\n\n case \"id\":\n return citation.pincite !== undefined ? `Id. at ${citation.pincite}` : \"Id.\"\n\n case \"supra\":\n return citation.pincite !== undefined\n ? `${citation.partyName}, supra, at ${citation.pincite}`\n : `${citation.partyName}, supra`\n\n case \"shortFormCase\": {\n const reporter = citation.reporter\n if (citation.pincite !== undefined) {\n return `${citation.volume} ${reporter} at ${citation.pincite}`\n }\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n return `${citation.volume} ${reporter}${page}`\n }\n }\n}\n","import type { Citation, FullCaseCitation } from \"../types/citation\"\nimport type { ResolvedCitation, ResolutionResult } from \"../resolve/types\"\nimport type { CaseGroup } from \"./types\"\nimport { toReporterKeys } from \"./reporterKey\"\n\n/**\n * Build a lookup key for a full case citation: \"volume-reporter-page\".\n * Used to group duplicate full citations that lack a parallel groupId.\n */\nfunction citeKey(c: FullCaseCitation): string {\n return `${c.volume}-${c.reporter}-${c.page ?? \"blank\"}`\n}\n\n/** Type guard: narrow a Citation to FullCaseCitation after checking type === \"case\". */\nfunction isFullCase(cite: Citation): cite is FullCaseCitation {\n return cite.type === \"case\"\n}\n\n/** Extract resolution from a resolved short-form citation. */\nfunction getResolution(cite: ResolvedCitation): ResolutionResult | undefined {\n return (cite as ResolvedCitation & { resolution?: ResolutionResult }).resolution\n}\n\n/**\n * Group resolved citations by underlying case.\n *\n * Composes parallel linking, resolution, and volume/reporter/page identity\n * into `CaseGroup` objects. Non-case citations are ignored. Unresolved\n * short-form citations are excluded.\n *\n * @example\n * ```typescript\n * const citations = extractCitations(text)\n * const resolved = resolveCitations(citations, text)\n * const groups = groupByCase(resolved)\n * ```\n */\nexport function groupByCase(citations: ResolvedCitation[]): CaseGroup[] {\n // Map from citation index -> group index (for short-form resolution lookup)\n const indexToGroup = new Map<number, number>()\n // Map from groupId or citeKey -> group index (for dedup)\n const keyToGroup = new Map<string, number>()\n const groups: CaseGroup[] = []\n\n // First pass: assign full case citations to groups\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (!isFullCase(cite)) continue\n\n // Check if this citation belongs to an existing group\n const gid = cite.groupId\n const key = citeKey(cite)\n const existingIdx = (gid ? keyToGroup.get(gid) : undefined) ?? keyToGroup.get(key)\n\n if (existingIdx !== undefined) {\n // Add to existing group\n groups[existingIdx].mentions.push(cite)\n indexToGroup.set(i, existingIdx)\n } else {\n // Create new group\n const groupIdx = groups.length\n const group: CaseGroup = {\n primaryCitation: cite,\n mentions: [cite],\n parallelCitations: toReporterKeys(cite),\n }\n groups.push(group)\n indexToGroup.set(i, groupIdx)\n keyToGroup.set(key, groupIdx)\n if (gid) {\n keyToGroup.set(gid, groupIdx)\n }\n }\n }\n\n // Second pass: assign short-form citations to their resolved group\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (cite.type !== \"id\" && cite.type !== \"supra\" && cite.type !== \"shortFormCase\") continue\n\n const resolution = getResolution(cite)\n if (resolution?.resolvedTo === undefined) continue\n\n const groupIdx = indexToGroup.get(resolution.resolvedTo)\n if (groupIdx === undefined) continue\n\n groups[groupIdx].mentions.push(cite)\n indexToGroup.set(i, groupIdx)\n }\n\n // Sort mentions within each group by document position\n for (const group of groups) {\n group.mentions.sort((a, b) => a.span.originalStart - b.span.originalStart)\n }\n\n return groups\n}\n","import type { ContextOptions, SurroundingContext } from \"./types\"\n\n/**\n * Legal abbreviations that contain periods but are NOT sentence boundaries.\n * Kept as a static set in this file — does NOT import from src/data/\n * to preserve tree-shaking of the utils entry point.\n */\nconst LEGAL_ABBREVIATIONS = new Set([\n // Court and case abbreviations\n \"v\",\n \"vs\",\n // Reporter abbreviations (common ones)\n \"U.S\",\n \"S.Ct\",\n \"S. Ct\",\n \"L.Ed\",\n \"L. Ed\",\n \"F\",\n \"F.2d\",\n \"F.3d\",\n \"F.4th\",\n \"F.Supp\",\n \"F. Supp\",\n \"A.2d\",\n \"A.3d\",\n \"N.E\",\n \"N.E.2d\",\n \"N.W\",\n \"N.W.2d\",\n \"S.E\",\n \"S.E.2d\",\n \"S.W\",\n \"S.W.2d\",\n \"S.W.3d\",\n \"So\",\n \"So.2d\",\n \"So.3d\",\n \"P\",\n \"P.2d\",\n \"P.3d\",\n // Titles and procedural terms\n \"No\",\n \"Nos\",\n \"Inc\",\n \"Corp\",\n \"Ltd\",\n \"Co\",\n \"Ass'n\",\n \"Dept\",\n \"Dist\",\n \"Cir\",\n \"App\",\n \"Supp\",\n \"Rev\",\n \"Stat\",\n \"Const\",\n // General legal abbreviations\n \"Mr\",\n \"Mrs\",\n \"Ms\",\n \"Dr\",\n \"Jr\",\n \"Sr\",\n \"St\",\n \"Ct\",\n \"Atl\",\n \"Cal\",\n \"Fla\",\n \"Ill\",\n \"Tex\",\n \"Pa\",\n \"Md\",\n \"Va\",\n \"Wis\",\n \"Minn\",\n \"Mich\",\n \"Mass\",\n \"Conn\",\n \"Colo\",\n \"Ariz\",\n \"Ark\",\n \"Ga\",\n \"La\",\n \"Ind\",\n \"Kan\",\n \"Ky\",\n \"Miss\",\n \"Mo\",\n \"Neb\",\n \"Nev\",\n \"Okla\",\n \"Or\",\n \"Tenn\",\n \"Vt\",\n \"Wash\",\n \"Wyo\",\n \"Del\",\n \"Haw\",\n \"Ida\",\n \"Me\",\n \"Mont\",\n \"R.I\",\n \"S.C\",\n \"S.D\",\n \"N.C\",\n \"N.D\",\n \"N.J\",\n \"N.M\",\n \"N.Y\",\n \"W.Va\",\n // Federal abbreviations\n \"U.S.C\",\n \"C.F.R\",\n \"Fed\",\n \"Reg\",\n \"Pub\",\n \"Amend\",\n \"Sec\",\n \"Art\",\n \"Cl\",\n \"Ch\",\n \"Pt\",\n \"Vol\",\n \"Ed\",\n \"Harv\",\n \"Yale\",\n \"Stan\",\n \"Colum\",\n \"Geo\",\n])\n\n/**\n * Check if a period at the given position is likely an abbreviation,\n * not a sentence boundary.\n */\nfunction isAbbreviationPeriod(text: string, dotIndex: number): boolean {\n // Look backwards from the dot to find the word\n let wordStart = dotIndex\n while (wordStart > 0 && text[wordStart - 1] !== \" \" && text[wordStart - 1] !== \"\\n\") {\n wordStart--\n }\n\n const word = text.slice(wordStart, dotIndex)\n\n // Single letter followed by period (e.g., \"U.\", \"S.\", \"F.\")\n // Only treat as abbreviation if part of a dotted sequence:\n // - preceded by a period (like the \"S\" in \"U.S.\")\n // - followed by a letter/digit (like the \"F\" in \"F.2d\")\n if (word.length === 1 && /[A-Z]/.test(word)) {\n const charAfterDot = dotIndex + 1 < text.length ? text[dotIndex + 1] : \"\"\n const charBeforeWord = wordStart > 0 ? text[wordStart - 1] : \"\"\n if (charBeforeWord === \".\" || /[A-Za-z0-9]/.test(charAfterDot)) return true\n }\n\n // Check multi-character abbreviations (strip any trailing dots for lookup)\n const stripped = word.replace(/\\.$/g, \"\")\n if (LEGAL_ABBREVIATIONS.has(stripped)) return true\n\n // Check if the word itself (with internal dots) is known: \"U.S\", \"F.2d\", etc.\n if (LEGAL_ABBREVIATIONS.has(word)) return true\n\n // Number followed by period (ordinals like \"1.\" in list context — not sentence end if no space+uppercase follows)\n // This is handled by the caller's space+uppercase check\n\n return false\n}\n\n/**\n * Find the start of the sentence containing the given position.\n */\nfunction findSentenceStart(text: string, pos: number): number {\n for (let i = pos - 1; i >= 0; i--) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n\n // Check if followed by whitespace (the char after this terminator)\n const next = i + 1\n if (next < text.length && /\\s/.test(text[next])) {\n // Skip whitespace to find the start of the next sentence\n let start = next\n while (start < pos && /\\s/.test(text[start])) start++\n return start\n }\n }\n }\n return 0\n}\n\n/**\n * Find the end of the sentence containing the given position.\n */\nfunction findSentenceEnd(text: string, pos: number): number {\n for (let i = pos; i < text.length; i++) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n return i + 1\n }\n }\n return text.length\n}\n\n/**\n * Find the enclosing sentence or paragraph around a citation span.\n *\n * Legal-text-aware: periods in reporter abbreviations, court names,\n * and procedural terms (Corp., U.S., F.3d, No., v.) are not treated\n * as sentence boundaries.\n *\n * @example\n * ```typescript\n * const ctx = getSurroundingContext(text, { start: 33, end: 52 })\n * // ctx.text: \"In Smith v. Doe, 500 F.2d 123 (2020), the Court held X.\"\n * // ctx.span: { start: 16, end: 71 }\n * ```\n */\nexport function getSurroundingContext(\n text: string,\n span: { start: number; end: number },\n options?: ContextOptions,\n): SurroundingContext {\n const type = options?.type ?? \"sentence\"\n const maxLength = options?.maxLength\n\n let start: number\n let end: number\n\n if (type === \"paragraph\") {\n // Find paragraph boundaries (double newline)\n const beforeSpan = text.lastIndexOf(\"\\n\\n\", span.start)\n start = beforeSpan === -1 ? 0 : beforeSpan + 2\n const afterSpan = text.indexOf(\"\\n\\n\", span.end)\n end = afterSpan === -1 ? text.length : afterSpan\n } else {\n start = findSentenceStart(text, span.start)\n end = findSentenceEnd(text, span.end)\n }\n\n const raw = text.slice(start, end)\n const resultText = raw.trim()\n const trimmedStart = start + (raw.length - raw.trimStart().length)\n const trimmedEnd = end - (raw.length - raw.trimEnd().length)\n\n if (maxLength && resultText.length > maxLength) {\n const truncated = resultText.slice(0, maxLength)\n return {\n text: truncated,\n span: { start: trimmedStart, end: trimmedStart + truncated.length },\n }\n }\n\n return {\n text: resultText,\n span: { start: trimmedStart, end: trimmedEnd },\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { t as Citation, u as FullCaseCitation } from "../citation-ChDj3ZlY.cjs";
2
- import { r as ResolvedCitation } from "../types-C7IqHYnj.cjs";
1
+ import { d as FullCaseCitation, t as Citation } from "../citation-BaRqFZnV.cjs";
2
+ import { r as ResolvedCitation } from "../types-BgmSZol5.cjs";
3
3
 
4
4
  //#region src/utils/types.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { t as Citation, u as FullCaseCitation } from "../citation-C-GPQmt3.mjs";
2
- import { r as ResolvedCitation } from "../types-lHL_1ON_.mjs";
1
+ import { d as FullCaseCitation, t as Citation } from "../citation-CVm_rp15.mjs";
2
+ import { r as ResolvedCitation } from "../types-DQzZmylJ.mjs";
3
3
 
4
4
  //#region src/utils/types.d.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- function e(e,t,n){return n===void 0?`${e} ${t}`:`${e} ${t} ${n}`}function t(t){let n=t.normalizedReporter??t.reporter,r=t.hasBlankPage?void 0:t.page;return e(t.volume,n,r)}function n(n){let r=[t(n)];if(n.parallelCitations?.length)for(let t of n.parallelCitations)r.push(e(t.volume,t.reporter,t.page));return r}function r(e){let t=[[10,`X`],[9,`IX`],[5,`V`],[4,`IV`],[1,`I`]],n=``,r=e;for(let[e,i]of t)for(;r>=e;)n+=i,r-=e;return n}function i(e){switch(e.type){case`case`:{let t=e.normalizedReporter??e.reporter,n;n=e.hasBlankPage?` ___`:e.page===void 0?``:` ${e.page}`;let r=`${e.volume} ${t}${n}`,i=e.pincite===void 0?``:`, ${e.pincite}`,a=e.year===void 0?``:` (${e.year})`;return`${e.caseName?`${e.caseName}, `:``}${r}${i}${a}`}case`statute`:{let t=e.title===void 0?``:`${e.title} `,n=`\u00A7 ${e.section}`,r=e.subsection??``,i=e.hasEtSeq?` et seq.`:``;return`${t}${e.code} ${n}${r}${i}`}case`constitutional`:{let t=`${e.jurisdiction===`US`?`U.S.`:e.jurisdiction??``} Const.`,n=``;return e.article!==void 0&&(n+=` art. ${r(e.article)}`),e.amendment!==void 0&&(n+=` amend. ${r(e.amendment)}`),e.section!==void 0&&(n+=`, \u00A7 ${e.section}`),e.clause!==void 0&&(n+=`, cl. ${e.clause}`),`${t}${n}`}case`journal`:{let t=e.volume===void 0?``:`${e.volume} `,n=e.page===void 0?``:` ${e.page}`,r=e.pincite===void 0?``:`, ${e.pincite}`,i=e.year===void 0?``:` (${e.year})`;return`${t}${e.abbreviation}${n}${r}${i}`}case`neutral`:return`${e.year} ${e.court} ${e.documentNumber}`;case`publicLaw`:return`Pub. L. No. ${e.congress}-${e.lawNumber}`;case`federalRegister`:{let t=e.year===void 0?``:` (${e.year})`;return`${e.volume} Fed. Reg. ${e.page}${t}`}case`statutesAtLarge`:return`${e.volume} Stat. ${e.page}`;case`id`:return e.pincite===void 0?`Id.`:`Id. at ${e.pincite}`;case`supra`:return e.pincite===void 0?`${e.partyName}, supra`:`${e.partyName}, supra, at ${e.pincite}`;case`shortFormCase`:{let t=e.reporter;if(e.pincite!==void 0)return`${e.volume} ${t} at ${e.pincite}`;let n=e.page===void 0?``:` ${e.page}`;return`${e.volume} ${t}${n}`}}}function a(e){return`${e.volume}-${e.reporter}-${e.page??`blank`}`}function o(e){return e.type===`case`}function s(e){return e.resolution}function c(e){let t=new Map,r=new Map,i=[];for(let s=0;s<e.length;s++){let c=e[s];if(!o(c))continue;let l=c.groupId,u=a(c),d=(l?r.get(l):void 0)??r.get(u);if(d!==void 0)i[d].mentions.push(c),t.set(s,d);else{let e=i.length,a={primaryCitation:c,mentions:[c],parallelCitations:n(c)};i.push(a),t.set(s,e),r.set(u,e),l&&r.set(l,e)}}for(let n=0;n<e.length;n++){let r=e[n];if(r.type!==`id`&&r.type!==`supra`&&r.type!==`shortFormCase`)continue;let a=s(r);if(a?.resolvedTo===void 0)continue;let o=t.get(a.resolvedTo);o!==void 0&&(i[o].mentions.push(r),t.set(n,o))}for(let e of i)e.mentions.sort((e,t)=>e.span.originalStart-t.span.originalStart);return i}const l=new Set(`v,vs,U.S,S.Ct,S. Ct,L.Ed,L. Ed,F,F.2d,F.3d,F.4th,F.Supp,F. Supp,A.2d,A.3d,N.E,N.E.2d,N.W,N.W.2d,S.E,S.E.2d,S.W,S.W.2d,S.W.3d,So,So.2d,So.3d,P,P.2d,P.3d,No,Nos,Inc,Corp,Ltd,Co,Ass'n,Dept,Dist,Cir,App,Supp,Rev,Stat,Const,Mr,Mrs,Ms,Dr,Jr,Sr,St,Ct,Atl,Cal,Fla,Ill,Tex,Pa,Md,Va,Wis,Minn,Mich,Mass,Conn,Colo,Ariz,Ark,Ga,La,Ind,Kan,Ky,Miss,Mo,Neb,Nev,Okla,Or,Tenn,Vt,Wash,Wyo,Del,Haw,Ida,Me,Mont,R.I,S.C,S.D,N.C,N.D,N.J,N.M,N.Y,W.Va,U.S.C,C.F.R,Fed,Reg,Pub,Amend,Sec,Art,Cl,Ch,Pt,Vol,Ed,Harv,Yale,Stan,Colum,Geo`.split(`,`));function u(e,t){let n=t;for(;n>0&&e[n-1]!==` `&&e[n-1]!==`
1
+ function e(e,t,n){return n===void 0?`${e} ${t}`:`${e} ${t} ${n}`}function t(t){let n=t.normalizedReporter??t.reporter,r=t.hasBlankPage?void 0:t.page;return e(t.volume,n,r)}function n(n){let r=[t(n)];if(n.parallelCitations?.length)for(let t of n.parallelCitations)r.push(e(t.volume,t.reporter,t.page));return r}function r(e){let t=[[10,`X`],[9,`IX`],[5,`V`],[4,`IV`],[1,`I`]],n=``,r=e;for(let[e,i]of t)for(;r>=e;)n+=i,r-=e;return n}function i(e){switch(e.type){case`case`:{let t=e.normalizedReporter??e.reporter,n;n=e.hasBlankPage?` ___`:e.page===void 0?``:` ${e.page}`;let r=`${e.volume} ${t}${n}`,i=e.pincite===void 0?``:`, ${e.pincite}`,a=e.year===void 0?``:` (${e.year})`;return`${e.caseName?`${e.caseName}, `:``}${r}${i}${a}`}case`statute`:{let t=e.title===void 0?``:`${e.title} `,n=`\u00A7 ${e.section}`,r=e.subsection??``,i=e.hasEtSeq?` et seq.`:``;return`${t}${e.code} ${n}${r}${i}`}case`constitutional`:{let t=`${e.jurisdiction===`US`?`U.S.`:e.jurisdiction??``} Const.`,n=``;return e.article!==void 0&&(n+=` art. ${r(e.article)}`),e.amendment!==void 0&&(n+=` amend. ${r(e.amendment)}`),e.section!==void 0&&(n+=`, \u00A7 ${e.section}`),e.clause!==void 0&&(n+=`, cl. ${e.clause}`),`${t}${n}`}case`journal`:{let t=e.volume===void 0?``:`${e.volume} `,n=e.page===void 0?``:` ${e.page}`,r=e.pincite===void 0?``:`, ${e.pincite}`,i=e.year===void 0?``:` (${e.year})`;return`${t}${e.abbreviation}${n}${r}${i}`}case`docket`:{let t=e.caseName?`${e.caseName}, `:``,n=e.court&&e.year?` (${e.court} ${e.year})`:e.court?` (${e.court})`:e.year?` (${e.year})`:``;return`${t}No. ${e.docketNumber}${n}`}case`neutral`:return`${e.year} ${e.court} ${e.documentNumber}`;case`publicLaw`:return`Pub. L. No. ${e.congress}-${e.lawNumber}`;case`federalRegister`:{let t=e.year===void 0?``:` (${e.year})`;return`${e.volume} Fed. Reg. ${e.page}${t}`}case`statutesAtLarge`:return`${e.volume} Stat. ${e.page}`;case`id`:return e.pincite===void 0?`Id.`:`Id. at ${e.pincite}`;case`supra`:return e.pincite===void 0?`${e.partyName}, supra`:`${e.partyName}, supra, at ${e.pincite}`;case`shortFormCase`:{let t=e.reporter;if(e.pincite!==void 0)return`${e.volume} ${t} at ${e.pincite}`;let n=e.page===void 0?``:` ${e.page}`;return`${e.volume} ${t}${n}`}}}function a(e){return`${e.volume}-${e.reporter}-${e.page??`blank`}`}function o(e){return e.type===`case`}function s(e){return e.resolution}function c(e){let t=new Map,r=new Map,i=[];for(let s=0;s<e.length;s++){let c=e[s];if(!o(c))continue;let l=c.groupId,u=a(c),d=(l?r.get(l):void 0)??r.get(u);if(d!==void 0)i[d].mentions.push(c),t.set(s,d);else{let e=i.length,a={primaryCitation:c,mentions:[c],parallelCitations:n(c)};i.push(a),t.set(s,e),r.set(u,e),l&&r.set(l,e)}}for(let n=0;n<e.length;n++){let r=e[n];if(r.type!==`id`&&r.type!==`supra`&&r.type!==`shortFormCase`)continue;let a=s(r);if(a?.resolvedTo===void 0)continue;let o=t.get(a.resolvedTo);o!==void 0&&(i[o].mentions.push(r),t.set(n,o))}for(let e of i)e.mentions.sort((e,t)=>e.span.originalStart-t.span.originalStart);return i}const l=new Set(`v,vs,U.S,S.Ct,S. Ct,L.Ed,L. Ed,F,F.2d,F.3d,F.4th,F.Supp,F. Supp,A.2d,A.3d,N.E,N.E.2d,N.W,N.W.2d,S.E,S.E.2d,S.W,S.W.2d,S.W.3d,So,So.2d,So.3d,P,P.2d,P.3d,No,Nos,Inc,Corp,Ltd,Co,Ass'n,Dept,Dist,Cir,App,Supp,Rev,Stat,Const,Mr,Mrs,Ms,Dr,Jr,Sr,St,Ct,Atl,Cal,Fla,Ill,Tex,Pa,Md,Va,Wis,Minn,Mich,Mass,Conn,Colo,Ariz,Ark,Ga,La,Ind,Kan,Ky,Miss,Mo,Neb,Nev,Okla,Or,Tenn,Vt,Wash,Wyo,Del,Haw,Ida,Me,Mont,R.I,S.C,S.D,N.C,N.D,N.J,N.M,N.Y,W.Va,U.S.C,C.F.R,Fed,Reg,Pub,Amend,Sec,Art,Cl,Ch,Pt,Vol,Ed,Harv,Yale,Stan,Colum,Geo`.split(`,`));function u(e,t){let n=t;for(;n>0&&e[n-1]!==` `&&e[n-1]!==`
2
2
  `;)n--;let r=e.slice(n,t);if(r.length===1&&/[A-Z]/.test(r)){let r=t+1<e.length?e[t+1]:``;if((n>0?e[n-1]:``)===`.`||/[A-Za-z0-9]/.test(r))return!0}let i=r.replace(/\.$/g,``);return!!(l.has(i)||l.has(r))}function d(e,t){for(let n=t-1;n>=0;n--){let r=e[n];if(r===`.`||r===`?`||r===`!`){if(r===`.`&&u(e,n))continue;let i=n+1;if(i<e.length&&/\s/.test(e[i])){let n=i;for(;n<t&&/\s/.test(e[n]);)n++;return n}}}return 0}function f(e,t){for(let n=t;n<e.length;n++){let t=e[n];if(t===`.`||t===`?`||t===`!`){if(t===`.`&&u(e,n))continue;return n+1}}return e.length}function p(e,t,n){let r=n?.type??`sentence`,i=n?.maxLength,a,o;if(r===`paragraph`){let n=e.lastIndexOf(`
3
3
 
4
4
  `,t.start);a=n===-1?0:n+2;let r=e.indexOf(`
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/reporterKey.ts","../../src/utils/bluebook.ts","../../src/utils/groupByCase.ts","../../src/utils/context.ts"],"sourcesContent":["import type { FullCaseCitation } from \"../types/citation\"\n\n/**\n * Format a volume-reporter-page key from citation fields.\n */\nfunction formatKey(volume: number | string, reporter: string, page: number | undefined): string {\n if (page === undefined) {\n return `${volume} ${reporter}`\n }\n return `${volume} ${reporter} ${page}`\n}\n\n/**\n * Extract the volume-reporter-page lookup key from a case citation.\n *\n * Strips case name, pincite, year, and parenthetical.\n * Uses `normalizedReporter` when available, falls back to `reporter`.\n * Omits the page for blank-page citations.\n *\n * @example\n * ```typescript\n * toReporterKey(citation) // \"550 U.S. 544\"\n * ```\n */\nexport function toReporterKey(citation: FullCaseCitation): string {\n const reporter = citation.normalizedReporter ?? citation.reporter\n const page = citation.hasBlankPage ? undefined : citation.page\n return formatKey(citation.volume, reporter, page)\n}\n\n/**\n * Extract all volume-reporter-page lookup keys from a case citation,\n * including parallel citations.\n *\n * Returns the primary key first, followed by any parallel citation keys.\n *\n * @example\n * ```typescript\n * toReporterKeys(citation) // [\"410 U.S. 113\", \"93 S. Ct. 705\"]\n * ```\n */\nexport function toReporterKeys(citation: FullCaseCitation): string[] {\n const keys = [toReporterKey(citation)]\n\n if (citation.parallelCitations?.length) {\n for (const p of citation.parallelCitations) {\n keys.push(formatKey(p.volume, p.reporter, p.page))\n }\n }\n\n return keys\n}\n","import type { Citation } from \"../types/citation\"\n\n/** Convert an integer to a Roman numeral (1-27 covers all amendments + articles). */\nfunction toRoman(n: number): string {\n const numerals: Array<[number, string]> = [\n [10, \"X\"],\n [9, \"IX\"],\n [5, \"V\"],\n [4, \"IV\"],\n [1, \"I\"],\n ]\n let result = \"\"\n let remaining = n\n for (const [value, numeral] of numerals) {\n while (remaining >= value) {\n result += numeral\n remaining -= value\n }\n }\n return result\n}\n\n/**\n * Reconstruct a canonical Bluebook-style citation string from structured fields.\n *\n * Works across all 11 citation types via the discriminated union.\n * Best-effort: uses whatever fields are available on the citation object.\n *\n * @example\n * ```typescript\n * toBluebook(caseCitation) // \"Bell Atl. Corp. v. Twombly, 550 U.S. 544 (2007)\"\n * toBluebook(statuteCite) // \"42 U.S.C. § 1983\"\n * toBluebook(idCite) // \"Id. at 570\"\n * ```\n */\nexport function toBluebook(citation: Citation): string {\n switch (citation.type) {\n case \"case\": {\n const reporter = citation.normalizedReporter ?? citation.reporter\n let pageStr: string\n if (citation.hasBlankPage) {\n pageStr = \" ___\"\n } else if (citation.page !== undefined) {\n pageStr = ` ${citation.page}`\n } else {\n pageStr = \"\"\n }\n\n const core = `${citation.volume} ${reporter}${pageStr}`\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n const caseName = citation.caseName ? `${citation.caseName}, ` : \"\"\n\n return `${caseName}${core}${pincite}${year}`\n }\n\n case \"statute\": {\n const title = citation.title !== undefined ? `${citation.title} ` : \"\"\n const section = `\\u00A7 ${citation.section}`\n const subsection = citation.subsection ?? \"\"\n const etSeq = citation.hasEtSeq ? \" et seq.\" : \"\"\n return `${title}${citation.code} ${section}${subsection}${etSeq}`\n }\n\n case \"constitutional\": {\n const jurisdiction = citation.jurisdiction === \"US\" ? \"U.S.\" : (citation.jurisdiction ?? \"\")\n const prefix = `${jurisdiction} Const.`\n\n let body = \"\"\n if (citation.article !== undefined) {\n body += ` art. ${toRoman(citation.article)}`\n }\n if (citation.amendment !== undefined) {\n body += ` amend. ${toRoman(citation.amendment)}`\n }\n if (citation.section !== undefined) {\n body += `, \\u00A7 ${citation.section}`\n }\n if (citation.clause !== undefined) {\n body += `, cl. ${citation.clause}`\n }\n return `${prefix}${body}`\n }\n\n case \"journal\": {\n const vol = citation.volume !== undefined ? `${citation.volume} ` : \"\"\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${vol}${citation.abbreviation}${page}${pincite}${year}`\n }\n\n case \"neutral\":\n return `${citation.year} ${citation.court} ${citation.documentNumber}`\n\n case \"publicLaw\":\n return `Pub. L. No. ${citation.congress}-${citation.lawNumber}`\n\n case \"federalRegister\": {\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${citation.volume} Fed. Reg. ${citation.page}${year}`\n }\n\n case \"statutesAtLarge\":\n return `${citation.volume} Stat. ${citation.page}`\n\n case \"id\":\n return citation.pincite !== undefined ? `Id. at ${citation.pincite}` : \"Id.\"\n\n case \"supra\":\n return citation.pincite !== undefined\n ? `${citation.partyName}, supra, at ${citation.pincite}`\n : `${citation.partyName}, supra`\n\n case \"shortFormCase\": {\n const reporter = citation.reporter\n if (citation.pincite !== undefined) {\n return `${citation.volume} ${reporter} at ${citation.pincite}`\n }\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n return `${citation.volume} ${reporter}${page}`\n }\n }\n}\n","import type { Citation, FullCaseCitation } from \"../types/citation\"\nimport type { ResolvedCitation, ResolutionResult } from \"../resolve/types\"\nimport type { CaseGroup } from \"./types\"\nimport { toReporterKeys } from \"./reporterKey\"\n\n/**\n * Build a lookup key for a full case citation: \"volume-reporter-page\".\n * Used to group duplicate full citations that lack a parallel groupId.\n */\nfunction citeKey(c: FullCaseCitation): string {\n return `${c.volume}-${c.reporter}-${c.page ?? \"blank\"}`\n}\n\n/** Type guard: narrow a Citation to FullCaseCitation after checking type === \"case\". */\nfunction isFullCase(cite: Citation): cite is FullCaseCitation {\n return cite.type === \"case\"\n}\n\n/** Extract resolution from a resolved short-form citation. */\nfunction getResolution(cite: ResolvedCitation): ResolutionResult | undefined {\n return (cite as ResolvedCitation & { resolution?: ResolutionResult }).resolution\n}\n\n/**\n * Group resolved citations by underlying case.\n *\n * Composes parallel linking, resolution, and volume/reporter/page identity\n * into `CaseGroup` objects. Non-case citations are ignored. Unresolved\n * short-form citations are excluded.\n *\n * @example\n * ```typescript\n * const citations = extractCitations(text)\n * const resolved = resolveCitations(citations, text)\n * const groups = groupByCase(resolved)\n * ```\n */\nexport function groupByCase(citations: ResolvedCitation[]): CaseGroup[] {\n // Map from citation index -> group index (for short-form resolution lookup)\n const indexToGroup = new Map<number, number>()\n // Map from groupId or citeKey -> group index (for dedup)\n const keyToGroup = new Map<string, number>()\n const groups: CaseGroup[] = []\n\n // First pass: assign full case citations to groups\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (!isFullCase(cite)) continue\n\n // Check if this citation belongs to an existing group\n const gid = cite.groupId\n const key = citeKey(cite)\n const existingIdx = (gid ? keyToGroup.get(gid) : undefined) ?? keyToGroup.get(key)\n\n if (existingIdx !== undefined) {\n // Add to existing group\n groups[existingIdx].mentions.push(cite)\n indexToGroup.set(i, existingIdx)\n } else {\n // Create new group\n const groupIdx = groups.length\n const group: CaseGroup = {\n primaryCitation: cite,\n mentions: [cite],\n parallelCitations: toReporterKeys(cite),\n }\n groups.push(group)\n indexToGroup.set(i, groupIdx)\n keyToGroup.set(key, groupIdx)\n if (gid) {\n keyToGroup.set(gid, groupIdx)\n }\n }\n }\n\n // Second pass: assign short-form citations to their resolved group\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (cite.type !== \"id\" && cite.type !== \"supra\" && cite.type !== \"shortFormCase\") continue\n\n const resolution = getResolution(cite)\n if (resolution?.resolvedTo === undefined) continue\n\n const groupIdx = indexToGroup.get(resolution.resolvedTo)\n if (groupIdx === undefined) continue\n\n groups[groupIdx].mentions.push(cite)\n indexToGroup.set(i, groupIdx)\n }\n\n // Sort mentions within each group by document position\n for (const group of groups) {\n group.mentions.sort((a, b) => a.span.originalStart - b.span.originalStart)\n }\n\n return groups\n}\n","import type { ContextOptions, SurroundingContext } from \"./types\"\n\n/**\n * Legal abbreviations that contain periods but are NOT sentence boundaries.\n * Kept as a static set in this file — does NOT import from src/data/\n * to preserve tree-shaking of the utils entry point.\n */\nconst LEGAL_ABBREVIATIONS = new Set([\n // Court and case abbreviations\n \"v\",\n \"vs\",\n // Reporter abbreviations (common ones)\n \"U.S\",\n \"S.Ct\",\n \"S. Ct\",\n \"L.Ed\",\n \"L. Ed\",\n \"F\",\n \"F.2d\",\n \"F.3d\",\n \"F.4th\",\n \"F.Supp\",\n \"F. Supp\",\n \"A.2d\",\n \"A.3d\",\n \"N.E\",\n \"N.E.2d\",\n \"N.W\",\n \"N.W.2d\",\n \"S.E\",\n \"S.E.2d\",\n \"S.W\",\n \"S.W.2d\",\n \"S.W.3d\",\n \"So\",\n \"So.2d\",\n \"So.3d\",\n \"P\",\n \"P.2d\",\n \"P.3d\",\n // Titles and procedural terms\n \"No\",\n \"Nos\",\n \"Inc\",\n \"Corp\",\n \"Ltd\",\n \"Co\",\n \"Ass'n\",\n \"Dept\",\n \"Dist\",\n \"Cir\",\n \"App\",\n \"Supp\",\n \"Rev\",\n \"Stat\",\n \"Const\",\n // General legal abbreviations\n \"Mr\",\n \"Mrs\",\n \"Ms\",\n \"Dr\",\n \"Jr\",\n \"Sr\",\n \"St\",\n \"Ct\",\n \"Atl\",\n \"Cal\",\n \"Fla\",\n \"Ill\",\n \"Tex\",\n \"Pa\",\n \"Md\",\n \"Va\",\n \"Wis\",\n \"Minn\",\n \"Mich\",\n \"Mass\",\n \"Conn\",\n \"Colo\",\n \"Ariz\",\n \"Ark\",\n \"Ga\",\n \"La\",\n \"Ind\",\n \"Kan\",\n \"Ky\",\n \"Miss\",\n \"Mo\",\n \"Neb\",\n \"Nev\",\n \"Okla\",\n \"Or\",\n \"Tenn\",\n \"Vt\",\n \"Wash\",\n \"Wyo\",\n \"Del\",\n \"Haw\",\n \"Ida\",\n \"Me\",\n \"Mont\",\n \"R.I\",\n \"S.C\",\n \"S.D\",\n \"N.C\",\n \"N.D\",\n \"N.J\",\n \"N.M\",\n \"N.Y\",\n \"W.Va\",\n // Federal abbreviations\n \"U.S.C\",\n \"C.F.R\",\n \"Fed\",\n \"Reg\",\n \"Pub\",\n \"Amend\",\n \"Sec\",\n \"Art\",\n \"Cl\",\n \"Ch\",\n \"Pt\",\n \"Vol\",\n \"Ed\",\n \"Harv\",\n \"Yale\",\n \"Stan\",\n \"Colum\",\n \"Geo\",\n])\n\n/**\n * Check if a period at the given position is likely an abbreviation,\n * not a sentence boundary.\n */\nfunction isAbbreviationPeriod(text: string, dotIndex: number): boolean {\n // Look backwards from the dot to find the word\n let wordStart = dotIndex\n while (wordStart > 0 && text[wordStart - 1] !== \" \" && text[wordStart - 1] !== \"\\n\") {\n wordStart--\n }\n\n const word = text.slice(wordStart, dotIndex)\n\n // Single letter followed by period (e.g., \"U.\", \"S.\", \"F.\")\n // Only treat as abbreviation if part of a dotted sequence:\n // - preceded by a period (like the \"S\" in \"U.S.\")\n // - followed by a letter/digit (like the \"F\" in \"F.2d\")\n if (word.length === 1 && /[A-Z]/.test(word)) {\n const charAfterDot = dotIndex + 1 < text.length ? text[dotIndex + 1] : \"\"\n const charBeforeWord = wordStart > 0 ? text[wordStart - 1] : \"\"\n if (charBeforeWord === \".\" || /[A-Za-z0-9]/.test(charAfterDot)) return true\n }\n\n // Check multi-character abbreviations (strip any trailing dots for lookup)\n const stripped = word.replace(/\\.$/g, \"\")\n if (LEGAL_ABBREVIATIONS.has(stripped)) return true\n\n // Check if the word itself (with internal dots) is known: \"U.S\", \"F.2d\", etc.\n if (LEGAL_ABBREVIATIONS.has(word)) return true\n\n // Number followed by period (ordinals like \"1.\" in list context — not sentence end if no space+uppercase follows)\n // This is handled by the caller's space+uppercase check\n\n return false\n}\n\n/**\n * Find the start of the sentence containing the given position.\n */\nfunction findSentenceStart(text: string, pos: number): number {\n for (let i = pos - 1; i >= 0; i--) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n\n // Check if followed by whitespace (the char after this terminator)\n const next = i + 1\n if (next < text.length && /\\s/.test(text[next])) {\n // Skip whitespace to find the start of the next sentence\n let start = next\n while (start < pos && /\\s/.test(text[start])) start++\n return start\n }\n }\n }\n return 0\n}\n\n/**\n * Find the end of the sentence containing the given position.\n */\nfunction findSentenceEnd(text: string, pos: number): number {\n for (let i = pos; i < text.length; i++) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n return i + 1\n }\n }\n return text.length\n}\n\n/**\n * Find the enclosing sentence or paragraph around a citation span.\n *\n * Legal-text-aware: periods in reporter abbreviations, court names,\n * and procedural terms (Corp., U.S., F.3d, No., v.) are not treated\n * as sentence boundaries.\n *\n * @example\n * ```typescript\n * const ctx = getSurroundingContext(text, { start: 33, end: 52 })\n * // ctx.text: \"In Smith v. Doe, 500 F.2d 123 (2020), the Court held X.\"\n * // ctx.span: { start: 16, end: 71 }\n * ```\n */\nexport function getSurroundingContext(\n text: string,\n span: { start: number; end: number },\n options?: ContextOptions,\n): SurroundingContext {\n const type = options?.type ?? \"sentence\"\n const maxLength = options?.maxLength\n\n let start: number\n let end: number\n\n if (type === \"paragraph\") {\n // Find paragraph boundaries (double newline)\n const beforeSpan = text.lastIndexOf(\"\\n\\n\", span.start)\n start = beforeSpan === -1 ? 0 : beforeSpan + 2\n const afterSpan = text.indexOf(\"\\n\\n\", span.end)\n end = afterSpan === -1 ? text.length : afterSpan\n } else {\n start = findSentenceStart(text, span.start)\n end = findSentenceEnd(text, span.end)\n }\n\n const raw = text.slice(start, end)\n const resultText = raw.trim()\n const trimmedStart = start + (raw.length - raw.trimStart().length)\n const trimmedEnd = end - (raw.length - raw.trimEnd().length)\n\n if (maxLength && resultText.length > maxLength) {\n const truncated = resultText.slice(0, maxLength)\n return {\n text: truncated,\n span: { start: trimmedStart, end: trimmedStart + truncated.length },\n }\n }\n\n return {\n text: resultText,\n span: { start: trimmedStart, end: trimmedEnd },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/reporterKey.ts","../../src/utils/bluebook.ts","../../src/utils/groupByCase.ts","../../src/utils/context.ts"],"sourcesContent":["import type { FullCaseCitation } from \"../types/citation\"\n\n/**\n * Format a volume-reporter-page key from citation fields.\n */\nfunction formatKey(volume: number | string, reporter: string, page: number | undefined): string {\n if (page === undefined) {\n return `${volume} ${reporter}`\n }\n return `${volume} ${reporter} ${page}`\n}\n\n/**\n * Extract the volume-reporter-page lookup key from a case citation.\n *\n * Strips case name, pincite, year, and parenthetical.\n * Uses `normalizedReporter` when available, falls back to `reporter`.\n * Omits the page for blank-page citations.\n *\n * @example\n * ```typescript\n * toReporterKey(citation) // \"550 U.S. 544\"\n * ```\n */\nexport function toReporterKey(citation: FullCaseCitation): string {\n const reporter = citation.normalizedReporter ?? citation.reporter\n const page = citation.hasBlankPage ? undefined : citation.page\n return formatKey(citation.volume, reporter, page)\n}\n\n/**\n * Extract all volume-reporter-page lookup keys from a case citation,\n * including parallel citations.\n *\n * Returns the primary key first, followed by any parallel citation keys.\n *\n * @example\n * ```typescript\n * toReporterKeys(citation) // [\"410 U.S. 113\", \"93 S. Ct. 705\"]\n * ```\n */\nexport function toReporterKeys(citation: FullCaseCitation): string[] {\n const keys = [toReporterKey(citation)]\n\n if (citation.parallelCitations?.length) {\n for (const p of citation.parallelCitations) {\n keys.push(formatKey(p.volume, p.reporter, p.page))\n }\n }\n\n return keys\n}\n","import type { Citation } from \"../types/citation\"\n\n/** Convert an integer to a Roman numeral (1-27 covers all amendments + articles). */\nfunction toRoman(n: number): string {\n const numerals: Array<[number, string]> = [\n [10, \"X\"],\n [9, \"IX\"],\n [5, \"V\"],\n [4, \"IV\"],\n [1, \"I\"],\n ]\n let result = \"\"\n let remaining = n\n for (const [value, numeral] of numerals) {\n while (remaining >= value) {\n result += numeral\n remaining -= value\n }\n }\n return result\n}\n\n/**\n * Reconstruct a canonical Bluebook-style citation string from structured fields.\n *\n * Works across all 11 citation types via the discriminated union.\n * Best-effort: uses whatever fields are available on the citation object.\n *\n * @example\n * ```typescript\n * toBluebook(caseCitation) // \"Bell Atl. Corp. v. Twombly, 550 U.S. 544 (2007)\"\n * toBluebook(statuteCite) // \"42 U.S.C. § 1983\"\n * toBluebook(idCite) // \"Id. at 570\"\n * ```\n */\nexport function toBluebook(citation: Citation): string {\n switch (citation.type) {\n case \"case\": {\n const reporter = citation.normalizedReporter ?? citation.reporter\n let pageStr: string\n if (citation.hasBlankPage) {\n pageStr = \" ___\"\n } else if (citation.page !== undefined) {\n pageStr = ` ${citation.page}`\n } else {\n pageStr = \"\"\n }\n\n const core = `${citation.volume} ${reporter}${pageStr}`\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n const caseName = citation.caseName ? `${citation.caseName}, ` : \"\"\n\n return `${caseName}${core}${pincite}${year}`\n }\n\n case \"statute\": {\n const title = citation.title !== undefined ? `${citation.title} ` : \"\"\n const section = `\\u00A7 ${citation.section}`\n const subsection = citation.subsection ?? \"\"\n const etSeq = citation.hasEtSeq ? \" et seq.\" : \"\"\n return `${title}${citation.code} ${section}${subsection}${etSeq}`\n }\n\n case \"constitutional\": {\n const jurisdiction = citation.jurisdiction === \"US\" ? \"U.S.\" : (citation.jurisdiction ?? \"\")\n const prefix = `${jurisdiction} Const.`\n\n let body = \"\"\n if (citation.article !== undefined) {\n body += ` art. ${toRoman(citation.article)}`\n }\n if (citation.amendment !== undefined) {\n body += ` amend. ${toRoman(citation.amendment)}`\n }\n if (citation.section !== undefined) {\n body += `, \\u00A7 ${citation.section}`\n }\n if (citation.clause !== undefined) {\n body += `, cl. ${citation.clause}`\n }\n return `${prefix}${body}`\n }\n\n case \"journal\": {\n const vol = citation.volume !== undefined ? `${citation.volume} ` : \"\"\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n const pincite = citation.pincite !== undefined ? `, ${citation.pincite}` : \"\"\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${vol}${citation.abbreviation}${page}${pincite}${year}`\n }\n\n case \"docket\": {\n const caseName = citation.caseName ? `${citation.caseName}, ` : \"\"\n const courtAndYear =\n citation.court && citation.year\n ? ` (${citation.court} ${citation.year})`\n : citation.court\n ? ` (${citation.court})`\n : citation.year\n ? ` (${citation.year})`\n : \"\"\n return `${caseName}No. ${citation.docketNumber}${courtAndYear}`\n }\n\n case \"neutral\":\n return `${citation.year} ${citation.court} ${citation.documentNumber}`\n\n case \"publicLaw\":\n return `Pub. L. No. ${citation.congress}-${citation.lawNumber}`\n\n case \"federalRegister\": {\n const year = citation.year !== undefined ? ` (${citation.year})` : \"\"\n return `${citation.volume} Fed. Reg. ${citation.page}${year}`\n }\n\n case \"statutesAtLarge\":\n return `${citation.volume} Stat. ${citation.page}`\n\n case \"id\":\n return citation.pincite !== undefined ? `Id. at ${citation.pincite}` : \"Id.\"\n\n case \"supra\":\n return citation.pincite !== undefined\n ? `${citation.partyName}, supra, at ${citation.pincite}`\n : `${citation.partyName}, supra`\n\n case \"shortFormCase\": {\n const reporter = citation.reporter\n if (citation.pincite !== undefined) {\n return `${citation.volume} ${reporter} at ${citation.pincite}`\n }\n const page = citation.page !== undefined ? ` ${citation.page}` : \"\"\n return `${citation.volume} ${reporter}${page}`\n }\n }\n}\n","import type { Citation, FullCaseCitation } from \"../types/citation\"\nimport type { ResolvedCitation, ResolutionResult } from \"../resolve/types\"\nimport type { CaseGroup } from \"./types\"\nimport { toReporterKeys } from \"./reporterKey\"\n\n/**\n * Build a lookup key for a full case citation: \"volume-reporter-page\".\n * Used to group duplicate full citations that lack a parallel groupId.\n */\nfunction citeKey(c: FullCaseCitation): string {\n return `${c.volume}-${c.reporter}-${c.page ?? \"blank\"}`\n}\n\n/** Type guard: narrow a Citation to FullCaseCitation after checking type === \"case\". */\nfunction isFullCase(cite: Citation): cite is FullCaseCitation {\n return cite.type === \"case\"\n}\n\n/** Extract resolution from a resolved short-form citation. */\nfunction getResolution(cite: ResolvedCitation): ResolutionResult | undefined {\n return (cite as ResolvedCitation & { resolution?: ResolutionResult }).resolution\n}\n\n/**\n * Group resolved citations by underlying case.\n *\n * Composes parallel linking, resolution, and volume/reporter/page identity\n * into `CaseGroup` objects. Non-case citations are ignored. Unresolved\n * short-form citations are excluded.\n *\n * @example\n * ```typescript\n * const citations = extractCitations(text)\n * const resolved = resolveCitations(citations, text)\n * const groups = groupByCase(resolved)\n * ```\n */\nexport function groupByCase(citations: ResolvedCitation[]): CaseGroup[] {\n // Map from citation index -> group index (for short-form resolution lookup)\n const indexToGroup = new Map<number, number>()\n // Map from groupId or citeKey -> group index (for dedup)\n const keyToGroup = new Map<string, number>()\n const groups: CaseGroup[] = []\n\n // First pass: assign full case citations to groups\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (!isFullCase(cite)) continue\n\n // Check if this citation belongs to an existing group\n const gid = cite.groupId\n const key = citeKey(cite)\n const existingIdx = (gid ? keyToGroup.get(gid) : undefined) ?? keyToGroup.get(key)\n\n if (existingIdx !== undefined) {\n // Add to existing group\n groups[existingIdx].mentions.push(cite)\n indexToGroup.set(i, existingIdx)\n } else {\n // Create new group\n const groupIdx = groups.length\n const group: CaseGroup = {\n primaryCitation: cite,\n mentions: [cite],\n parallelCitations: toReporterKeys(cite),\n }\n groups.push(group)\n indexToGroup.set(i, groupIdx)\n keyToGroup.set(key, groupIdx)\n if (gid) {\n keyToGroup.set(gid, groupIdx)\n }\n }\n }\n\n // Second pass: assign short-form citations to their resolved group\n for (let i = 0; i < citations.length; i++) {\n const cite = citations[i]\n if (cite.type !== \"id\" && cite.type !== \"supra\" && cite.type !== \"shortFormCase\") continue\n\n const resolution = getResolution(cite)\n if (resolution?.resolvedTo === undefined) continue\n\n const groupIdx = indexToGroup.get(resolution.resolvedTo)\n if (groupIdx === undefined) continue\n\n groups[groupIdx].mentions.push(cite)\n indexToGroup.set(i, groupIdx)\n }\n\n // Sort mentions within each group by document position\n for (const group of groups) {\n group.mentions.sort((a, b) => a.span.originalStart - b.span.originalStart)\n }\n\n return groups\n}\n","import type { ContextOptions, SurroundingContext } from \"./types\"\n\n/**\n * Legal abbreviations that contain periods but are NOT sentence boundaries.\n * Kept as a static set in this file — does NOT import from src/data/\n * to preserve tree-shaking of the utils entry point.\n */\nconst LEGAL_ABBREVIATIONS = new Set([\n // Court and case abbreviations\n \"v\",\n \"vs\",\n // Reporter abbreviations (common ones)\n \"U.S\",\n \"S.Ct\",\n \"S. Ct\",\n \"L.Ed\",\n \"L. Ed\",\n \"F\",\n \"F.2d\",\n \"F.3d\",\n \"F.4th\",\n \"F.Supp\",\n \"F. Supp\",\n \"A.2d\",\n \"A.3d\",\n \"N.E\",\n \"N.E.2d\",\n \"N.W\",\n \"N.W.2d\",\n \"S.E\",\n \"S.E.2d\",\n \"S.W\",\n \"S.W.2d\",\n \"S.W.3d\",\n \"So\",\n \"So.2d\",\n \"So.3d\",\n \"P\",\n \"P.2d\",\n \"P.3d\",\n // Titles and procedural terms\n \"No\",\n \"Nos\",\n \"Inc\",\n \"Corp\",\n \"Ltd\",\n \"Co\",\n \"Ass'n\",\n \"Dept\",\n \"Dist\",\n \"Cir\",\n \"App\",\n \"Supp\",\n \"Rev\",\n \"Stat\",\n \"Const\",\n // General legal abbreviations\n \"Mr\",\n \"Mrs\",\n \"Ms\",\n \"Dr\",\n \"Jr\",\n \"Sr\",\n \"St\",\n \"Ct\",\n \"Atl\",\n \"Cal\",\n \"Fla\",\n \"Ill\",\n \"Tex\",\n \"Pa\",\n \"Md\",\n \"Va\",\n \"Wis\",\n \"Minn\",\n \"Mich\",\n \"Mass\",\n \"Conn\",\n \"Colo\",\n \"Ariz\",\n \"Ark\",\n \"Ga\",\n \"La\",\n \"Ind\",\n \"Kan\",\n \"Ky\",\n \"Miss\",\n \"Mo\",\n \"Neb\",\n \"Nev\",\n \"Okla\",\n \"Or\",\n \"Tenn\",\n \"Vt\",\n \"Wash\",\n \"Wyo\",\n \"Del\",\n \"Haw\",\n \"Ida\",\n \"Me\",\n \"Mont\",\n \"R.I\",\n \"S.C\",\n \"S.D\",\n \"N.C\",\n \"N.D\",\n \"N.J\",\n \"N.M\",\n \"N.Y\",\n \"W.Va\",\n // Federal abbreviations\n \"U.S.C\",\n \"C.F.R\",\n \"Fed\",\n \"Reg\",\n \"Pub\",\n \"Amend\",\n \"Sec\",\n \"Art\",\n \"Cl\",\n \"Ch\",\n \"Pt\",\n \"Vol\",\n \"Ed\",\n \"Harv\",\n \"Yale\",\n \"Stan\",\n \"Colum\",\n \"Geo\",\n])\n\n/**\n * Check if a period at the given position is likely an abbreviation,\n * not a sentence boundary.\n */\nfunction isAbbreviationPeriod(text: string, dotIndex: number): boolean {\n // Look backwards from the dot to find the word\n let wordStart = dotIndex\n while (wordStart > 0 && text[wordStart - 1] !== \" \" && text[wordStart - 1] !== \"\\n\") {\n wordStart--\n }\n\n const word = text.slice(wordStart, dotIndex)\n\n // Single letter followed by period (e.g., \"U.\", \"S.\", \"F.\")\n // Only treat as abbreviation if part of a dotted sequence:\n // - preceded by a period (like the \"S\" in \"U.S.\")\n // - followed by a letter/digit (like the \"F\" in \"F.2d\")\n if (word.length === 1 && /[A-Z]/.test(word)) {\n const charAfterDot = dotIndex + 1 < text.length ? text[dotIndex + 1] : \"\"\n const charBeforeWord = wordStart > 0 ? text[wordStart - 1] : \"\"\n if (charBeforeWord === \".\" || /[A-Za-z0-9]/.test(charAfterDot)) return true\n }\n\n // Check multi-character abbreviations (strip any trailing dots for lookup)\n const stripped = word.replace(/\\.$/g, \"\")\n if (LEGAL_ABBREVIATIONS.has(stripped)) return true\n\n // Check if the word itself (with internal dots) is known: \"U.S\", \"F.2d\", etc.\n if (LEGAL_ABBREVIATIONS.has(word)) return true\n\n // Number followed by period (ordinals like \"1.\" in list context — not sentence end if no space+uppercase follows)\n // This is handled by the caller's space+uppercase check\n\n return false\n}\n\n/**\n * Find the start of the sentence containing the given position.\n */\nfunction findSentenceStart(text: string, pos: number): number {\n for (let i = pos - 1; i >= 0; i--) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n\n // Check if followed by whitespace (the char after this terminator)\n const next = i + 1\n if (next < text.length && /\\s/.test(text[next])) {\n // Skip whitespace to find the start of the next sentence\n let start = next\n while (start < pos && /\\s/.test(text[start])) start++\n return start\n }\n }\n }\n return 0\n}\n\n/**\n * Find the end of the sentence containing the given position.\n */\nfunction findSentenceEnd(text: string, pos: number): number {\n for (let i = pos; i < text.length; i++) {\n const ch = text[i]\n if (ch === \".\" || ch === \"?\" || ch === \"!\") {\n if (ch === \".\" && isAbbreviationPeriod(text, i)) continue\n return i + 1\n }\n }\n return text.length\n}\n\n/**\n * Find the enclosing sentence or paragraph around a citation span.\n *\n * Legal-text-aware: periods in reporter abbreviations, court names,\n * and procedural terms (Corp., U.S., F.3d, No., v.) are not treated\n * as sentence boundaries.\n *\n * @example\n * ```typescript\n * const ctx = getSurroundingContext(text, { start: 33, end: 52 })\n * // ctx.text: \"In Smith v. Doe, 500 F.2d 123 (2020), the Court held X.\"\n * // ctx.span: { start: 16, end: 71 }\n * ```\n */\nexport function getSurroundingContext(\n text: string,\n span: { start: number; end: number },\n options?: ContextOptions,\n): SurroundingContext {\n const type = options?.type ?? \"sentence\"\n const maxLength = options?.maxLength\n\n let start: number\n let end: number\n\n if (type === \"paragraph\") {\n // Find paragraph boundaries (double newline)\n const beforeSpan = text.lastIndexOf(\"\\n\\n\", span.start)\n start = beforeSpan === -1 ? 0 : beforeSpan + 2\n const afterSpan = text.indexOf(\"\\n\\n\", span.end)\n end = afterSpan === -1 ? text.length : afterSpan\n } else {\n start = findSentenceStart(text, span.start)\n end = findSentenceEnd(text, span.end)\n }\n\n const raw = text.slice(start, end)\n const resultText = raw.trim()\n const trimmedStart = start + (raw.length - raw.trimStart().length)\n const trimmedEnd = end - (raw.length - raw.trimEnd().length)\n\n if (maxLength && resultText.length > maxLength) {\n const truncated = resultText.slice(0, maxLength)\n return {\n text: truncated,\n span: { start: trimmedStart, end: trimmedStart + truncated.length },\n }\n }\n\n return {\n text: resultText,\n span: { start: trimmedStart, end: trimmedEnd },\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyecite-ts",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "TypeScript port of eyecite - extract, resolve, and annotate legal citations",
5
5
  "type": "module",
6
6
  "exports": {