eyecite-ts 0.4.0 → 0.6.0

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.
Files changed (51) hide show
  1. package/README.md +80 -10
  2. package/dist/annotate/index.cjs.map +1 -1
  3. package/dist/annotate/index.d.cts +1 -1
  4. package/dist/annotate/index.d.mts +1 -1
  5. package/dist/annotate/index.mjs.map +1 -1
  6. package/dist/{citation-4bmWbhSK.d.cts → citation-BwXdJTA9.d.mts} +190 -12
  7. package/dist/citation-BwXdJTA9.d.mts.map +1 -0
  8. package/dist/{citation-BVN0o8TJ.d.mts → citation-By8QXtGC.d.cts} +190 -12
  9. package/dist/citation-By8QXtGC.d.cts.map +1 -0
  10. package/dist/data/index.cjs +1 -1
  11. package/dist/data/index.cjs.map +1 -1
  12. package/dist/data/index.d.cts +91 -1
  13. package/dist/data/index.d.cts.map +1 -1
  14. package/dist/data/index.d.mts +91 -1
  15. package/dist/data/index.d.mts.map +1 -1
  16. package/dist/data/index.mjs +1 -1
  17. package/dist/data/index.mjs.map +1 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +94 -144
  21. package/dist/index.d.cts.map +1 -1
  22. package/dist/index.d.mts +94 -144
  23. package/dist/index.d.mts.map +1 -1
  24. package/dist/index.mjs +1 -1
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/knownCodes-CI-vnoBO.cjs +2 -0
  27. package/dist/knownCodes-CI-vnoBO.cjs.map +1 -0
  28. package/dist/knownCodes-MkDSiR1j.mjs +2 -0
  29. package/dist/knownCodes-MkDSiR1j.mjs.map +1 -0
  30. package/dist/{reporters-DYNnh4O0.mjs → reporters-CZoC98-L.mjs} +1 -1
  31. package/dist/reporters-CZoC98-L.mjs.map +1 -0
  32. package/dist/reporters-Wob0oyD9.cjs +2 -0
  33. package/dist/reporters-Wob0oyD9.cjs.map +1 -0
  34. package/dist/types-BfYnmSHC.d.mts +115 -0
  35. package/dist/types-BfYnmSHC.d.mts.map +1 -0
  36. package/dist/types-C_5aOCZ1.d.cts +115 -0
  37. package/dist/types-C_5aOCZ1.d.cts.map +1 -0
  38. package/dist/utils/index.cjs +7 -0
  39. package/dist/utils/index.cjs.map +1 -0
  40. package/dist/utils/index.d.cts +122 -0
  41. package/dist/utils/index.d.cts.map +1 -0
  42. package/dist/utils/index.d.mts +122 -0
  43. package/dist/utils/index.d.mts.map +1 -0
  44. package/dist/utils/index.mjs +7 -0
  45. package/dist/utils/index.mjs.map +1 -0
  46. package/package.json +10 -1
  47. package/dist/citation-4bmWbhSK.d.cts.map +0 -1
  48. package/dist/citation-BVN0o8TJ.d.mts.map +0 -1
  49. package/dist/reporters-BclWimmk.cjs +0 -2
  50. package/dist/reporters-BclWimmk.cjs.map +0 -1
  51. package/dist/reporters-DYNnh4O0.mjs.map +0 -1
package/README.md CHANGED
@@ -15,7 +15,7 @@ Extract, resolve, and annotate legal citations from court opinions and legal doc
15
15
 
16
16
  ## Features
17
17
 
18
- - **Full citation extraction**: Case citations, statutes, journal articles, neutral citations, public laws, federal register
18
+ - **Full citation extraction**: Case citations, statutes (20 jurisdictions), constitutional citations (U.S. + 50 states), journal articles, neutral citations, public laws, federal register
19
19
  - **Case name & full span**: Backward search extracts case names ("Smith v. Jones", "In re Smith"), `fullSpan` covers case name through closing parenthetical
20
20
  - **Parallel citation linking**: Automatic detection and grouping of comma-separated citations sharing a parenthetical (e.g., "410 U.S. 113, 93 S. Ct. 705 (1973)")
21
21
  - **Complex parentheticals**: Unified parser handles court+year, full dates (Jan. 15, 2020 / January 15, 2020 / 1/15/2020), disposition (en banc, per curiam), and chained parentheticals
@@ -24,7 +24,7 @@ Extract, resolve, and annotate legal citations from court opinions and legal doc
24
24
  - **Citation annotation**: HTML markup with auto-escape XSS protection and position tracking
25
25
  - **Bundle optimization**: Tree-shakeable exports, lazy-loaded reporter data, separate entry points
26
26
  - **TypeScript native**: Discriminated unions, conditional types, type guards, full IntelliSense
27
- - **Zero dependencies**: No runtime dependencies, 7KB gzipped core bundle
27
+ - **Zero dependencies**: No runtime dependencies, ~10KB gzipped core bundle
28
28
 
29
29
  ## Installation
30
30
 
@@ -75,6 +75,75 @@ citations.forEach(citation => {
75
75
  })
76
76
  ```
77
77
 
78
+ ### Statute Citations
79
+
80
+ Extract citations from 20 state and federal jurisdictions with subsection, et seq., and jurisdiction identification:
81
+
82
+ ```typescript
83
+ import { extractCitations } from 'eyecite-ts'
84
+
85
+ const text = `
86
+ See 42 U.S.C. § 1983(a)(1) et seq.
87
+ Also Cal. Penal Code § 187.
88
+ And N.Y. Penal Law § 125.25(1)(a).
89
+ Compare 735 ILCS 5/2-1001.
90
+ `
91
+ const citations = extractCitations(text)
92
+
93
+ // Federal with subsections + et seq.
94
+ // { type: 'statute', title: 42, code: 'U.S.C.', section: '1983',
95
+ // subsection: '(a)(1)', jurisdiction: 'US', hasEtSeq: true, confidence: 1.0 }
96
+
97
+ // California named-code
98
+ // { type: 'statute', code: 'Penal', section: '187', jurisdiction: 'CA', confidence: 0.95 }
99
+
100
+ // New York named-code with subsections
101
+ // { type: 'statute', code: 'Penal Law', section: '125.25',
102
+ // subsection: '(1)(a)', jurisdiction: 'NY', confidence: 1.0 }
103
+
104
+ // Illinois chapter-act format
105
+ // { type: 'statute', title: 735, code: '5', section: '2-1001',
106
+ // jurisdiction: 'IL', confidence: 0.95 }
107
+ ```
108
+
109
+ **Supported jurisdictions:**
110
+
111
+ | Family | Jurisdictions |
112
+ |--------|--------------|
113
+ | Federal | USC, CFR, prose ("section X of title Y") |
114
+ | Named-code | NY (21 laws), CA (29 codes), TX (29 codes), MD (36 articles), VA, AL, MA |
115
+ | Abbreviated-code | FL, OH, MI, UT, CO, WA, NC, GA, PA, IN, NJ, DE |
116
+ | Chapter-act | IL (ILCS) |
117
+
118
+ ### Constitutional Citations
119
+
120
+ Extract U.S. and state constitutional citations with article, amendment, section, and clause parsing:
121
+
122
+ ```typescript
123
+ import { extractCitations } from 'eyecite-ts'
124
+
125
+ const text = `
126
+ Under U.S. Const. amend. XIV, § 1, equal protection is guaranteed.
127
+ See also Cal. Const. art. I, § 7.
128
+ And U.S. Const. art. I, § 8, cl. 3.
129
+ `
130
+ const citations = extractCitations(text)
131
+
132
+ // U.S. amendment with section
133
+ // { type: 'constitutional', jurisdiction: 'US', amendment: 14,
134
+ // section: '1', confidence: 0.95 }
135
+
136
+ // California article with section
137
+ // { type: 'constitutional', jurisdiction: 'CA', article: 1,
138
+ // section: '7', confidence: 0.9 }
139
+
140
+ // Commerce Clause (article + section + clause)
141
+ // { type: 'constitutional', jurisdiction: 'US', article: 1,
142
+ // section: '8', clause: 3, confidence: 0.95 }
143
+ ```
144
+
145
+ Roman numerals (I–XXVII) are automatically parsed to integers. All 50 state abbreviations are supported.
146
+
78
147
  ### Async API
79
148
 
80
149
  ```typescript
@@ -370,9 +439,10 @@ All citation types use a discriminated union on the `type` field:
370
439
 
371
440
  ```typescript
372
441
  import type {
373
- Citation, // Union of all 9 types
442
+ Citation, // Union of all 11 types
374
443
  FullCaseCitation,
375
444
  StatuteCitation,
445
+ ConstitutionalCitation,
376
446
  JournalCitation,
377
447
  NeutralCitation,
378
448
  PublicLawCitation,
@@ -380,10 +450,10 @@ import type {
380
450
  IdCitation,
381
451
  SupraCitation,
382
452
  ShortFormCaseCitation,
383
- CitationOfType, // Extract subtype: CitationOfType<'case'> = FullCaseCitation
384
- ExtractorMap, // Maps FullCitationType keys to citation subtypes
385
- FullCitation, // Union of full citation types
386
- ShortFormCitation, // Union of short-form types
453
+ CitationOfType, // Extract subtype: CitationOfType<'case'> = FullCaseCitation
454
+ ExtractorMap, // Maps FullCitationType keys to citation subtypes
455
+ FullCitation, // Union of full citation types
456
+ ShortFormCitation, // Union of short-form types
387
457
  } from 'eyecite-ts'
388
458
  ```
389
459
 
@@ -412,7 +482,7 @@ if (isCitationType(citation, 'statute')) {
412
482
  switch (citation.type) {
413
483
  case 'case': /* ... */ break
414
484
  case 'statute': /* ... */ break
415
- // ... all 9 types ...
485
+ // ... all 11 types ...
416
486
  default: assertUnreachable(citation.type)
417
487
  }
418
488
  ```
@@ -434,7 +504,7 @@ Three entry points for optimal tree-shaking:
434
504
 
435
505
  | Entry Point | Import | Gzipped |
436
506
  |------------|--------|---------|
437
- | Core extraction | `eyecite-ts` | 7.0 KB |
507
+ | Core extraction | `eyecite-ts` | ~10 KB |
438
508
  | Annotation | `eyecite-ts/annotate` | 0.7 KB |
439
509
  | Reporter data | `eyecite-ts/data` | 86.5 KB (lazy-loaded) |
440
510
 
@@ -469,7 +539,7 @@ pnpm lint # Lint with Biome
469
539
  pnpm format # Format with Biome
470
540
  ```
471
541
 
472
- 527 tests across 22 test files.
542
+ 1030+ tests across 34 test files.
473
543
 
474
544
  ## License
475
545
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../src/annotate/annotate.ts"],"sourcesContent":["import type { Citation } from '../types/citation'\nimport type { AnnotationOptions, AnnotationResult } from './types'\n\n/**\n * Annotate citations in text with custom markup.\n *\n * Supports two modes:\n * - **Template mode**: Simple before/after wrapping (set `options.template`)\n * - **Callback mode**: Custom logic with full citation context (set `options.callback`)\n *\n * Citations are processed in reverse order to avoid position shifts invalidating\n * subsequent annotations. Position tracking maps original positions to new positions\n * after markup insertion.\n *\n * @param text - Original or cleaned text to annotate\n * @param citations - Citations to mark up (from extraction pipeline)\n * @param options - Annotation configuration\n * @returns Annotated text with position mapping\n *\n * @example Template mode\n * ```typescript\n * const result = annotate(text, citations, {\n * template: { before: '<cite>', after: '</cite>' }\n * })\n * // Result: \"See <cite>500 F.2d 123</cite>\"\n * ```\n *\n * @example Callback mode\n * ```typescript\n * const result = annotate(text, citations, {\n * callback: (citation) => {\n * if (citation.type === 'case') {\n * return `<a href=\"/cases/${citation.volume}\">${citation.matchedText}</a>`\n * }\n * return citation.matchedText\n * }\n * })\n * ```\n *\n * @example Position tracking\n * ```typescript\n * const result = annotate(text, citations, { template: { before: '<mark>', after: '</mark>' } })\n * // result.positionMap tracks how positions shifted\n * const originalPos = 10\n * const newPos = result.positionMap.get(originalPos)\n * ```\n */\nexport function annotate<C extends Citation = Citation>(\n text: string,\n citations: C[],\n options: AnnotationOptions<C> = {}\n): AnnotationResult {\n const {\n useCleanText = false,\n autoEscape = true, // Secure by default\n useFullSpan = false, // Backward compatible default\n template,\n callback,\n } = options\n\n // Sort reverse to avoid position shifts invalidating subsequent annotations\n const sorted = [...citations].sort((a, b) => {\n const aPos = useCleanText ? a.span.cleanStart : a.span.originalStart\n const bPos = useCleanText ? b.span.cleanStart : b.span.originalStart\n return bPos - aPos // Reverse for backward iteration\n })\n\n let result = text\n const positionMap = new Map<number, number>()\n const skipped: Citation[] = []\n\n for (const citation of sorted) {\n // Determine which span to use\n let start: number\n let end: number\n\n if (useFullSpan && 'fullSpan' in citation && citation.fullSpan) {\n // Full span mode: case name through parenthetical\n start = useCleanText ? citation.fullSpan.cleanStart : citation.fullSpan.originalStart\n end = useCleanText ? citation.fullSpan.cleanEnd : citation.fullSpan.originalEnd\n } else {\n // Default mode: core citation only\n start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\n }\n\n // Snap positions out of HTML tags when annotating original text\n if (!useCleanText) {\n const snapped = snapOutOfHtmlTags(result, start, end)\n if (snapped === null) {\n // Could not safely snap — skip this citation\n skipped.push(citation)\n continue\n }\n start = snapped.start\n end = snapped.end\n }\n\n let markup = ''\n\n if (callback) {\n // Callback mode: developer provides full logic\n const surrounding = text.substring(\n Math.max(0, start - 30),\n Math.min(text.length, end + 30)\n )\n markup = callback(citation, surrounding)\n } else if (template) {\n // Template mode: simple before/after wrapping\n const citationText = result.substring(start, end)\n const escaped = autoEscape ? escapeHtmlEntities(citationText) : citationText\n markup = template.before + escaped + template.after\n } else {\n // No annotation specified\n continue\n }\n\n // Insert annotation (working backwards preserves positions for later citations)\n result = result.slice(0, start) + markup + result.slice(end)\n\n // Track original position to new position (before this annotation was added)\n positionMap.set(start, start)\n }\n\n return { text: result, positionMap, skipped }\n}\n\n/**\n * Check if a position falls inside an HTML tag (between `<` and `>`).\n * Returns the index of the opening `<` if inside a tag, otherwise -1.\n */\nfunction findContainingTag(text: string, pos: number): { tagStart: number; tagEnd: number } | null {\n // Search backwards from pos for '<' without encountering '>' first\n let i = pos - 1\n while (i >= 0) {\n if (text[i] === '>') return null // Hit a tag close — we're outside\n if (text[i] === '<') {\n // Found opening '<' — now find the closing '>'\n let j = pos\n while (j < text.length) {\n if (text[j] === '>') return { tagStart: i, tagEnd: j + 1 }\n j++\n }\n // Unclosed tag — treat as inside\n return { tagStart: i, tagEnd: text.length }\n }\n i--\n }\n return null\n}\n\n/**\n * Snap annotation start/end positions to avoid landing inside HTML tags.\n *\n * If a position falls inside an HTML tag, it is moved:\n * - Start position: snapped to before the tag's `<`\n * - End position: snapped to after the tag's `>`\n *\n * Returns null if the positions can't be safely adjusted (e.g., entirely\n * within a single tag).\n */\nfunction snapOutOfHtmlTags(\n text: string,\n start: number,\n end: number,\n): { start: number; end: number } | null {\n let snappedStart = start\n let snappedEnd = end\n\n const startTag = findContainingTag(text, start)\n if (startTag) {\n snappedStart = startTag.tagStart\n }\n\n const endTag = findContainingTag(text, end)\n if (endTag) {\n snappedEnd = endTag.tagEnd\n }\n\n // Sanity check: start must come before end\n if (snappedStart >= snappedEnd) return null\n\n return { start: snappedStart, end: snappedEnd }\n}\n\n/**\n * Escape HTML entities to prevent XSS injection.\n *\n * Converts special HTML characters to their entity equivalents:\n * - `&` → `&amp;`\n * - `<` → `&lt;`\n * - `>` → `&gt;`\n * - `\"` → `&quot;`\n * - `'` → `&#39;`\n * - `/` → `&#x2F;`\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtmlEntities(text: string): string {\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n '/': '&#x2F;',\n }\n return text.replace(/[&<>\"'/]/g, (char) => map[char])\n}\n"],"mappings":"mEA+CA,SAAgB,EACd,EACA,EACA,EAAgC,EAAE,CAChB,CAClB,GAAM,CACJ,eAAe,GACf,aAAa,GACb,cAAc,GACd,WACA,YACE,EAGE,EAAS,CAAC,GAAG,EAAU,CAAC,MAAM,EAAG,IAAM,CAC3C,IAAM,EAAO,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,cAEvD,OADa,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,eACzC,GACd,CAEE,EAAS,EACP,EAAc,IAAI,IAClB,EAAsB,EAAE,CAE9B,IAAK,IAAM,KAAY,EAAQ,CAE7B,IAAI,EACA,EAaJ,GAXI,GAAe,aAAc,GAAY,EAAS,UAEpD,EAAQ,EAAe,EAAS,SAAS,WAAa,EAAS,SAAS,cACxE,EAAM,EAAe,EAAS,SAAS,SAAW,EAAS,SAAS,cAGpE,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,aAI1D,CAAC,EAAc,CACjB,IAAM,EAAU,EAAkB,EAAQ,EAAO,EAAI,CACrD,GAAI,IAAY,KAAM,CAEpB,EAAQ,KAAK,EAAS,CACtB,SAEF,EAAQ,EAAQ,MAChB,EAAM,EAAQ,IAGhB,IAAI,EAAS,GAEb,GAAI,EAMF,EAAS,EAAS,EAJE,EAAK,UACvB,KAAK,IAAI,EAAG,EAAQ,GAAG,CACvB,KAAK,IAAI,EAAK,OAAQ,EAAM,GAAG,CAChC,CACuC,SAC/B,EAAU,CAEnB,IAAM,EAAe,EAAO,UAAU,EAAO,EAAI,CAC3C,EAAU,EAAa,EAAmB,EAAa,CAAG,EAChE,EAAS,EAAS,OAAS,EAAU,EAAS,WAG9C,SAIF,EAAS,EAAO,MAAM,EAAG,EAAM,CAAG,EAAS,EAAO,MAAM,EAAI,CAG5D,EAAY,IAAI,EAAO,EAAM,CAG/B,MAAO,CAAE,KAAM,EAAQ,cAAa,UAAS,CAO/C,SAAS,EAAkB,EAAc,EAA0D,CAEjG,IAAI,EAAI,EAAM,EACd,KAAO,GAAK,GAAG,CACb,GAAI,EAAK,KAAO,IAAK,OAAO,KAC5B,GAAI,EAAK,KAAO,IAAK,CAEnB,IAAI,EAAI,EACR,KAAO,EAAI,EAAK,QAAQ,CACtB,GAAI,EAAK,KAAO,IAAK,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAI,EAAG,CAC1D,IAGF,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAK,OAAQ,CAE7C,IAEF,OAAO,KAaT,SAAS,EACP,EACA,EACA,EACuC,CACvC,IAAI,EAAe,EACf,EAAa,EAEX,EAAW,EAAkB,EAAM,EAAM,CAC3C,IACF,EAAe,EAAS,UAG1B,IAAM,EAAS,EAAkB,EAAM,EAAI,CAQ3C,OAPI,IACF,EAAa,EAAO,QAIlB,GAAgB,EAAmB,KAEhC,CAAE,MAAO,EAAc,IAAK,EAAY,CAiBjD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,YAAc,GAAS,EAAI,GAAM"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/annotate/annotate.ts"],"sourcesContent":["import type { Citation } from \"../types/citation\"\nimport type { AnnotationOptions, AnnotationResult } from \"./types\"\n\n/**\n * Annotate citations in text with custom markup.\n *\n * Supports two modes:\n * - **Template mode**: Simple before/after wrapping (set `options.template`)\n * - **Callback mode**: Custom logic with full citation context (set `options.callback`)\n *\n * Citations are processed in reverse order to avoid position shifts invalidating\n * subsequent annotations. Position tracking maps original positions to new positions\n * after markup insertion.\n *\n * @param text - Original or cleaned text to annotate\n * @param citations - Citations to mark up (from extraction pipeline)\n * @param options - Annotation configuration\n * @returns Annotated text with position mapping\n *\n * @example Template mode\n * ```typescript\n * const result = annotate(text, citations, {\n * template: { before: '<cite>', after: '</cite>' }\n * })\n * // Result: \"See <cite>500 F.2d 123</cite>\"\n * ```\n *\n * @example Callback mode\n * ```typescript\n * const result = annotate(text, citations, {\n * callback: (citation) => {\n * if (citation.type === 'case') {\n * return `<a href=\"/cases/${citation.volume}\">${citation.matchedText}</a>`\n * }\n * return citation.matchedText\n * }\n * })\n * ```\n *\n * @example Position tracking\n * ```typescript\n * const result = annotate(text, citations, { template: { before: '<mark>', after: '</mark>' } })\n * // result.positionMap tracks how positions shifted\n * const originalPos = 10\n * const newPos = result.positionMap.get(originalPos)\n * ```\n */\nexport function annotate<C extends Citation = Citation>(\n text: string,\n citations: C[],\n options: AnnotationOptions<C> = {},\n): AnnotationResult {\n const {\n useCleanText = false,\n autoEscape = true, // Secure by default\n useFullSpan = false, // Backward compatible default\n template,\n callback,\n } = options\n\n // Sort reverse to avoid position shifts invalidating subsequent annotations\n const sorted = [...citations].sort((a, b) => {\n const aPos = useCleanText ? a.span.cleanStart : a.span.originalStart\n const bPos = useCleanText ? b.span.cleanStart : b.span.originalStart\n return bPos - aPos // Reverse for backward iteration\n })\n\n let result = text\n const positionMap = new Map<number, number>()\n const skipped: Citation[] = []\n\n for (const citation of sorted) {\n // Determine which span to use\n let start: number\n let end: number\n\n if (useFullSpan && \"fullSpan\" in citation && citation.fullSpan) {\n // Full span mode: case name through parenthetical\n start = useCleanText ? citation.fullSpan.cleanStart : citation.fullSpan.originalStart\n end = useCleanText ? citation.fullSpan.cleanEnd : citation.fullSpan.originalEnd\n } else {\n // Default mode: core citation only\n start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\n }\n\n // Snap positions out of HTML tags when annotating original text\n if (!useCleanText) {\n const snapped = snapOutOfHtmlTags(result, start, end)\n if (snapped === null) {\n // Could not safely snap — skip this citation\n skipped.push(citation)\n continue\n }\n start = snapped.start\n end = snapped.end\n }\n\n let markup = \"\"\n\n if (callback) {\n // Callback mode: developer provides full logic\n const surrounding = text.substring(Math.max(0, start - 30), Math.min(text.length, end + 30))\n markup = callback(citation, surrounding)\n } else if (template) {\n // Template mode: simple before/after wrapping\n const citationText = result.substring(start, end)\n const escaped = autoEscape ? escapeHtmlEntities(citationText) : citationText\n markup = template.before + escaped + template.after\n } else {\n // No annotation specified\n continue\n }\n\n // Insert annotation (working backwards preserves positions for later citations)\n result = result.slice(0, start) + markup + result.slice(end)\n\n // Track original position to new position (before this annotation was added)\n positionMap.set(start, start)\n }\n\n return { text: result, positionMap, skipped }\n}\n\n/**\n * Check if a position falls inside an HTML tag (between `<` and `>`).\n * Returns the index of the opening `<` if inside a tag, otherwise -1.\n */\nfunction findContainingTag(text: string, pos: number): { tagStart: number; tagEnd: number } | null {\n // Search backwards from pos for '<' without encountering '>' first\n let i = pos - 1\n while (i >= 0) {\n if (text[i] === \">\") return null // Hit a tag close — we're outside\n if (text[i] === \"<\") {\n // Found opening '<' — now find the closing '>'\n let j = pos\n while (j < text.length) {\n if (text[j] === \">\") return { tagStart: i, tagEnd: j + 1 }\n j++\n }\n // Unclosed tag — treat as inside\n return { tagStart: i, tagEnd: text.length }\n }\n i--\n }\n return null\n}\n\n/**\n * Snap annotation start/end positions to avoid landing inside HTML tags.\n *\n * If a position falls inside an HTML tag, it is moved:\n * - Start position: snapped to before the tag's `<`\n * - End position: snapped to after the tag's `>`\n *\n * Returns null if the positions can't be safely adjusted (e.g., entirely\n * within a single tag).\n */\nfunction snapOutOfHtmlTags(\n text: string,\n start: number,\n end: number,\n): { start: number; end: number } | null {\n let snappedStart = start\n let snappedEnd = end\n\n const startTag = findContainingTag(text, start)\n if (startTag) {\n snappedStart = startTag.tagStart\n }\n\n const endTag = findContainingTag(text, end)\n if (endTag) {\n snappedEnd = endTag.tagEnd\n }\n\n // Sanity check: start must come before end\n if (snappedStart >= snappedEnd) return null\n\n return { start: snappedStart, end: snappedEnd }\n}\n\n/**\n * Escape HTML entities to prevent XSS injection.\n *\n * Converts special HTML characters to their entity equivalents:\n * - `&` → `&amp;`\n * - `<` → `&lt;`\n * - `>` → `&gt;`\n * - `\"` → `&quot;`\n * - `'` → `&#39;`\n * - `/` → `&#x2F;`\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtmlEntities(text: string): string {\n const map: Record<string, string> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n \"/\": \"&#x2F;\",\n }\n return text.replace(/[&<>\"'/]/g, (char) => map[char])\n}\n"],"mappings":"mEA+CA,SAAgB,EACd,EACA,EACA,EAAgC,EAAE,CAChB,CAClB,GAAM,CACJ,eAAe,GACf,aAAa,GACb,cAAc,GACd,WACA,YACE,EAGE,EAAS,CAAC,GAAG,EAAU,CAAC,MAAM,EAAG,IAAM,CAC3C,IAAM,EAAO,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,cAEvD,OADa,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,eACzC,GACd,CAEE,EAAS,EACP,EAAc,IAAI,IAClB,EAAsB,EAAE,CAE9B,IAAK,IAAM,KAAY,EAAQ,CAE7B,IAAI,EACA,EAaJ,GAXI,GAAe,aAAc,GAAY,EAAS,UAEpD,EAAQ,EAAe,EAAS,SAAS,WAAa,EAAS,SAAS,cACxE,EAAM,EAAe,EAAS,SAAS,SAAW,EAAS,SAAS,cAGpE,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,aAI1D,CAAC,EAAc,CACjB,IAAM,EAAU,EAAkB,EAAQ,EAAO,EAAI,CACrD,GAAI,IAAY,KAAM,CAEpB,EAAQ,KAAK,EAAS,CACtB,SAEF,EAAQ,EAAQ,MAChB,EAAM,EAAQ,IAGhB,IAAI,EAAS,GAEb,GAAI,EAGF,EAAS,EAAS,EADE,EAAK,UAAU,KAAK,IAAI,EAAG,EAAQ,GAAG,CAAE,KAAK,IAAI,EAAK,OAAQ,EAAM,GAAG,CAAC,CACpD,SAC/B,EAAU,CAEnB,IAAM,EAAe,EAAO,UAAU,EAAO,EAAI,CAC3C,EAAU,EAAa,EAAmB,EAAa,CAAG,EAChE,EAAS,EAAS,OAAS,EAAU,EAAS,WAG9C,SAIF,EAAS,EAAO,MAAM,EAAG,EAAM,CAAG,EAAS,EAAO,MAAM,EAAI,CAG5D,EAAY,IAAI,EAAO,EAAM,CAG/B,MAAO,CAAE,KAAM,EAAQ,cAAa,UAAS,CAO/C,SAAS,EAAkB,EAAc,EAA0D,CAEjG,IAAI,EAAI,EAAM,EACd,KAAO,GAAK,GAAG,CACb,GAAI,EAAK,KAAO,IAAK,OAAO,KAC5B,GAAI,EAAK,KAAO,IAAK,CAEnB,IAAI,EAAI,EACR,KAAO,EAAI,EAAK,QAAQ,CACtB,GAAI,EAAK,KAAO,IAAK,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAI,EAAG,CAC1D,IAGF,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAK,OAAQ,CAE7C,IAEF,OAAO,KAaT,SAAS,EACP,EACA,EACA,EACuC,CACvC,IAAI,EAAe,EACf,EAAa,EAEX,EAAW,EAAkB,EAAM,EAAM,CAC3C,IACF,EAAe,EAAS,UAG1B,IAAM,EAAS,EAAkB,EAAM,EAAI,CAQ3C,OAPI,IACF,EAAa,EAAO,QAIlB,GAAgB,EAAmB,KAEhC,CAAE,MAAO,EAAc,IAAK,EAAY,CAiBjD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,YAAc,GAAS,EAAI,GAAM"}
@@ -1,4 +1,4 @@
1
- import { t as Citation } from "../citation-4bmWbhSK.cjs";
1
+ import { t as Citation } from "../citation-By8QXtGC.cjs";
2
2
 
3
3
  //#region src/annotate/types.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Citation } from "../citation-BVN0o8TJ.mjs";
1
+ import { t as Citation } from "../citation-BwXdJTA9.mjs";
2
2
 
3
3
  //#region src/annotate/types.d.ts
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/annotate/annotate.ts"],"sourcesContent":["import type { Citation } from '../types/citation'\nimport type { AnnotationOptions, AnnotationResult } from './types'\n\n/**\n * Annotate citations in text with custom markup.\n *\n * Supports two modes:\n * - **Template mode**: Simple before/after wrapping (set `options.template`)\n * - **Callback mode**: Custom logic with full citation context (set `options.callback`)\n *\n * Citations are processed in reverse order to avoid position shifts invalidating\n * subsequent annotations. Position tracking maps original positions to new positions\n * after markup insertion.\n *\n * @param text - Original or cleaned text to annotate\n * @param citations - Citations to mark up (from extraction pipeline)\n * @param options - Annotation configuration\n * @returns Annotated text with position mapping\n *\n * @example Template mode\n * ```typescript\n * const result = annotate(text, citations, {\n * template: { before: '<cite>', after: '</cite>' }\n * })\n * // Result: \"See <cite>500 F.2d 123</cite>\"\n * ```\n *\n * @example Callback mode\n * ```typescript\n * const result = annotate(text, citations, {\n * callback: (citation) => {\n * if (citation.type === 'case') {\n * return `<a href=\"/cases/${citation.volume}\">${citation.matchedText}</a>`\n * }\n * return citation.matchedText\n * }\n * })\n * ```\n *\n * @example Position tracking\n * ```typescript\n * const result = annotate(text, citations, { template: { before: '<mark>', after: '</mark>' } })\n * // result.positionMap tracks how positions shifted\n * const originalPos = 10\n * const newPos = result.positionMap.get(originalPos)\n * ```\n */\nexport function annotate<C extends Citation = Citation>(\n text: string,\n citations: C[],\n options: AnnotationOptions<C> = {}\n): AnnotationResult {\n const {\n useCleanText = false,\n autoEscape = true, // Secure by default\n useFullSpan = false, // Backward compatible default\n template,\n callback,\n } = options\n\n // Sort reverse to avoid position shifts invalidating subsequent annotations\n const sorted = [...citations].sort((a, b) => {\n const aPos = useCleanText ? a.span.cleanStart : a.span.originalStart\n const bPos = useCleanText ? b.span.cleanStart : b.span.originalStart\n return bPos - aPos // Reverse for backward iteration\n })\n\n let result = text\n const positionMap = new Map<number, number>()\n const skipped: Citation[] = []\n\n for (const citation of sorted) {\n // Determine which span to use\n let start: number\n let end: number\n\n if (useFullSpan && 'fullSpan' in citation && citation.fullSpan) {\n // Full span mode: case name through parenthetical\n start = useCleanText ? citation.fullSpan.cleanStart : citation.fullSpan.originalStart\n end = useCleanText ? citation.fullSpan.cleanEnd : citation.fullSpan.originalEnd\n } else {\n // Default mode: core citation only\n start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\n }\n\n // Snap positions out of HTML tags when annotating original text\n if (!useCleanText) {\n const snapped = snapOutOfHtmlTags(result, start, end)\n if (snapped === null) {\n // Could not safely snap — skip this citation\n skipped.push(citation)\n continue\n }\n start = snapped.start\n end = snapped.end\n }\n\n let markup = ''\n\n if (callback) {\n // Callback mode: developer provides full logic\n const surrounding = text.substring(\n Math.max(0, start - 30),\n Math.min(text.length, end + 30)\n )\n markup = callback(citation, surrounding)\n } else if (template) {\n // Template mode: simple before/after wrapping\n const citationText = result.substring(start, end)\n const escaped = autoEscape ? escapeHtmlEntities(citationText) : citationText\n markup = template.before + escaped + template.after\n } else {\n // No annotation specified\n continue\n }\n\n // Insert annotation (working backwards preserves positions for later citations)\n result = result.slice(0, start) + markup + result.slice(end)\n\n // Track original position to new position (before this annotation was added)\n positionMap.set(start, start)\n }\n\n return { text: result, positionMap, skipped }\n}\n\n/**\n * Check if a position falls inside an HTML tag (between `<` and `>`).\n * Returns the index of the opening `<` if inside a tag, otherwise -1.\n */\nfunction findContainingTag(text: string, pos: number): { tagStart: number; tagEnd: number } | null {\n // Search backwards from pos for '<' without encountering '>' first\n let i = pos - 1\n while (i >= 0) {\n if (text[i] === '>') return null // Hit a tag close — we're outside\n if (text[i] === '<') {\n // Found opening '<' — now find the closing '>'\n let j = pos\n while (j < text.length) {\n if (text[j] === '>') return { tagStart: i, tagEnd: j + 1 }\n j++\n }\n // Unclosed tag — treat as inside\n return { tagStart: i, tagEnd: text.length }\n }\n i--\n }\n return null\n}\n\n/**\n * Snap annotation start/end positions to avoid landing inside HTML tags.\n *\n * If a position falls inside an HTML tag, it is moved:\n * - Start position: snapped to before the tag's `<`\n * - End position: snapped to after the tag's `>`\n *\n * Returns null if the positions can't be safely adjusted (e.g., entirely\n * within a single tag).\n */\nfunction snapOutOfHtmlTags(\n text: string,\n start: number,\n end: number,\n): { start: number; end: number } | null {\n let snappedStart = start\n let snappedEnd = end\n\n const startTag = findContainingTag(text, start)\n if (startTag) {\n snappedStart = startTag.tagStart\n }\n\n const endTag = findContainingTag(text, end)\n if (endTag) {\n snappedEnd = endTag.tagEnd\n }\n\n // Sanity check: start must come before end\n if (snappedStart >= snappedEnd) return null\n\n return { start: snappedStart, end: snappedEnd }\n}\n\n/**\n * Escape HTML entities to prevent XSS injection.\n *\n * Converts special HTML characters to their entity equivalents:\n * - `&` → `&amp;`\n * - `<` → `&lt;`\n * - `>` → `&gt;`\n * - `\"` → `&quot;`\n * - `'` → `&#39;`\n * - `/` → `&#x2F;`\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtmlEntities(text: string): string {\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n '/': '&#x2F;',\n }\n return text.replace(/[&<>\"'/]/g, (char) => map[char])\n}\n"],"mappings":"AA+CA,SAAgB,EACd,EACA,EACA,EAAgC,EAAE,CAChB,CAClB,GAAM,CACJ,eAAe,GACf,aAAa,GACb,cAAc,GACd,WACA,YACE,EAGE,EAAS,CAAC,GAAG,EAAU,CAAC,MAAM,EAAG,IAAM,CAC3C,IAAM,EAAO,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,cAEvD,OADa,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,eACzC,GACd,CAEE,EAAS,EACP,EAAc,IAAI,IAClB,EAAsB,EAAE,CAE9B,IAAK,IAAM,KAAY,EAAQ,CAE7B,IAAI,EACA,EAaJ,GAXI,GAAe,aAAc,GAAY,EAAS,UAEpD,EAAQ,EAAe,EAAS,SAAS,WAAa,EAAS,SAAS,cACxE,EAAM,EAAe,EAAS,SAAS,SAAW,EAAS,SAAS,cAGpE,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,aAI1D,CAAC,EAAc,CACjB,IAAM,EAAU,EAAkB,EAAQ,EAAO,EAAI,CACrD,GAAI,IAAY,KAAM,CAEpB,EAAQ,KAAK,EAAS,CACtB,SAEF,EAAQ,EAAQ,MAChB,EAAM,EAAQ,IAGhB,IAAI,EAAS,GAEb,GAAI,EAMF,EAAS,EAAS,EAJE,EAAK,UACvB,KAAK,IAAI,EAAG,EAAQ,GAAG,CACvB,KAAK,IAAI,EAAK,OAAQ,EAAM,GAAG,CAChC,CACuC,SAC/B,EAAU,CAEnB,IAAM,EAAe,EAAO,UAAU,EAAO,EAAI,CAC3C,EAAU,EAAa,EAAmB,EAAa,CAAG,EAChE,EAAS,EAAS,OAAS,EAAU,EAAS,WAG9C,SAIF,EAAS,EAAO,MAAM,EAAG,EAAM,CAAG,EAAS,EAAO,MAAM,EAAI,CAG5D,EAAY,IAAI,EAAO,EAAM,CAG/B,MAAO,CAAE,KAAM,EAAQ,cAAa,UAAS,CAO/C,SAAS,EAAkB,EAAc,EAA0D,CAEjG,IAAI,EAAI,EAAM,EACd,KAAO,GAAK,GAAG,CACb,GAAI,EAAK,KAAO,IAAK,OAAO,KAC5B,GAAI,EAAK,KAAO,IAAK,CAEnB,IAAI,EAAI,EACR,KAAO,EAAI,EAAK,QAAQ,CACtB,GAAI,EAAK,KAAO,IAAK,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAI,EAAG,CAC1D,IAGF,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAK,OAAQ,CAE7C,IAEF,OAAO,KAaT,SAAS,EACP,EACA,EACA,EACuC,CACvC,IAAI,EAAe,EACf,EAAa,EAEX,EAAW,EAAkB,EAAM,EAAM,CAC3C,IACF,EAAe,EAAS,UAG1B,IAAM,EAAS,EAAkB,EAAM,EAAI,CAQ3C,OAPI,IACF,EAAa,EAAO,QAIlB,GAAgB,EAAmB,KAEhC,CAAE,MAAO,EAAc,IAAK,EAAY,CAiBjD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,YAAc,GAAS,EAAI,GAAM"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/annotate/annotate.ts"],"sourcesContent":["import type { Citation } from \"../types/citation\"\nimport type { AnnotationOptions, AnnotationResult } from \"./types\"\n\n/**\n * Annotate citations in text with custom markup.\n *\n * Supports two modes:\n * - **Template mode**: Simple before/after wrapping (set `options.template`)\n * - **Callback mode**: Custom logic with full citation context (set `options.callback`)\n *\n * Citations are processed in reverse order to avoid position shifts invalidating\n * subsequent annotations. Position tracking maps original positions to new positions\n * after markup insertion.\n *\n * @param text - Original or cleaned text to annotate\n * @param citations - Citations to mark up (from extraction pipeline)\n * @param options - Annotation configuration\n * @returns Annotated text with position mapping\n *\n * @example Template mode\n * ```typescript\n * const result = annotate(text, citations, {\n * template: { before: '<cite>', after: '</cite>' }\n * })\n * // Result: \"See <cite>500 F.2d 123</cite>\"\n * ```\n *\n * @example Callback mode\n * ```typescript\n * const result = annotate(text, citations, {\n * callback: (citation) => {\n * if (citation.type === 'case') {\n * return `<a href=\"/cases/${citation.volume}\">${citation.matchedText}</a>`\n * }\n * return citation.matchedText\n * }\n * })\n * ```\n *\n * @example Position tracking\n * ```typescript\n * const result = annotate(text, citations, { template: { before: '<mark>', after: '</mark>' } })\n * // result.positionMap tracks how positions shifted\n * const originalPos = 10\n * const newPos = result.positionMap.get(originalPos)\n * ```\n */\nexport function annotate<C extends Citation = Citation>(\n text: string,\n citations: C[],\n options: AnnotationOptions<C> = {},\n): AnnotationResult {\n const {\n useCleanText = false,\n autoEscape = true, // Secure by default\n useFullSpan = false, // Backward compatible default\n template,\n callback,\n } = options\n\n // Sort reverse to avoid position shifts invalidating subsequent annotations\n const sorted = [...citations].sort((a, b) => {\n const aPos = useCleanText ? a.span.cleanStart : a.span.originalStart\n const bPos = useCleanText ? b.span.cleanStart : b.span.originalStart\n return bPos - aPos // Reverse for backward iteration\n })\n\n let result = text\n const positionMap = new Map<number, number>()\n const skipped: Citation[] = []\n\n for (const citation of sorted) {\n // Determine which span to use\n let start: number\n let end: number\n\n if (useFullSpan && \"fullSpan\" in citation && citation.fullSpan) {\n // Full span mode: case name through parenthetical\n start = useCleanText ? citation.fullSpan.cleanStart : citation.fullSpan.originalStart\n end = useCleanText ? citation.fullSpan.cleanEnd : citation.fullSpan.originalEnd\n } else {\n // Default mode: core citation only\n start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\n }\n\n // Snap positions out of HTML tags when annotating original text\n if (!useCleanText) {\n const snapped = snapOutOfHtmlTags(result, start, end)\n if (snapped === null) {\n // Could not safely snap — skip this citation\n skipped.push(citation)\n continue\n }\n start = snapped.start\n end = snapped.end\n }\n\n let markup = \"\"\n\n if (callback) {\n // Callback mode: developer provides full logic\n const surrounding = text.substring(Math.max(0, start - 30), Math.min(text.length, end + 30))\n markup = callback(citation, surrounding)\n } else if (template) {\n // Template mode: simple before/after wrapping\n const citationText = result.substring(start, end)\n const escaped = autoEscape ? escapeHtmlEntities(citationText) : citationText\n markup = template.before + escaped + template.after\n } else {\n // No annotation specified\n continue\n }\n\n // Insert annotation (working backwards preserves positions for later citations)\n result = result.slice(0, start) + markup + result.slice(end)\n\n // Track original position to new position (before this annotation was added)\n positionMap.set(start, start)\n }\n\n return { text: result, positionMap, skipped }\n}\n\n/**\n * Check if a position falls inside an HTML tag (between `<` and `>`).\n * Returns the index of the opening `<` if inside a tag, otherwise -1.\n */\nfunction findContainingTag(text: string, pos: number): { tagStart: number; tagEnd: number } | null {\n // Search backwards from pos for '<' without encountering '>' first\n let i = pos - 1\n while (i >= 0) {\n if (text[i] === \">\") return null // Hit a tag close — we're outside\n if (text[i] === \"<\") {\n // Found opening '<' — now find the closing '>'\n let j = pos\n while (j < text.length) {\n if (text[j] === \">\") return { tagStart: i, tagEnd: j + 1 }\n j++\n }\n // Unclosed tag — treat as inside\n return { tagStart: i, tagEnd: text.length }\n }\n i--\n }\n return null\n}\n\n/**\n * Snap annotation start/end positions to avoid landing inside HTML tags.\n *\n * If a position falls inside an HTML tag, it is moved:\n * - Start position: snapped to before the tag's `<`\n * - End position: snapped to after the tag's `>`\n *\n * Returns null if the positions can't be safely adjusted (e.g., entirely\n * within a single tag).\n */\nfunction snapOutOfHtmlTags(\n text: string,\n start: number,\n end: number,\n): { start: number; end: number } | null {\n let snappedStart = start\n let snappedEnd = end\n\n const startTag = findContainingTag(text, start)\n if (startTag) {\n snappedStart = startTag.tagStart\n }\n\n const endTag = findContainingTag(text, end)\n if (endTag) {\n snappedEnd = endTag.tagEnd\n }\n\n // Sanity check: start must come before end\n if (snappedStart >= snappedEnd) return null\n\n return { start: snappedStart, end: snappedEnd }\n}\n\n/**\n * Escape HTML entities to prevent XSS injection.\n *\n * Converts special HTML characters to their entity equivalents:\n * - `&` → `&amp;`\n * - `<` → `&lt;`\n * - `>` → `&gt;`\n * - `\"` → `&quot;`\n * - `'` → `&#39;`\n * - `/` → `&#x2F;`\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtmlEntities(text: string): string {\n const map: Record<string, string> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\n \"/\": \"&#x2F;\",\n }\n return text.replace(/[&<>\"'/]/g, (char) => map[char])\n}\n"],"mappings":"AA+CA,SAAgB,EACd,EACA,EACA,EAAgC,EAAE,CAChB,CAClB,GAAM,CACJ,eAAe,GACf,aAAa,GACb,cAAc,GACd,WACA,YACE,EAGE,EAAS,CAAC,GAAG,EAAU,CAAC,MAAM,EAAG,IAAM,CAC3C,IAAM,EAAO,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,cAEvD,OADa,EAAe,EAAE,KAAK,WAAa,EAAE,KAAK,eACzC,GACd,CAEE,EAAS,EACP,EAAc,IAAI,IAClB,EAAsB,EAAE,CAE9B,IAAK,IAAM,KAAY,EAAQ,CAE7B,IAAI,EACA,EAaJ,GAXI,GAAe,aAAc,GAAY,EAAS,UAEpD,EAAQ,EAAe,EAAS,SAAS,WAAa,EAAS,SAAS,cACxE,EAAM,EAAe,EAAS,SAAS,SAAW,EAAS,SAAS,cAGpE,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,aAI1D,CAAC,EAAc,CACjB,IAAM,EAAU,EAAkB,EAAQ,EAAO,EAAI,CACrD,GAAI,IAAY,KAAM,CAEpB,EAAQ,KAAK,EAAS,CACtB,SAEF,EAAQ,EAAQ,MAChB,EAAM,EAAQ,IAGhB,IAAI,EAAS,GAEb,GAAI,EAGF,EAAS,EAAS,EADE,EAAK,UAAU,KAAK,IAAI,EAAG,EAAQ,GAAG,CAAE,KAAK,IAAI,EAAK,OAAQ,EAAM,GAAG,CAAC,CACpD,SAC/B,EAAU,CAEnB,IAAM,EAAe,EAAO,UAAU,EAAO,EAAI,CAC3C,EAAU,EAAa,EAAmB,EAAa,CAAG,EAChE,EAAS,EAAS,OAAS,EAAU,EAAS,WAG9C,SAIF,EAAS,EAAO,MAAM,EAAG,EAAM,CAAG,EAAS,EAAO,MAAM,EAAI,CAG5D,EAAY,IAAI,EAAO,EAAM,CAG/B,MAAO,CAAE,KAAM,EAAQ,cAAa,UAAS,CAO/C,SAAS,EAAkB,EAAc,EAA0D,CAEjG,IAAI,EAAI,EAAM,EACd,KAAO,GAAK,GAAG,CACb,GAAI,EAAK,KAAO,IAAK,OAAO,KAC5B,GAAI,EAAK,KAAO,IAAK,CAEnB,IAAI,EAAI,EACR,KAAO,EAAI,EAAK,QAAQ,CACtB,GAAI,EAAK,KAAO,IAAK,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAI,EAAG,CAC1D,IAGF,MAAO,CAAE,SAAU,EAAG,OAAQ,EAAK,OAAQ,CAE7C,IAEF,OAAO,KAaT,SAAS,EACP,EACA,EACA,EACuC,CACvC,IAAI,EAAe,EACf,EAAa,EAEX,EAAW,EAAkB,EAAM,EAAM,CAC3C,IACF,EAAe,EAAS,UAG1B,IAAM,EAAS,EAAkB,EAAM,EAAI,CAQ3C,OAPI,IACF,EAAa,EAAO,QAIlB,GAAgB,EAAmB,KAEhC,CAAE,MAAO,EAAc,IAAK,EAAY,CAiBjD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,YAAc,GAAS,EAAI,GAAM"}
@@ -1,3 +1,34 @@
1
+ //#region src/extract/pincite.d.ts
2
+ /**
3
+ * Structured pincite information parsed from citation text.
4
+ */
5
+ interface PinciteInfo {
6
+ /** Primary page number */
7
+ page: number;
8
+ /** End page for ranges: "570-75" → 575 */
9
+ endPage?: number;
10
+ /** Footnote number: "570 n.3" → 3 */
11
+ footnote?: number;
12
+ /** True if this is a page range */
13
+ isRange: boolean;
14
+ /** Original text before parsing */
15
+ raw: string;
16
+ }
17
+ /**
18
+ * Parse a pincite string into structured components.
19
+ *
20
+ * Handles simple pages, ranges (with abbreviated end pages),
21
+ * footnote references, and "at" prefixes.
22
+ *
23
+ * @example
24
+ * parsePincite("570") // { page: 570, isRange: false, raw: "570" }
25
+ * parsePincite("570-75") // { page: 570, endPage: 575, isRange: true, raw: "570-75" }
26
+ * parsePincite("570 n.3") // { page: 570, footnote: 3, isRange: false, raw: "570 n.3" }
27
+ *
28
+ * @returns Parsed pincite info, or null if unparseable
29
+ */
30
+ declare function parsePincite(raw: string): PinciteInfo | null;
31
+ //#endregion
1
32
  //#region src/types/span.d.ts
2
33
  /**
3
34
  * Represents a text span with positions tracked through transformations.
@@ -43,7 +74,7 @@ interface TransformationMap {
43
74
  /**
44
75
  * Citation type discriminator for type-safe pattern matching.
45
76
  */
46
- type CitationType = "case" | "statute" | "journal" | "neutral" | "publicLaw" | "federalRegister" | "statutesAtLarge" | "id" | "supra" | "shortFormCase";
77
+ type CitationType = "case" | "statute" | "journal" | "neutral" | "publicLaw" | "federalRegister" | "statutesAtLarge" | "constitutional" | "id" | "supra" | "shortFormCase";
47
78
  /**
48
79
  * Warning generated during citation parsing.
49
80
  */
@@ -61,6 +92,11 @@ interface Warning {
61
92
  context?: string;
62
93
  }
63
94
  /**
95
+ * Introductory signal word classification for citation support level.
96
+ * Based on Bluebook signal categories (Rule 1.2).
97
+ */
98
+ type CitationSignal = "see" | "see also" | "see generally" | "cf" | "but see" | "but cf" | "compare" | "accord" | "contra";
99
+ /**
64
100
  * Base fields shared by all citation types.
65
101
  */
66
102
  interface CitationBase {
@@ -84,6 +120,75 @@ interface CitationBase {
84
120
  patternsChecked: number;
85
121
  /** Warnings for malformed or ambiguous regions */
86
122
  warnings?: Warning[];
123
+ /** Introductory signal word (e.g., "see", "see also", "but see") */
124
+ signal?: CitationSignal;
125
+ /** Group ID for string citations sharing the same proposition */
126
+ stringCitationGroupId?: string;
127
+ /** Position within the string citation group (0-indexed) */
128
+ stringCitationIndex?: number;
129
+ /** Total number of citations in this string citation group */
130
+ stringCitationGroupSize?: number;
131
+ /** Whether this citation appears in a footnote (only populated when detectFootnotes enabled) */
132
+ inFootnote?: boolean;
133
+ /** Footnote number, if applicable (only populated when detectFootnotes enabled) */
134
+ footnoteNumber?: number;
135
+ }
136
+ /**
137
+ * Court level and jurisdiction inferred from reporter series.
138
+ * Populated independently of the parenthetical-extracted `court` field.
139
+ */
140
+ interface CourtInference {
141
+ /** Court level classification */
142
+ level: "supreme" | "appellate" | "trial" | "unknown";
143
+ /** Jurisdiction classification */
144
+ jurisdiction: "federal" | "state" | "unknown";
145
+ /** 2-letter state code, only for state-specific reporters */
146
+ state?: string;
147
+ /** Confidence score 0.0-1.0 (1.0 for unambiguous, 0.7 for regional multi-state) */
148
+ confidence: number;
149
+ }
150
+ /**
151
+ * Signal-word classification for explanatory parentheticals.
152
+ * Based on the leading gerund/verb form in the parenthetical text.
153
+ */
154
+ type ParentheticalType = "holding" | "finding" | "stating" | "noting" | "explaining" | "quoting" | "citing" | "discussing" | "describing" | "recognizing" | "applying" | "rejecting" | "adopting" | "requiring" | "other";
155
+ /**
156
+ * An extracted explanatory parenthetical from a case citation.
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * { text: "holding that X requires Y", type: "holding" }
161
+ * { text: "citing Doe v. City for the same proposition", type: "citing" }
162
+ * ```
163
+ */
164
+ interface Parenthetical {
165
+ /** Full text content between the parentheses (excluding parens themselves) */
166
+ text: string;
167
+ /** Signal-word classification based on leading gerund */
168
+ type: ParentheticalType;
169
+ }
170
+ /**
171
+ * Normalized subsequent history signal classification.
172
+ * Maps variant spellings (aff'd, affirmed) to canonical forms.
173
+ */
174
+ type HistorySignal = "affirmed" | "reversed" | "cert_denied" | "cert_granted" | "overruled" | "vacated" | "remanded" | "modified" | "abrogated" | "superseded" | "disapproved" | "questioned" | "distinguished" | "withdrawn" | "reinstated";
175
+ /**
176
+ * A single subsequent history entry from a case citation.
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * { signal: "affirmed", rawSignal: "aff'd", signalSpan: { ... }, order: 0 }
181
+ * ```
182
+ */
183
+ interface SubsequentHistoryEntry {
184
+ /** Normalized signal classification */
185
+ signal: HistorySignal;
186
+ /** Raw signal text as it appeared in the document */
187
+ rawSignal: string;
188
+ /** Position of the signal text in the document */
189
+ signalSpan: Span;
190
+ /** Order in the history chain (0-based) */
191
+ order: number;
87
192
  }
88
193
  /**
89
194
  * Full case citation (volume-reporter-page format).
@@ -98,7 +203,11 @@ interface FullCaseCitation extends CitationBase {
98
203
  /** Page number — optional for blank page placeholder citations (e.g., "___" or "---") */
99
204
  page?: number;
100
205
  pincite?: number;
206
+ /** Structured pincite information (page, range, footnote) */
207
+ pinciteInfo?: PinciteInfo;
101
208
  court?: string;
209
+ /** Normalized court string: spaces collapsed, trailing period ensured */
210
+ normalizedCourt?: string;
102
211
  year?: number;
103
212
  /** Normalized reporter abbreviation from reporters-db (e.g., "F.2d" vs "F. 2d") */
104
213
  normalizedReporter?: string;
@@ -116,12 +225,29 @@ interface FullCaseCitation extends CitationBase {
116
225
  reporter: string;
117
226
  page: number;
118
227
  }>;
119
- /** Citation signal (introductory phrase) */
120
- signal?: "see" | "see also" | "cf" | "but see" | "compare";
121
- /** Parenthetical explanation following the citation */
122
- parenthetical?: string;
123
- /** Subsequent procedural history (e.g., "aff'd", "rev'd", "cert. denied") */
124
- subsequentHistory?: string;
228
+ /**
229
+ * Explanatory parentheticals following the citation.
230
+ * Only populated when explanatory content is found (not court/year/disposition).
231
+ * @example [{ text: "holding that X requires Y", type: "holding" }]
232
+ */
233
+ parentheticals?: Parenthetical[];
234
+ /**
235
+ * Subsequent history entries for this citation.
236
+ * Each entry describes a procedural event (affirmed, reversed, etc.).
237
+ * Only populated on the parent (original) citation.
238
+ * @example [{ signal: "affirmed", rawSignal: "aff'd", signalSpan: {...}, order: 0 }]
239
+ */
240
+ subsequentHistoryEntries?: SubsequentHistoryEntry[];
241
+ /**
242
+ * Back-pointer indicating this citation is a subsequent history citation.
243
+ * `index` is the parent's position in the results array returned by
244
+ * `extractCitations()` — it becomes invalid if the array is filtered or reordered.
245
+ * @example { index: 0, signal: "affirmed" }
246
+ */
247
+ subsequentHistoryOf?: {
248
+ index: number;
249
+ signal: HistorySignal;
250
+ };
125
251
  /**
126
252
  * Date information in multiple formats.
127
253
  * - iso: ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ssZ)
@@ -189,6 +315,18 @@ interface FullCaseCitation extends CitationBase {
189
315
  */
190
316
  proceduralPrefix?: string;
191
317
  /**
318
+ * Nominative (historical) reporter volume for early SCOTUS citations.
319
+ * Present only when citation includes a nominative parenthetical, e.g.,
320
+ * `67 U.S. (2 Black) 635` → nominativeVolume: 2
321
+ */
322
+ nominativeVolume?: number;
323
+ /**
324
+ * Nominative (historical) reporter abbreviation for early SCOTUS citations.
325
+ * Present only when citation includes a nominative parenthetical, e.g.,
326
+ * `67 U.S. (2 Black) 635` → nominativeReporter: "Black"
327
+ */
328
+ nominativeReporter?: string;
329
+ /**
192
330
  * True when page position contains a blank placeholder ("___" or "---").
193
331
  * Populated by Phase 5 (Blank Page support).
194
332
  * When true, page field will be undefined and confidence reduced to 0.8.
@@ -200,17 +338,37 @@ interface FullCaseCitation extends CitationBase {
200
338
  * @example "en banc", "per curiam"
201
339
  */
202
340
  disposition?: string;
341
+ /**
342
+ * Court level/jurisdiction inferred from reporter series.
343
+ * Always populated independently of the parenthetical `court` field.
344
+ * Uses a curated static lookup table — does not depend on the reporter DB
345
+ * to preserve tree-shaking of the `eyecite-ts/data` entry point.
346
+ */
347
+ inferredCourt?: CourtInference;
203
348
  }
204
349
  /**
205
350
  * Statute citation (U.S. Code, state codes, etc.).
206
351
  *
207
352
  * @example "42 U.S.C. § 1983"
353
+ * @example "42 U.S.C. § 1983(a)(1) et seq."
208
354
  */
209
355
  interface StatuteCitation extends CitationBase {
210
356
  type: "statute";
211
357
  title?: number;
212
358
  code: string;
213
359
  section: string;
360
+ /** Subsection/pincite chain, e.g. "(a)(1)(A)" */
361
+ subsection?: string;
362
+ /** 2-letter state code or "US" when unambiguously identified */
363
+ jurisdiction?: string;
364
+ /**
365
+ * Alias for subsection (eyecite-ts convention).
366
+ * Note: this is string (subsection chain), unlike FullCaseCitation.pincite which is number (page offset).
367
+ * The discriminated union on `type` ensures type safety at call sites.
368
+ */
369
+ pincite?: string;
370
+ /** True when "et seq." follows the citation */
371
+ hasEtSeq?: boolean;
214
372
  }
215
373
  /**
216
374
  * Journal citation (law review, legal periodical).
@@ -301,6 +459,26 @@ interface StatutesAtLargeCitation extends CitationBase {
301
459
  year?: number;
302
460
  }
303
461
  /**
462
+ * Constitutional citation (U.S. or state constitution).
463
+ *
464
+ * @example "U.S. Const. art. III, § 2"
465
+ * @example "U.S. Const. amend. XIV, § 1"
466
+ * @example "Cal. Const. art. I, § 7"
467
+ */
468
+ interface ConstitutionalCitation extends CitationBase {
469
+ type: "constitutional";
470
+ /** Jurisdiction code: "US", 2-letter state code, or undefined for bare "Const." */
471
+ jurisdiction?: string;
472
+ /** Article number (parsed from Roman numerals) — mutually exclusive with amendment */
473
+ article?: number;
474
+ /** Amendment number (parsed from Roman numerals) — mutually exclusive with article */
475
+ amendment?: number;
476
+ /** Section identifier (string to handle non-numeric like "3-a") */
477
+ section?: string;
478
+ /** Clause number (always numeric) */
479
+ clause?: number;
480
+ }
481
+ /**
304
482
  * Id. citation (refers to immediately preceding citation).
305
483
  *
306
484
  * @example "Id."
@@ -353,16 +531,16 @@ interface ShortFormCaseCitation extends CitationBase {
353
531
  * // ...
354
532
  * }
355
533
  */
356
- type Citation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation | StatutesAtLargeCitation | IdCitation | SupraCitation | ShortFormCaseCitation;
534
+ type Citation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation | StatutesAtLargeCitation | ConstitutionalCitation | IdCitation | SupraCitation | ShortFormCaseCitation;
357
535
  /**
358
536
  * Citation type discriminators grouped by category.
359
537
  */
360
- type FullCitationType = "case" | "statute" | "journal" | "neutral" | "publicLaw" | "federalRegister" | "statutesAtLarge";
538
+ type FullCitationType = "case" | "statute" | "journal" | "neutral" | "publicLaw" | "federalRegister" | "statutesAtLarge" | "constitutional";
361
539
  type ShortFormCitationType = "id" | "supra" | "shortFormCase";
362
540
  /**
363
541
  * Union of all full citation types (not short-form references).
364
542
  */
365
- type FullCitation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation | StatutesAtLargeCitation;
543
+ type FullCitation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation | StatutesAtLargeCitation | ConstitutionalCitation;
366
544
  /**
367
545
  * Union of all short-form citation types (Id., supra, short-form case).
368
546
  */
@@ -385,5 +563,5 @@ type CitationOfType<T extends CitationType> = Extract<Citation, {
385
563
  */
386
564
  type ExtractorMap = { [K in FullCitationType]: CitationOfType<K> };
387
565
  //#endregion
388
- export { TransformationMap as S, StatuteCitation as _, ExtractorMap as a, Warning as b, FullCitation as c, JournalCitation as d, NeutralCitation as f, ShortFormCitationType as g, ShortFormCitation as h, CitationType as i, FullCitationType as l, ShortFormCaseCitation as m, CitationBase as n, FederalRegisterCitation as o, PublicLawCitation as p, CitationOfType as r, FullCaseCitation as s, Citation as t, IdCitation as u, StatutesAtLargeCitation as v, Span as x, SupraCitation as y };
389
- //# sourceMappingURL=citation-4bmWbhSK.d.cts.map
566
+ export { PinciteInfo as A, StatuteCitation as C, Warning as D, SupraCitation as E, Span as O, ShortFormCitationType as S, SubsequentHistoryEntry as T, Parenthetical as _, CitationType as a, ShortFormCaseCitation as b, ExtractorMap as c, FullCitation as d, FullCitationType as f, NeutralCitation as g, JournalCitation as h, CitationSignal as i, parsePincite as j, TransformationMap as k, FederalRegisterCitation as l, IdCitation as m, CitationBase as n, ConstitutionalCitation as o, HistorySignal as p, CitationOfType as r, CourtInference as s, Citation as t, FullCaseCitation as u, ParentheticalType as v, StatutesAtLargeCitation as w, ShortFormCitation as x, PublicLawCitation as y };
567
+ //# sourceMappingURL=citation-BwXdJTA9.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citation-BwXdJTA9.d.mts","names":[],"sources":["../src/extract/pincite.ts","../src/types/span.ts","../src/types/citation.ts"],"mappings":"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"}