eyecite-ts 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # eyecite-ts
2
2
 
3
- TypeScript legal citation extraction library - port of Python [eyecite](https://github.com/freelawproject/eyecite).
3
+ TypeScript legal citation extraction library port of Python [eyecite](https://github.com/freelawproject/eyecite).
4
4
 
5
- Extract, validate, annotate, and resolve legal citations from court opinions and legal documents with zero runtime dependencies and a <50KB bundle size.
5
+ Extract, resolve, and annotate legal citations from court opinions and legal documents with zero runtime dependencies.
6
6
 
7
7
  ## Features
8
8
 
9
9
  - **Full citation extraction**: Case citations, statutes, journal articles, neutral citations, public laws, federal register
10
10
  - **Short-form resolution**: Id./Ibid., supra, and short-form case citations resolved to their full antecedents
11
- - **Reporter database**: 1235 reporters with variant matching and confidence scoring
12
- - **Citation annotation**: HTML/Markdown markup with auto-escape and position tracking
13
- - **Bundle optimization**: Tree-shakeable exports, lazy-loaded data, separate entry points
14
- - **TypeScript native**: Discriminated unions, strict types, full IntelliSense
11
+ - **Reporter database**: 1,200+ reporters with variant matching and confidence scoring
12
+ - **Citation annotation**: HTML markup with auto-escape XSS protection and position tracking
13
+ - **Bundle optimization**: Tree-shakeable exports, lazy-loaded reporter data, separate entry points
14
+ - **TypeScript native**: Discriminated unions, conditional types, type guards, full IntelliSense
15
+ - **Zero dependencies**: No runtime dependencies, 4.4KB gzipped core bundle
15
16
 
16
17
  ## Installation
17
18
 
@@ -36,13 +37,13 @@ console.log(citations[0])
36
37
  // court: '9th Cir.',
37
38
  // year: 2020,
38
39
  // confidence: 0.85,
39
- // span: { originalStart: 4, originalEnd: 48 }
40
+ // span: { originalStart: 4, originalEnd: 48, cleanStart: 4, cleanEnd: 48 }
40
41
  // }
41
42
  ```
42
43
 
43
44
  ## Citation Extraction
44
45
 
45
- ### Basic Usage
46
+ ### Multiple Citation Types
46
47
 
47
48
  ```typescript
48
49
  import { extractCitations } from 'eyecite-ts'
@@ -70,7 +71,8 @@ const citations = await extractCitationsAsync(text)
70
71
  ### Custom Patterns
71
72
 
72
73
  ```typescript
73
- import { extractCitations, casePatterns } from 'eyecite-ts'
74
+ import { extractCitations } from 'eyecite-ts'
75
+ import { casePatterns } from 'eyecite-ts'
74
76
 
75
77
  // Extract only case citations
76
78
  const citations = extractCitations(text, {
@@ -81,17 +83,17 @@ const citations = extractCitations(text, {
81
83
  ### Custom Cleaners
82
84
 
83
85
  ```typescript
84
- import { extractCitations, stripHtmlTags } from 'eyecite-ts'
86
+ import { extractCitations, cleanText } from 'eyecite-ts'
85
87
 
86
- // Use only HTML stripping, skip Unicode normalization
88
+ // Use only HTML stripping
87
89
  const citations = extractCitations(html, {
88
- cleaners: [stripHtmlTags]
90
+ cleaners: [(text) => text.replace(/<[^>]+>/g, '')]
89
91
  })
90
92
  ```
91
93
 
92
94
  ## Resolving Short-Form Citations
93
95
 
94
- Short-form citations (Id., supra, short-form case) refer to earlier citations in the document. The resolution engine automatically links them to their full antecedents.
96
+ Short-form citations (Id., supra, short-form case) refer to earlier citations in the document. The resolution engine links them to their full antecedents.
95
97
 
96
98
  ### Convenience API
97
99
 
@@ -105,16 +107,11 @@ const text = `
105
107
  500 F.2d at 140.
106
108
  `
107
109
 
108
- // Convenience: extract + resolve in one call
109
110
  const citations = extractCitations(text, { resolve: true })
110
111
 
111
112
  // citations[1] is Id. citation
112
113
  console.log(citations[1].resolution)
113
- // {
114
- // resolvedTo: 0, // Points to Smith v. Jones (index 0)
115
- // confidence: 1.0,
116
- // warnings: []
117
- // }
114
+ // { resolvedTo: 0, confidence: 1.0 }
118
115
  ```
119
116
 
120
117
  ### Power-User API
@@ -122,15 +119,13 @@ console.log(citations[1].resolution)
122
119
  ```typescript
123
120
  import { extractCitations, resolveCitations } from 'eyecite-ts'
124
121
 
125
- // Step 1: Extract citations
126
122
  const citations = extractCitations(text)
127
123
 
128
- // Step 2: Resolve short-form citations
129
124
  const resolved = resolveCitations(citations, text, {
130
- scopeStrategy: 'paragraph', // Only resolve within paragraphs
131
- fuzzyPartyMatching: true, // Enable fuzzy supra matching
132
- partyMatchThreshold: 0.8, // Similarity threshold (0-1)
133
- reportUnresolved: true // Report failure reasons
125
+ scopeStrategy: 'paragraph',
126
+ fuzzyPartyMatching: true,
127
+ partyMatchThreshold: 0.8,
128
+ reportUnresolved: true
134
129
  })
135
130
  ```
136
131
 
@@ -153,125 +148,91 @@ const resolved = resolveCitations(citations, text, {
153
148
  ```typescript
154
149
  const text = 'Smith v. Jones, 500 F.2d 123. Id. at 125.'
155
150
  const citations = extractCitations(text, { resolve: true })
156
-
157
- // citations[1].resolution.resolvedTo === 0 (points to Smith v. Jones)
151
+ // citations[1].resolution.resolvedTo === 0
158
152
  ```
159
153
 
160
154
  **Supra citations:**
161
155
 
162
156
  ```typescript
163
- const text = 'Smith v. Jones, 500 F.2d 123. See also Smith, supra, at 130.'
157
+ const text = 'Smith v. Jones, 500 F.2d 123. Smith, supra, at 130.'
164
158
  const citations = extractCitations(text, { resolve: true })
165
-
166
159
  // citations[1].resolution.resolvedTo === 0 (party name matches "Smith")
167
160
  ```
168
161
 
169
162
  **Short-form case citations:**
170
163
 
171
164
  ```typescript
172
- const text = 'Brown v. Board, 347 U.S. 483 (1954). See 347 U.S. at 495.'
165
+ const text = 'Brown v. Board, 347 U.S. 483. See 347 U.S. at 495.'
173
166
  const citations = extractCitations(text, { resolve: true })
174
-
175
167
  // citations[1].resolution.resolvedTo === 0 (volume/reporter matches)
176
168
  ```
177
169
 
178
- ### Handling Unresolved Citations
170
+ **Unresolved citations:**
179
171
 
180
172
  ```typescript
181
173
  const text = 'Id. at 100.' // Orphan Id. with no preceding citation
182
-
183
174
  const citations = extractCitations(text, { resolve: true })
184
-
185
- console.log(citations[0].resolution)
186
- // {
187
- // resolvedTo: undefined,
188
- // failureReason: 'No preceding full citation found',
189
- // confidence: 0,
190
- // warnings: []
191
- // }
192
- ```
193
-
194
- To suppress unresolved warnings:
195
-
196
- ```typescript
197
- const citations = extractCitations(text, {
198
- resolve: true,
199
- resolutionOptions: {
200
- reportUnresolved: false // Omits resolution field for unresolved citations
201
- }
202
- })
203
- ```
204
-
205
- ## Citation Validation
206
-
207
- Validate case citations against the reporters database:
208
-
209
- ```typescript
210
- import { validateCitation } from 'eyecite-ts/data'
211
-
212
- // Returns citations with adjusted confidence scores
213
- const validated = await validateCitation(citations)
214
-
215
- // Confidence adjustments:
216
- // - +0.2 boost for reporter match
217
- // - -0.3 penalty for reporter mismatch
218
- // - -0.1 penalty for ambiguous reporter
175
+ // citations[0].resolution.failureReason === 'No preceding full case citation found'
219
176
  ```
220
177
 
221
178
  ## Citation Annotation
222
179
 
223
- Add HTML/Markdown markup to citations:
180
+ Add HTML markup to citations in text:
224
181
 
225
182
  ```typescript
226
183
  import { annotate } from 'eyecite-ts/annotate'
184
+ import { extractCitations } from 'eyecite-ts'
185
+
186
+ const text = 'See Smith v. Jones, 500 F.2d 123 (2020).'
187
+ const citations = extractCitations(text)
227
188
 
228
- // Template mode (simple)
229
- const html = annotate(
230
- text,
231
- citations,
232
- '<a href="{{url}}">{{text}}</a>'
233
- )
189
+ // Template mode
190
+ const result = annotate(text, citations, {
191
+ template: { before: '<cite>', after: '</cite>' }
192
+ })
193
+ // result.text === 'See Smith v. Jones, <cite>500 F.2d 123</cite> (2020).'
234
194
 
235
195
  // Callback mode (full control)
236
- const html = annotate(text, citations, (citation, text) => {
237
- const url = `https://example.com/${citation.volume}/${citation.reporter}/${citation.page}`
238
- return `<a href="${url}">${text}</a>`
196
+ const result2 = annotate(text, citations, {
197
+ callback: (citation, surrounding) => {
198
+ if (citation.type === 'case') {
199
+ return `<a href="/cases/${citation.volume}">${citation.matchedText}</a>`
200
+ }
201
+ return citation.matchedText
202
+ }
239
203
  })
240
204
  ```
241
205
 
242
206
  Auto-escape is enabled by default for XSS protection:
243
207
 
244
208
  ```typescript
245
- // User input is automatically escaped
246
- const html = annotate(text, citations, '<a>{{text}}</a>', {
247
- autoEscape: true // default
209
+ const result = annotate(text, citations, {
210
+ template: { before: '<cite>', after: '</cite>' },
211
+ autoEscape: true // default — escapes &, <, >, ", ', /
248
212
  })
249
213
  ```
250
214
 
251
- ## Bundle Size
252
-
253
- Core library is optimized for tree-shaking:
254
-
255
- - **Core extraction**: 2.5 KB gzipped
256
- - **Reporter database**: 88.5 KB gzipped (lazy-loaded)
257
- - **Annotation**: 0.5 KB gzipped
215
+ ## Reporter Validation
258
216
 
259
- Import only what you need:
217
+ Validate case citations against the reporters database:
260
218
 
261
219
  ```typescript
262
- // Tree-shakeable imports
263
- import { extractCitations } from 'eyecite-ts' // Core only
264
- import { validateCitation } from 'eyecite-ts/data' // Core + data
265
- import { annotate } from 'eyecite-ts/annotate' // Core + annotate
220
+ import { extractWithValidation } from 'eyecite-ts'
221
+
222
+ const validated = await extractWithValidation(text, { validate: true })
223
+ // Confidence adjustments:
224
+ // +0.2 boost for reporter match
225
+ // -0.3 penalty for unknown reporter
226
+ // -0.1 per extra match for ambiguous reporter
266
227
  ```
267
228
 
268
- ## Citation Types
229
+ ## Type System
269
230
 
270
- All citation types are exported with full TypeScript types:
231
+ All citation types use a discriminated union on the `type` field:
271
232
 
272
233
  ```typescript
273
234
  import type {
274
- Citation,
235
+ Citation, // Union of all 9 types
275
236
  FullCaseCitation,
276
237
  StatuteCitation,
277
238
  JournalCitation,
@@ -280,24 +241,69 @@ import type {
280
241
  FederalRegisterCitation,
281
242
  IdCitation,
282
243
  SupraCitation,
283
- ShortFormCaseCitation
244
+ ShortFormCaseCitation,
245
+ CitationOfType, // Extract subtype: CitationOfType<'case'> = FullCaseCitation
246
+ ExtractorMap, // Maps FullCitationType keys to citation subtypes
247
+ FullCitation, // Union of full citation types
248
+ ShortFormCitation, // Union of short-form types
284
249
  } from 'eyecite-ts'
250
+ ```
285
251
 
286
- // Discriminated union - switch on type
287
- citations.forEach(citation => {
288
- switch (citation.type) {
289
- case 'case':
290
- console.log(citation.reporter) // FullCaseCitation
291
- break
292
- case 'statute':
293
- console.log(citation.title) // StatuteCitation
294
- break
295
- case 'id':
296
- console.log(citation.pincite) // IdCitation
297
- break
298
- // etc.
299
- }
300
- })
252
+ ### Type Guards
253
+
254
+ ```typescript
255
+ import {
256
+ isFullCitation,
257
+ isShortFormCitation,
258
+ isCaseCitation,
259
+ isCitationType,
260
+ assertUnreachable
261
+ } from 'eyecite-ts'
262
+
263
+ // Specific guards
264
+ if (isFullCitation(citation)) {
265
+ // citation: FullCitation
266
+ }
267
+
268
+ // Generic guard — narrows to any specific type
269
+ if (isCitationType(citation, 'statute')) {
270
+ // citation: StatuteCitation
271
+ }
272
+
273
+ // Exhaustiveness check in switch statements
274
+ switch (citation.type) {
275
+ case 'case': /* ... */ break
276
+ case 'statute': /* ... */ break
277
+ // ... all 9 types ...
278
+ default: assertUnreachable(citation.type)
279
+ }
280
+ ```
281
+
282
+ ### Resolved Citation Types
283
+
284
+ `ResolvedCitation` uses a conditional type — `resolution` is only meaningfully present on short-form citations:
285
+
286
+ ```typescript
287
+ import type { ResolvedCitation } from 'eyecite-ts'
288
+
289
+ // On short-form citations: resolution: ResolutionResult | undefined
290
+ // On full citations: resolution?: undefined
291
+ ```
292
+
293
+ ## Bundle Size
294
+
295
+ Three entry points for optimal tree-shaking:
296
+
297
+ | Entry Point | Import | Gzipped |
298
+ |------------|--------|---------|
299
+ | Core extraction | `eyecite-ts` | 4.4 KB |
300
+ | Annotation | `eyecite-ts/annotate` | 0.5 KB |
301
+ | Reporter data | `eyecite-ts/data` | 88.5 KB (lazy-loaded) |
302
+
303
+ ```typescript
304
+ import { extractCitations } from 'eyecite-ts' // Core only
305
+ import { annotate } from 'eyecite-ts/annotate' // Annotation
306
+ import { loadReporters } from 'eyecite-ts/data' // Reporter database
301
307
  ```
302
308
 
303
309
  ## Architecture
@@ -307,28 +313,26 @@ Citation extraction follows a 4-stage pipeline:
307
313
  1. **Clean**: Remove HTML, normalize Unicode, fix smart quotes
308
314
  2. **Tokenize**: Apply regex patterns to find citation candidates
309
315
  3. **Extract**: Parse metadata (volume, reporter, page, etc.)
310
- 4. **Translate**: Map positions from cleaned text → original text
316
+ 4. **Resolve** (optional): Link short-form citations to antecedents
311
317
 
312
- All positions (spans) track both cleaned and original text offsets.
318
+ All positions (spans) track both cleaned and original text offsets via `TransformationMap`.
313
319
 
314
320
  See [ARCHITECTURE.md](ARCHITECTURE.md) for details.
315
321
 
316
322
  ## Development
317
323
 
318
324
  ```bash
319
- # Install dependencies
320
- npm install
321
-
322
- # Run tests
323
- npm test
324
-
325
- # Type checking
326
- npm run typecheck
327
-
328
- # Build
329
- npm run build
325
+ npm install # Install dependencies
326
+ npm test # Run tests (vitest, watch mode)
327
+ npx vitest run # Run tests once
328
+ npm run typecheck # Type-check with tsc
329
+ npm run build # Build (ESM + CJS + DTS)
330
+ npm run lint # Lint with Biome
331
+ npm run format # Format with Biome
330
332
  ```
331
333
 
334
+ 304 tests, 97% statement coverage, 91% branch coverage.
335
+
332
336
  ## License
333
337
 
334
338
  MIT
@@ -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(\n text: string,\n citations: Citation[],\n options: AnnotationOptions = {}\n): AnnotationResult {\n const {\n useCleanText = false,\n autoEscape = true, // Secure by 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\n for (const citation of sorted) {\n const start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n const end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\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 * 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,EAA6B,EAAE,CACb,CAClB,GAAM,CACJ,eAAe,GACf,aAAa,GACb,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,IAExB,IAAK,IAAM,KAAY,EAAQ,CAC7B,IAAM,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,YAE9D,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,QAAS,EAAE,CAAE,CAiBnD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,aAAe,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 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\n for (const citation of sorted) {\n const start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n const end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\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 * 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,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,IAExB,IAAK,IAAM,KAAY,EAAQ,CAC7B,IAAM,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,YAE9D,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,QAAS,EAAE,CAAE,CAiBnD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,aAAe,GAAS,EAAI,GAAM"}
@@ -1,4 +1,4 @@
1
- import { t as Citation } from "../citation-BcY5zzWb.cjs";
1
+ import { t as Citation } from "../citation-BhJJj_AZ.cjs";
2
2
 
3
3
  //#region src/annotate/types.d.ts
4
4
  /**
@@ -27,7 +27,7 @@ import { t as Citation } from "../citation-BcY5zzWb.cjs";
27
27
  * })
28
28
  * ```
29
29
  */
30
- interface AnnotationOptions {
30
+ interface AnnotationOptions<C extends Citation = Citation> {
31
31
  /**
32
32
  * Apply annotations to cleaned text (true) or original text (false).
33
33
  *
@@ -65,7 +65,7 @@ interface AnnotationOptions {
65
65
  * @param surrounding - Text around the citation (for context-aware markup)
66
66
  * @returns Complete markup string (replaces citation.matchedText)
67
67
  */
68
- callback?: (citation: Citation, surrounding: string) => string;
68
+ callback?: (citation: C, surrounding: string) => string;
69
69
  /**
70
70
  * Template mode: simple before/after markup strings.
71
71
  *
@@ -157,7 +157,7 @@ interface AnnotationResult {
157
157
  * const newPos = result.positionMap.get(originalPos)
158
158
  * ```
159
159
  */
160
- declare function annotate(text: string, citations: Citation[], options?: AnnotationOptions): AnnotationResult;
160
+ declare function annotate<C extends Citation = Citation>(text: string, citations: C[], options?: AnnotationOptions<C>): AnnotationResult;
161
161
  //#endregion
162
162
  export { AnnotationOptions, AnnotationResult, annotate };
163
163
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/annotate/types.ts","../../src/annotate/annotate.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;AAoEA;;;;;;;;;UApEiB,iBAAA;EA0FN;;;;ACvEX;;;;EDVE,YAAA;;;;;;;;;;;;;;;;;;EAmBA,UAAA;;;;;;;;;;;EAYA,QAAA,IAAY,QAAA,EAAU,QAAA,EAAU,WAAA;;;;;;;;;;;;;;;;EAiBhC,QAAA;+CAEE,MAAA;IAEA,KAAA;EAAA;AAAA;;;;UAOa,gBAAA;;;;EAIf,IAAA;;;;;;;;;EAUA,WAAA,EAAa,GAAA;;;;;;;EAQb,OAAA,EAAS,QAAA;AAAA;;;;AA1FX;;;;;;;;;;;;;;;AAoEA;;;;;;;;;;;;;;ACjDA;;;;;;;;;;;;;;iBAAgB,QAAA,CACd,IAAA,UACA,SAAA,EAAW,QAAA,IACX,OAAA,GAAS,iBAAA,GACR,gBAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/annotate/types.ts","../../src/annotate/annotate.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;;;;;;;;AAoEA;;UApEiB,iBAAA,WAA4B,QAAA,GAAW,QAAA;EA0F7C;;;;;;;;EAjFT,YAAA;;;ACUF;;;;;;;;;;;;;;;EDSE,UAAA;;;;;;;;;;;EAYA,QAAA,IAAY,QAAA,EAAU,CAAA,EAAG,WAAA;;;;;;;;;;;;;;;;EAiBzB,QAAA;+CAEE,MAAA;IAEA,KAAA;EAAA;AAAA;;;;UAOa,gBAAA;;;;EAIf,IAAA;;;;;;;;;EAUA,WAAA,EAAa,GAAA;;;;;;;EAQb,OAAA,EAAS,QAAA;AAAA;;;;AA1FX;;;;;;;;;;;;;;;;;;;;;;AAoEA;;;;;;;;;;;;;;ACjDA;;;;;;;iBAAgB,QAAA,WAAmB,QAAA,GAAW,QAAA,CAAA,CAC5C,IAAA,UACA,SAAA,EAAW,CAAA,IACX,OAAA,GAAS,iBAAA,CAAkB,CAAA,IAC1B,gBAAA"}
@@ -1,4 +1,4 @@
1
- import { t as Citation } from "../citation-8_GvfEuj.mjs";
1
+ import { t as Citation } from "../citation-FJ10UFM7.mjs";
2
2
 
3
3
  //#region src/annotate/types.d.ts
4
4
  /**
@@ -27,7 +27,7 @@ import { t as Citation } from "../citation-8_GvfEuj.mjs";
27
27
  * })
28
28
  * ```
29
29
  */
30
- interface AnnotationOptions {
30
+ interface AnnotationOptions<C extends Citation = Citation> {
31
31
  /**
32
32
  * Apply annotations to cleaned text (true) or original text (false).
33
33
  *
@@ -65,7 +65,7 @@ interface AnnotationOptions {
65
65
  * @param surrounding - Text around the citation (for context-aware markup)
66
66
  * @returns Complete markup string (replaces citation.matchedText)
67
67
  */
68
- callback?: (citation: Citation, surrounding: string) => string;
68
+ callback?: (citation: C, surrounding: string) => string;
69
69
  /**
70
70
  * Template mode: simple before/after markup strings.
71
71
  *
@@ -157,7 +157,7 @@ interface AnnotationResult {
157
157
  * const newPos = result.positionMap.get(originalPos)
158
158
  * ```
159
159
  */
160
- declare function annotate(text: string, citations: Citation[], options?: AnnotationOptions): AnnotationResult;
160
+ declare function annotate<C extends Citation = Citation>(text: string, citations: C[], options?: AnnotationOptions<C>): AnnotationResult;
161
161
  //#endregion
162
162
  export { AnnotationOptions, AnnotationResult, annotate };
163
163
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/annotate/types.ts","../../src/annotate/annotate.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;AAoEA;;;;;;;;;UApEiB,iBAAA;EA0FN;;;;ACvEX;;;;EDVE,YAAA;;;;;;;;;;;;;;;;;;EAmBA,UAAA;;;;;;;;;;;EAYA,QAAA,IAAY,QAAA,EAAU,QAAA,EAAU,WAAA;;;;;;;;;;;;;;;;EAiBhC,QAAA;+CAEE,MAAA;IAEA,KAAA;EAAA;AAAA;;;;UAOa,gBAAA;;;;EAIf,IAAA;;;;;;;;;EAUA,WAAA,EAAa,GAAA;;;;;;;EAQb,OAAA,EAAS,QAAA;AAAA;;;;AA1FX;;;;;;;;;;;;;;;AAoEA;;;;;;;;;;;;;;ACjDA;;;;;;;;;;;;;;iBAAgB,QAAA,CACd,IAAA,UACA,SAAA,EAAW,QAAA,IACX,OAAA,GAAS,iBAAA,GACR,gBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/annotate/types.ts","../../src/annotate/annotate.ts"],"mappings":";;;;;AA4BA;;;;;;;;;;;;;;;;;;;;;;AAoEA;;UApEiB,iBAAA,WAA4B,QAAA,GAAW,QAAA;EA0F7C;;;;;;;;EAjFT,YAAA;;;ACUF;;;;;;;;;;;;;;;EDSE,UAAA;;;;;;;;;;;EAYA,QAAA,IAAY,QAAA,EAAU,CAAA,EAAG,WAAA;;;;;;;;;;;;;;;;EAiBzB,QAAA;+CAEE,MAAA;IAEA,KAAA;EAAA;AAAA;;;;UAOa,gBAAA;;;;EAIf,IAAA;;;;;;;;;EAUA,WAAA,EAAa,GAAA;;;;;;;EAQb,OAAA,EAAS,QAAA;AAAA;;;;AA1FX;;;;;;;;;;;;;;;;;;;;;;AAoEA;;;;;;;;;;;;;;ACjDA;;;;;;;iBAAgB,QAAA,WAAmB,QAAA,GAAW,QAAA,CAAA,CAC5C,IAAA,UACA,SAAA,EAAW,CAAA,IACX,OAAA,GAAS,iBAAA,CAAkB,CAAA,IAC1B,gBAAA"}
@@ -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(\n text: string,\n citations: Citation[],\n options: AnnotationOptions = {}\n): AnnotationResult {\n const {\n useCleanText = false,\n autoEscape = true, // Secure by 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\n for (const citation of sorted) {\n const start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n const end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\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 * 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,EAA6B,EAAE,CACb,CAClB,GAAM,CACJ,eAAe,GACf,aAAa,GACb,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,IAExB,IAAK,IAAM,KAAY,EAAQ,CAC7B,IAAM,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,YAE9D,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,QAAS,EAAE,CAAE,CAiBnD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,aAAe,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 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\n for (const citation of sorted) {\n const start = useCleanText ? citation.span.cleanStart : citation.span.originalStart\n const end = useCleanText ? citation.span.cleanEnd : citation.span.originalEnd\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 * 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,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,IAExB,IAAK,IAAM,KAAY,EAAQ,CAC7B,IAAM,EAAQ,EAAe,EAAS,KAAK,WAAa,EAAS,KAAK,cAChE,EAAM,EAAe,EAAS,KAAK,SAAW,EAAS,KAAK,YAE9D,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,QAAS,EAAE,CAAE,CAiBnD,SAAS,EAAmB,EAAsB,CAChD,IAAM,EAA8B,CAClC,IAAK,QACL,IAAK,OACL,IAAK,OACL,IAAK,SACL,IAAK,QACL,IAAK,SACN,CACD,OAAO,EAAK,QAAQ,aAAe,GAAS,EAAI,GAAM"}
@@ -281,6 +281,36 @@ interface ShortFormCaseCitation extends CitationBase {
281
281
  * }
282
282
  */
283
283
  type Citation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation | IdCitation | SupraCitation | ShortFormCaseCitation;
284
+ /**
285
+ * Citation type discriminators grouped by category.
286
+ */
287
+ type FullCitationType = "case" | "statute" | "journal" | "neutral" | "publicLaw" | "federalRegister";
288
+ type ShortFormCitationType = "id" | "supra" | "shortFormCase";
289
+ /**
290
+ * Union of all full citation types (not short-form references).
291
+ */
292
+ type FullCitation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation;
293
+ /**
294
+ * Union of all short-form citation types (Id., supra, short-form case).
295
+ */
296
+ type ShortFormCitation = IdCitation | SupraCitation | ShortFormCaseCitation;
297
+ /**
298
+ * Extract the Citation subtype for a given type discriminator.
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * type CaseCit = CitationOfType<'case'> // FullCaseCitation
303
+ * type IdCit = CitationOfType<'id'> // IdCitation
304
+ * ```
305
+ */
306
+ type CitationOfType<T extends CitationType> = Extract<Citation, {
307
+ type: T;
308
+ }>;
309
+ /**
310
+ * Maps each full citation type to its concrete Citation subtype.
311
+ * Useful for generic code building custom extraction pipelines.
312
+ */
313
+ type ExtractorMap = { [K in FullCitationType]: CitationOfType<K> };
284
314
  //#endregion
285
- export { FullCaseCitation as a, NeutralCitation as c, StatuteCitation as d, SupraCitation as f, FederalRegisterCitation as i, PublicLawCitation as l, TransformationMap as m, CitationBase as n, IdCitation as o, Span as p, CitationType as r, JournalCitation as s, Citation as t, ShortFormCaseCitation as u };
286
- //# sourceMappingURL=citation-8_GvfEuj.d.mts.map
315
+ export { StatuteCitation as _, ExtractorMap as a, Span 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, SupraCitation as v, TransformationMap as x, Warning as y };
316
+ //# sourceMappingURL=citation-BhJJj_AZ.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"citation-8_GvfEuj.d.mts","names":[],"sources":["../src/types/span.ts","../src/types/citation.ts"],"mappings":";;AAiBA;;;;;;;;;;AAoBA;;;;;;UApBiB,IAAA;;EAEf,UAAA;EAuBiB;EApBjB,QAAA;;EAGA,aAAA;ECpBF;EDuBE,WAAA;AAAA;;;AClBF;;;;UD2BiB,iBAAA;;EAEf,eAAA,EAAiB,GAAA;;EAGjB,eAAA,EAAiB,GAAA;AAAA;;;AAzBnB;;;AAAA,KCZY,YAAA;;;;UAKK,OAAA;;EAEf,KAAA;EDyBF;ECvBE,OAAA;;EAEA,QAAA;IAAY,KAAA;IAAe,GAAA;EAAA;;EAE3B,OAAA;AAAA;;;;UAMe,YAAA;EAnBL;EAqBV,IAAA;EArBU;EAwBV,IAAA,EAAM,IAAA;EAnBR;;;;;;;EA4BE,UAAA;;EAGA,WAAA;;EAGA,aAAA;EApBF;EAuBE,eAAA;;EAGA,QAAA,GAAW,OAAA;AAAA;;;;;;;UASI,gBAAA,SAAyB,YAAA;EACxC,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;;EAGA,kBAAA;;EAGA,iBAAA,GAAoB,KAAA;IAClB,MAAA;IACA,QAAA;IACA,IAAA;EAAA;;EAIF,MAAA;;EAGA,aAAA;;EAGA,iBAAA;;;;;;EAOA,IAAA;IACE,GAAA;IACA,MAAA;MAAW,IAAA;MAAc,KAAA;MAAgB,GAAA;IAAA;EAAA;;;;;EAO3C,uBAAA,GAA0B,KAAA;IACxB,MAAA;IACA,QAAA;IACA,IAAA;IACA,UAAA;IACA,MAAA;EAAA;AAAA;;;;;;UASa,eAAA,SAAwB,YAAA;EACvC,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,MAAA;EAcA;EAZA,KAAA;EAuBe;EArBf,MAAA;EAqBuC;EAnBvC,OAAA;;EAEA,YAAA;;EAEA,IAAA;;EAEA,OAAA;EA+BF;EA7BE,IAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,IAAA;;EAEA,KAAA;;EAEA,cAAA;AAAA;;;AA6CF;;;;;;UAlCiB,iBAAA,SAA0B,YAAA;EACzC,IAAA;EAmCA;EAjCA,QAAA;EA0Ce;EAxCf,SAAA;EAwCqC;EAtCrC,KAAA;AAAA;;;;;AAoDF;;;;UAzCiB,uBAAA,SAAgC,YAAA;EAC/C,IAAA;;EAEA,MAAA;;EAEA,IAAA;;EAEA,IAAA;AAAA;;;;;;;UASe,UAAA,SAAmB,YAAA;EAClC,IAAA;EACA,OAAA;AAAA;;;;;;;UASe,aAAA,SAAsB,YAAA;EACrC,IAAA;;EAEA,SAAA;;EAEA,OAAA;AAAA;;;;;;;UASe,qBAAA,SAA8B,YAAA;EAC7C,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;;;;;;;;;;KAoBU,QAAA,GACR,gBAAA,GACA,eAAA,GACA,eAAA,GACA,eAAA,GACA,iBAAA,GACA,uBAAA,GACA,UAAA,GACA,aAAA,GACA,qBAAA"}
1
+ {"version":3,"file":"citation-BhJJj_AZ.d.cts","names":[],"sources":["../src/types/span.ts","../src/types/citation.ts"],"mappings":";;AAiBA;;;;;;;;;;AAoBA;;;;;;UApBiB,IAAA;;EAEf,UAAA;EAuBiB;EApBjB,QAAA;;EAGA,aAAA;ECpBF;EDuBE,WAAA;AAAA;;;AClBF;;;;UD2BiB,iBAAA;;EAEf,eAAA,EAAiB,GAAA;;EAGjB,eAAA,EAAiB,GAAA;AAAA;;;AAzBnB;;;AAAA,KCZY,YAAA;;;;UAKK,OAAA;;EAEf,KAAA;EDyBF;ECvBE,OAAA;;EAEA,QAAA;IAAY,KAAA;IAAe,GAAA;EAAA;;EAE3B,OAAA;AAAA;;;;UAMe,YAAA;EAnBL;EAqBV,IAAA;EArBU;EAwBV,IAAA,EAAM,IAAA;EAnBR;;;;;;;EA4BE,UAAA;;EAGA,WAAA;;EAGA,aAAA;EApBF;EAuBE,eAAA;;EAGA,QAAA,GAAW,OAAA;AAAA;;;;;;;UASI,gBAAA,SAAyB,YAAA;EACxC,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;;EAGA,kBAAA;;EAGA,iBAAA,GAAoB,KAAA;IAClB,MAAA;IACA,QAAA;IACA,IAAA;EAAA;;EAIF,MAAA;;EAGA,aAAA;;EAGA,iBAAA;;;;;;EAOA,IAAA;IACE,GAAA;IACA,MAAA;MAAW,IAAA;MAAc,KAAA;MAAgB,GAAA;IAAA;EAAA;;;;;EAO3C,uBAAA,GAA0B,KAAA;IACxB,MAAA;IACA,QAAA;IACA,IAAA;IACA,UAAA;IACA,MAAA;EAAA;AAAA;;;;;;UASa,eAAA,SAAwB,YAAA;EACvC,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,MAAA;EAcA;EAZA,KAAA;EAuBe;EArBf,MAAA;EAqBuC;EAnBvC,OAAA;;EAEA,YAAA;;EAEA,IAAA;;EAEA,OAAA;EA+BF;EA7BE,IAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,IAAA;;EAEA,KAAA;;EAEA,cAAA;AAAA;;;AA6CF;;;;;;UAlCiB,iBAAA,SAA0B,YAAA;EACzC,IAAA;EAmCA;EAjCA,QAAA;EA0Ce;EAxCf,SAAA;EAwCqC;EAtCrC,KAAA;AAAA;;;;;AAoDF;;;;UAzCiB,uBAAA,SAAgC,YAAA;EAC/C,IAAA;;EAEA,MAAA;;EAEA,IAAA;;EAEA,IAAA;AAAA;;;;;;;UASe,UAAA,SAAmB,YAAA;EAClC,IAAA;EACA,OAAA;AAAA;;;;;;;UASe,aAAA,SAAsB,YAAA;EACrC,IAAA;;EAEA,SAAA;;EAEA,OAAA;AAAA;;AAgDF;;;;;UAvCiB,qBAAA,SAA8B,YAAA;EAC7C,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;;;;;;;;;;KAoBU,QAAA,GACR,gBAAA,GACA,eAAA,GACA,eAAA,GACA,eAAA,GACA,iBAAA,GACA,uBAAA,GACA,UAAA,GACA,aAAA,GACA,qBAAA;;;;KAKQ,gBAAA;AAAA,KACA,qBAAA;;;;KAKA,YAAA,GAAe,gBAAA,GAAmB,eAAA,GAAkB,eAAA,GAAkB,eAAA,GAAkB,iBAAA,GAAoB,uBAAA;;;;KAK5G,iBAAA,GAAoB,UAAA,GAAa,aAAA,GAAgB,qBAAA;;;;;;;;;;KAWjD,cAAA,WAAyB,YAAA,IAAgB,OAAA,CAAQ,QAAA;EAAY,IAAA,EAAM,CAAA;AAAA;;;;;KAMnE,YAAA,WACJ,gBAAA,GAAmB,cAAA,CAAe,CAAA"}
@@ -281,6 +281,36 @@ interface ShortFormCaseCitation extends CitationBase {
281
281
  * }
282
282
  */
283
283
  type Citation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation | IdCitation | SupraCitation | ShortFormCaseCitation;
284
+ /**
285
+ * Citation type discriminators grouped by category.
286
+ */
287
+ type FullCitationType = "case" | "statute" | "journal" | "neutral" | "publicLaw" | "federalRegister";
288
+ type ShortFormCitationType = "id" | "supra" | "shortFormCase";
289
+ /**
290
+ * Union of all full citation types (not short-form references).
291
+ */
292
+ type FullCitation = FullCaseCitation | StatuteCitation | JournalCitation | NeutralCitation | PublicLawCitation | FederalRegisterCitation;
293
+ /**
294
+ * Union of all short-form citation types (Id., supra, short-form case).
295
+ */
296
+ type ShortFormCitation = IdCitation | SupraCitation | ShortFormCaseCitation;
297
+ /**
298
+ * Extract the Citation subtype for a given type discriminator.
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * type CaseCit = CitationOfType<'case'> // FullCaseCitation
303
+ * type IdCit = CitationOfType<'id'> // IdCitation
304
+ * ```
305
+ */
306
+ type CitationOfType<T extends CitationType> = Extract<Citation, {
307
+ type: T;
308
+ }>;
309
+ /**
310
+ * Maps each full citation type to its concrete Citation subtype.
311
+ * Useful for generic code building custom extraction pipelines.
312
+ */
313
+ type ExtractorMap = { [K in FullCitationType]: CitationOfType<K> };
284
314
  //#endregion
285
- export { FullCaseCitation as a, NeutralCitation as c, StatuteCitation as d, SupraCitation as f, FederalRegisterCitation as i, PublicLawCitation as l, TransformationMap as m, CitationBase as n, IdCitation as o, Span as p, CitationType as r, JournalCitation as s, Citation as t, ShortFormCaseCitation as u };
286
- //# sourceMappingURL=citation-BcY5zzWb.d.cts.map
315
+ export { StatuteCitation as _, ExtractorMap as a, Span 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, SupraCitation as v, TransformationMap as x, Warning as y };
316
+ //# sourceMappingURL=citation-FJ10UFM7.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"citation-BcY5zzWb.d.cts","names":[],"sources":["../src/types/span.ts","../src/types/citation.ts"],"mappings":";;AAiBA;;;;;;;;;;AAoBA;;;;;;UApBiB,IAAA;;EAEf,UAAA;EAuBiB;EApBjB,QAAA;;EAGA,aAAA;ECpBF;EDuBE,WAAA;AAAA;;;AClBF;;;;UD2BiB,iBAAA;;EAEf,eAAA,EAAiB,GAAA;;EAGjB,eAAA,EAAiB,GAAA;AAAA;;;AAzBnB;;;AAAA,KCZY,YAAA;;;;UAKK,OAAA;;EAEf,KAAA;EDyBF;ECvBE,OAAA;;EAEA,QAAA;IAAY,KAAA;IAAe,GAAA;EAAA;;EAE3B,OAAA;AAAA;;;;UAMe,YAAA;EAnBL;EAqBV,IAAA;EArBU;EAwBV,IAAA,EAAM,IAAA;EAnBR;;;;;;;EA4BE,UAAA;;EAGA,WAAA;;EAGA,aAAA;EApBF;EAuBE,eAAA;;EAGA,QAAA,GAAW,OAAA;AAAA;;;;;;;UASI,gBAAA,SAAyB,YAAA;EACxC,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;;EAGA,kBAAA;;EAGA,iBAAA,GAAoB,KAAA;IAClB,MAAA;IACA,QAAA;IACA,IAAA;EAAA;;EAIF,MAAA;;EAGA,aAAA;;EAGA,iBAAA;;;;;;EAOA,IAAA;IACE,GAAA;IACA,MAAA;MAAW,IAAA;MAAc,KAAA;MAAgB,GAAA;IAAA;EAAA;;;;;EAO3C,uBAAA,GAA0B,KAAA;IACxB,MAAA;IACA,QAAA;IACA,IAAA;IACA,UAAA;IACA,MAAA;EAAA;AAAA;;;;;;UASa,eAAA,SAAwB,YAAA;EACvC,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,MAAA;EAcA;EAZA,KAAA;EAuBe;EArBf,MAAA;EAqBuC;EAnBvC,OAAA;;EAEA,YAAA;;EAEA,IAAA;;EAEA,OAAA;EA+BF;EA7BE,IAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,IAAA;;EAEA,KAAA;;EAEA,cAAA;AAAA;;;AA6CF;;;;;;UAlCiB,iBAAA,SAA0B,YAAA;EACzC,IAAA;EAmCA;EAjCA,QAAA;EA0Ce;EAxCf,SAAA;EAwCqC;EAtCrC,KAAA;AAAA;;;;;AAoDF;;;;UAzCiB,uBAAA,SAAgC,YAAA;EAC/C,IAAA;;EAEA,MAAA;;EAEA,IAAA;;EAEA,IAAA;AAAA;;;;;;;UASe,UAAA,SAAmB,YAAA;EAClC,IAAA;EACA,OAAA;AAAA;;;;;;;UASe,aAAA,SAAsB,YAAA;EACrC,IAAA;;EAEA,SAAA;;EAEA,OAAA;AAAA;;;;;;;UASe,qBAAA,SAA8B,YAAA;EAC7C,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;;;;;;;;;;KAoBU,QAAA,GACR,gBAAA,GACA,eAAA,GACA,eAAA,GACA,eAAA,GACA,iBAAA,GACA,uBAAA,GACA,UAAA,GACA,aAAA,GACA,qBAAA"}
1
+ {"version":3,"file":"citation-FJ10UFM7.d.mts","names":[],"sources":["../src/types/span.ts","../src/types/citation.ts"],"mappings":";;AAiBA;;;;;;;;;;AAoBA;;;;;;UApBiB,IAAA;;EAEf,UAAA;EAuBiB;EApBjB,QAAA;;EAGA,aAAA;ECpBF;EDuBE,WAAA;AAAA;;;AClBF;;;;UD2BiB,iBAAA;;EAEf,eAAA,EAAiB,GAAA;;EAGjB,eAAA,EAAiB,GAAA;AAAA;;;AAzBnB;;;AAAA,KCZY,YAAA;;;;UAKK,OAAA;;EAEf,KAAA;EDyBF;ECvBE,OAAA;;EAEA,QAAA;IAAY,KAAA;IAAe,GAAA;EAAA;;EAE3B,OAAA;AAAA;;;;UAMe,YAAA;EAnBL;EAqBV,IAAA;EArBU;EAwBV,IAAA,EAAM,IAAA;EAnBR;;;;;;;EA4BE,UAAA;;EAGA,WAAA;;EAGA,aAAA;EApBF;EAuBE,eAAA;;EAGA,QAAA,GAAW,OAAA;AAAA;;;;;;;UASI,gBAAA,SAAyB,YAAA;EACxC,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;;EAGA,kBAAA;;EAGA,iBAAA,GAAoB,KAAA;IAClB,MAAA;IACA,QAAA;IACA,IAAA;EAAA;;EAIF,MAAA;;EAGA,aAAA;;EAGA,iBAAA;;;;;;EAOA,IAAA;IACE,GAAA;IACA,MAAA;MAAW,IAAA;MAAc,KAAA;MAAgB,GAAA;IAAA;EAAA;;;;;EAO3C,uBAAA,GAA0B,KAAA;IACxB,MAAA;IACA,QAAA;IACA,IAAA;IACA,UAAA;IACA,MAAA;EAAA;AAAA;;;;;;UASa,eAAA,SAAwB,YAAA;EACvC,IAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,MAAA;EAcA;EAZA,KAAA;EAuBe;EArBf,MAAA;EAqBuC;EAnBvC,OAAA;;EAEA,YAAA;;EAEA,IAAA;;EAEA,OAAA;EA+BF;EA7BE,IAAA;AAAA;;;;;;;;;UAWe,eAAA,SAAwB,YAAA;EACvC,IAAA;;EAEA,IAAA;;EAEA,KAAA;;EAEA,cAAA;AAAA;;;AA6CF;;;;;;UAlCiB,iBAAA,SAA0B,YAAA;EACzC,IAAA;EAmCA;EAjCA,QAAA;EA0Ce;EAxCf,SAAA;EAwCqC;EAtCrC,KAAA;AAAA;;;;;AAoDF;;;;UAzCiB,uBAAA,SAAgC,YAAA;EAC/C,IAAA;;EAEA,MAAA;;EAEA,IAAA;;EAEA,IAAA;AAAA;;;;;;;UASe,UAAA,SAAmB,YAAA;EAClC,IAAA;EACA,OAAA;AAAA;;;;;;;UASe,aAAA,SAAsB,YAAA;EACrC,IAAA;;EAEA,SAAA;;EAEA,OAAA;AAAA;;AAgDF;;;;;UAvCiB,qBAAA,SAA8B,YAAA;EAC7C,IAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EACA,OAAA;AAAA;;;;;;;;;;;;;;;;;;KAoBU,QAAA,GACR,gBAAA,GACA,eAAA,GACA,eAAA,GACA,eAAA,GACA,iBAAA,GACA,uBAAA,GACA,UAAA,GACA,aAAA,GACA,qBAAA;;;;KAKQ,gBAAA;AAAA,KACA,qBAAA;;;;KAKA,YAAA,GAAe,gBAAA,GAAmB,eAAA,GAAkB,eAAA,GAAkB,eAAA,GAAkB,iBAAA,GAAoB,uBAAA;;;;KAK5G,iBAAA,GAAoB,UAAA,GAAa,aAAA,GAAgB,qBAAA;;;;;;;;;;KAWjD,cAAA,WAAyB,YAAA,IAAgB,OAAA,CAAQ,QAAA;EAAY,IAAA,EAAM,CAAA;AAAA;;;;;KAMnE,YAAA,WACJ,gBAAA,GAAmB,cAAA,CAAe,CAAA"}