@stll/anonymize-wasm 0.1.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.
@@ -0,0 +1,919 @@
1
+ import { Validator } from "@stll/stdnum";
2
+ import { Match, PatternEntry, TextSearch } from "@stll/text-search";
3
+ import { Tokenizer } from "@huggingface/tokenizers";
4
+
5
+ //#region src/types.d.ts
6
+ /**
7
+ * Source of a detected entity span.
8
+ * Ordered by detection layer in the pipeline.
9
+ */
10
+ declare const DETECTION_SOURCES: {
11
+ readonly TRIGGER: "trigger";
12
+ readonly REGEX: "regex";
13
+ readonly DENY_LIST: "deny-list";
14
+ readonly LEGAL_FORM: "legal-form";
15
+ readonly GAZETTEER: "gazetteer";
16
+ readonly NER: "ner";
17
+ readonly COREFERENCE: "coreference";
18
+ };
19
+ type DetectionSource = (typeof DETECTION_SOURCES)[keyof typeof DETECTION_SOURCES];
20
+ /**
21
+ * Priority levels for detection sources.
22
+ * Higher = more structurally reliable. Used during
23
+ * overlap resolution so deterministic detectors beat
24
+ * probabilistic ones regardless of raw score.
25
+ */
26
+ declare const DETECTOR_PRIORITY: Record<DetectionSource, number>;
27
+ /**
28
+ * A detected PII entity span in the source text.
29
+ * Every detection layer produces these.
30
+ */
31
+ type Entity = {
32
+ start: number;
33
+ end: number;
34
+ label: string;
35
+ text: string;
36
+ score: number;
37
+ source: DetectionSource;
38
+ };
39
+ /**
40
+ * Entity after human review. Extends the base Entity
41
+ * with a review decision.
42
+ */
43
+ type ReviewDecision = "confirmed" | "rejected" | "relabeled";
44
+ type ReviewedEntity = Entity & {
45
+ decision?: ReviewDecision;
46
+ originalLabel?: string;
47
+ };
48
+ /**
49
+ * A single entry in the workspace-scoped gazetteer
50
+ * (deny list). Persisted in IndexedDB.
51
+ */
52
+ type GazetteerEntry = {
53
+ id: string;
54
+ canonical: string;
55
+ label: string;
56
+ variants: string[];
57
+ workspaceId: string;
58
+ createdAt: number;
59
+ source: "manual" | "confirmed-from-model";
60
+ };
61
+ /** Extraction strategy — closed discriminated union. */
62
+ type TriggerStrategy = {
63
+ type: "to-next-comma";
64
+ } | {
65
+ type: "to-end-of-line";
66
+ } | {
67
+ type: "n-words";
68
+ count: number;
69
+ } | {
70
+ type: "company-id-value";
71
+ } | {
72
+ type: "address";
73
+ maxChars?: number;
74
+ };
75
+ /** Validation rules — closed discriminated union. */
76
+ type TriggerValidation = {
77
+ type: "starts-uppercase";
78
+ } | {
79
+ type: "min-length";
80
+ min: number;
81
+ } | {
82
+ type: "max-length";
83
+ max: number;
84
+ } | {
85
+ type: "no-digits";
86
+ } | {
87
+ type: "has-digits";
88
+ } | {
89
+ type: "matches-pattern";
90
+ pattern: string;
91
+ flags?: string;
92
+ };
93
+ /** Auto-generated trigger variants — closed set. */
94
+ type TriggerExtension = "add-colon" | "add-trailing-space" | "add-colon-space" | "normalize-spaces";
95
+ /** V2 trigger config entry (JSON shape). */
96
+ type TriggerGroupConfig = {
97
+ id?: string;
98
+ triggers: string[];
99
+ label: string;
100
+ strategy: TriggerStrategy;
101
+ extensions?: TriggerExtension[];
102
+ validations?: TriggerValidation[];
103
+ /** When true, include the trigger text in the
104
+ * entity span (e.g., court names). */
105
+ includeTrigger?: boolean;
106
+ };
107
+ /** Compiled validation with pre-built regex. */
108
+ type CompiledValidation = {
109
+ type: "starts-uppercase";
110
+ re: RegExp;
111
+ } | {
112
+ type: "min-length";
113
+ min: number;
114
+ } | {
115
+ type: "max-length";
116
+ max: number;
117
+ } | {
118
+ type: "no-digits";
119
+ re: RegExp;
120
+ } | {
121
+ type: "has-digits";
122
+ re: RegExp;
123
+ } | {
124
+ type: "matches-pattern";
125
+ re: RegExp;
126
+ };
127
+ /**
128
+ * Runtime rule — one per trigger string after
129
+ * expansion. Fed to the Aho-Corasick automaton.
130
+ */
131
+ type TriggerRule = {
132
+ trigger: string;
133
+ label: string;
134
+ strategy: TriggerStrategy;
135
+ validations: CompiledValidation[];
136
+ includeTrigger: boolean;
137
+ };
138
+ /**
139
+ * Anonymisation operator types. Each operator defines
140
+ * how a confirmed entity is replaced in the output.
141
+ */
142
+ declare const OPERATOR_TYPES: readonly ["replace", "redact"];
143
+ type OperatorType = (typeof OPERATOR_TYPES)[number];
144
+ /** Per-label operator selection. Key is the entity label. */
145
+ type OperatorConfig = {
146
+ /** Operator per label. Missing labels default to "replace". */operators: Record<string, OperatorType>; /** Custom replacement string for the redact operator. */
147
+ redactString: string;
148
+ };
149
+ /** Whether an operator produces a reversible redaction entry. */
150
+ type OperatorReversibility = "reversible" | "irreversible";
151
+ type AnonymisationOperator = {
152
+ type: OperatorType;
153
+ reversibility: OperatorReversibility;
154
+ /**
155
+ * Apply the operator to a single entity occurrence.
156
+ * Returns the replacement string to embed in the document.
157
+ */
158
+ apply: (text: string, label: string, placeholder: string, redactString: string) => string;
159
+ };
160
+ /**
161
+ * Redacted document output with stable entity mapping.
162
+ */
163
+ type RedactionResult = {
164
+ redactedText: string;
165
+ /**
166
+ * Maps placeholder to original text. Only populated for
167
+ * reversible operators (replace). Empty for redact.
168
+ */
169
+ redactionMap: Map<string, string>; /** Maps placeholder to the operator that produced it. */
170
+ operatorMap: Map<string, OperatorType>;
171
+ entityCount: number;
172
+ };
173
+ /**
174
+ * Configuration for the detection pipeline.
175
+ */
176
+ type DenyListCategory = "Names" | "Places" | "Addresses" | "Courts" | "Financial" | "Government" | "Healthcare" | "Education" | "Political" | "Organizations" | "International";
177
+ type PipelineConfig = {
178
+ threshold: number;
179
+ enableTriggerPhrases: boolean;
180
+ enableRegex: boolean;
181
+ enableNameCorpus: boolean;
182
+ enableDenyList: boolean;
183
+ denyListCountries?: string[];
184
+ denyListRegions?: string[];
185
+ denyListExcludeCategories?: string[];
186
+ enableGazetteer: boolean;
187
+ enableNer: boolean;
188
+ enableConfidenceBoost: boolean;
189
+ enableCoreference: boolean;
190
+ enableZoneClassification?: boolean;
191
+ enableHotwordRules?: boolean;
192
+ labels: string[];
193
+ workspaceId: string;
194
+ };
195
+ /**
196
+ * Canonical entity labels used across the pipeline.
197
+ * NER models may use different native labels; the bench
198
+ * NER wrapper maps model output to these canonical names.
199
+ *
200
+ * These labels are ephemeral: entities are regenerated on
201
+ * every pipeline run and never persisted to the database.
202
+ * Renaming a label here requires no migration.
203
+ */
204
+ declare const DEFAULT_ENTITY_LABELS: readonly ["person", "organization", "phone number", "address", "email address", "date", "date of birth", "bank account number", "iban", "tax identification number", "identity card number", "registration number", "credit card number", "passport number", "monetary amount", "land parcel"];
205
+ //#endregion
206
+ //#region src/detectors/regex.d.ts
207
+ type RegexMeta = {
208
+ label: string;
209
+ score: number; /** Post-match stdnum validator for confirmation. */
210
+ validator?: Validator;
211
+ };
212
+ /** Flat pattern array for text-search. */
213
+ declare const REGEX_PATTERNS: readonly string[];
214
+ /** Parallel metadata. Index = pattern index. */
215
+ declare const REGEX_META: readonly RegexMeta[];
216
+ /**
217
+ * Get dynamically built date patterns from
218
+ * date-months.json. Returns a cached promise; the JSON
219
+ * is loaded only once.
220
+ */
221
+ declare const getDatePatterns: () => Promise<string[]>;
222
+ /** Date pattern metadata (all are score 1 dates). */
223
+ declare const DATE_PATTERN_META: Readonly<RegexMeta>;
224
+ /**
225
+ * Get dynamically built monetary amount patterns from
226
+ * currencies.json. Returns a cached promise; the JSON
227
+ * is loaded only once.
228
+ */
229
+ declare const getCurrencyPatterns: () => Promise<string[]>;
230
+ /** Currency pattern metadata (score 0.9). */
231
+ declare const CURRENCY_PATTERN_META: Readonly<RegexMeta>;
232
+ /**
233
+ * Process regex matches from the unified search.
234
+ * Receives all matches; filters to the regex slice
235
+ * via sliceStart/sliceEnd. Local index into META is
236
+ * match.pattern - sliceStart.
237
+ *
238
+ * For stdnum-derived patterns (those with a validator
239
+ * in META), the matched text is passed through the
240
+ * validator's validate() method. If validation fails,
241
+ * the match is discarded as a false positive.
242
+ */
243
+ declare const processRegexMatches: (allMatches: Match[], sliceStart: number, sliceEnd: number, meta_: readonly RegexMeta[]) => Entity[];
244
+ //#endregion
245
+ //#region src/detectors/deny-list.d.ts
246
+ type DenyListConfig = Pick<PipelineConfig, "enableDenyList" | "denyListCountries" | "denyListRegions" | "denyListExcludeCategories">;
247
+ /**
248
+ * Source tag for each pattern in the automaton.
249
+ * "deny-list" = standard deny list entry
250
+ * "first-name" = name corpus first name
251
+ * "surname" = name corpus surname
252
+ * "title" = academic/professional title
253
+ */
254
+ type PatternSource = "deny-list" | "first-name" | "surname" | "title";
255
+ /**
256
+ * Pre-built deny list data. Constructed once by
257
+ * `buildDenyList`, reused across `processDenyListMatches`
258
+ * calls. Contains PatternEntry[] for the unified builder
259
+ * plus parallel label/source arrays for post-processing.
260
+ */
261
+ type DenyListData = {
262
+ /**
263
+ * Maps pattern index → entity labels (plural).
264
+ * Same pattern can have multiple labels when it
265
+ * appears in multiple dictionaries (e.g., "Denver"
266
+ * is both a person name and a city name).
267
+ */
268
+ labels: string[][]; /** Maps pattern index → original pattern text. */
269
+ originals: string[]; /** Maps pattern index → source types (plural). */
270
+ sources: PatternSource[][];
271
+ };
272
+ /**
273
+ * Resolve which dictionaries to load based on country
274
+ * and category filters, load them, and build the deny
275
+ * list data. The returned data provides PatternEntry[]
276
+ * for the unified builder and parallel arrays for
277
+ * post-processing.
278
+ *
279
+ * Requires `@stll/anonymize-data` to be installed.
280
+ * Returns null if the data package is not available.
281
+ */
282
+ declare const buildDenyList: (config: DenyListConfig, ctx?: PipelineContext) => Promise<DenyListData | null>;
283
+ /**
284
+ * Ensure all deny-list support data (stopwords, allow
285
+ * list, person stopwords, generic roles) is loaded on
286
+ * the given context. Call this before
287
+ * processDenyListMatches / filterFalsePositives when
288
+ * the search instance was built on a different context
289
+ * (e.g. cachedSearch).
290
+ */
291
+ declare const ensureDenyListData: (ctx?: PipelineContext) => Promise<void>;
292
+ /**
293
+ * Process deny list matches from the unified search.
294
+ * Receives all matches; filters to the deny list slice
295
+ * via sliceStart/sliceEnd. Local index into data.labels,
296
+ * data.originals, data.sources is match.pattern - sliceStart.
297
+ *
298
+ * Two-pass approach to reduce false positives:
299
+ * 1. Collect all matches (case-insensitive,
300
+ * whole-word via Rust automaton)
301
+ * 2. Require uppercase start in source text
302
+ * 3. For person names, require at least one
303
+ * mid-sentence occurrence to prove proper noun
304
+ * 4. Return all occurrences of validated terms
305
+ */
306
+ declare const processDenyListMatches: (allMatches: Match[], sliceStart: number, sliceEnd: number, fullText: string, data: DenyListData, ctx?: PipelineContext) => Entity[];
307
+ //#endregion
308
+ //#region src/build-unified-search.d.ts
309
+ type PatternSlice = {
310
+ start: number;
311
+ end: number;
312
+ };
313
+ type GazetteerData = {
314
+ /** Maps local pattern index to entry label. */labels: string[];
315
+ /**
316
+ * Whether each pattern is fuzzy (distance > 0).
317
+ * Used by the post-processor to assign scores.
318
+ */
319
+ isFuzzy: boolean[];
320
+ };
321
+ type UnifiedSearchInstance = {
322
+ /** Regex + triggers + legal-forms. */tsRegex: TextSearch; /** Deny-list + street-types + gazetteer. */
323
+ tsLiterals: TextSearch;
324
+ slices: {
325
+ regex: PatternSlice;
326
+ legalForms: PatternSlice;
327
+ triggers: PatternSlice;
328
+ denyList: PatternSlice;
329
+ streetTypes: PatternSlice;
330
+ gazetteer: PatternSlice;
331
+ };
332
+ regexMeta: readonly RegexMeta[];
333
+ triggerRules: readonly TriggerRule[];
334
+ denyListData: DenyListData | null;
335
+ gazetteerData: GazetteerData | null;
336
+ };
337
+ declare const buildUnifiedSearch: (config: PipelineConfig, gazetteerEntries?: GazetteerEntry[], ctx?: PipelineContext) => Promise<UnifiedSearchInstance>;
338
+ //#endregion
339
+ //#region src/context.d.ts
340
+ /**
341
+ * Build a stable cache key for an entity that survives
342
+ * shallow copies (spread). Uses position + label so the
343
+ * key is identical for the original object and any
344
+ * `{ ...entity }` copy produced by mergeAndDedup.
345
+ */
346
+ declare const corefKey: (e: Entity) => string;
347
+ /**
348
+ * Compiled RegExp pattern used for coreference
349
+ * definition extraction.
350
+ */
351
+ type DefinitionPattern = {
352
+ pattern: RegExp;
353
+ };
354
+ /**
355
+ * Cached data for the name corpus detector.
356
+ * Populated by initNameCorpus; consumed by
357
+ * detectNameCorpus and deny-list AC integration.
358
+ */
359
+ type NameCorpusData = {
360
+ firstNames: ReadonlySet<string>;
361
+ surnames: ReadonlySet<string>;
362
+ titleTokens: ReadonlySet<string>;
363
+ excludedWords: ReadonlySet<string>; /** Raw arrays exposed for deny-list AC integration. */
364
+ firstNamesList: readonly string[];
365
+ surnamesList: readonly string[];
366
+ titlesList: readonly string[];
367
+ excludedList: readonly string[];
368
+ };
369
+ /**
370
+ * All cached state for a single pipeline run (or
371
+ * sequence of runs sharing the same config). Replacing
372
+ * module-level singletons with this object enables
373
+ * concurrent pipelines with different configs and
374
+ * simplifies testing.
375
+ *
376
+ * Each field starts null and is populated lazily on
377
+ * first use by the corresponding loader function.
378
+ */
379
+ type PipelineContext = {
380
+ search: UnifiedSearchInstance | null;
381
+ searchKey: string;
382
+ searchPromise: Promise<UnifiedSearchInstance> | null;
383
+ nameCorpus: NameCorpusData | null;
384
+ nameCorpusPromise: Promise<void> | null;
385
+ stopwords: ReadonlySet<string> | null;
386
+ stopwordsPromise: Promise<ReadonlySet<string>> | null;
387
+ allowList: ReadonlySet<string> | null;
388
+ allowListPromise: Promise<ReadonlySet<string>> | null;
389
+ personStopwords: ReadonlySet<string> | null;
390
+ personStopwordsPromise: Promise<ReadonlySet<string>> | null; /** First-name exclusions for stopword filtering. */
391
+ firstNameExclusions: ReadonlySet<string> | null;
392
+ firstNameExclusionCorpusLen: number;
393
+ genericRoles: ReadonlySet<string> | null;
394
+ genericRolesPromise: Promise<ReadonlySet<string>> | null;
395
+ corefPatterns: DefinitionPattern[] | null;
396
+ corefPatternsPromise: Promise<DefinitionPattern[]> | null;
397
+ corefLoadAttempted: boolean;
398
+ roleStopSet: ReadonlySet<string> | null;
399
+ roleStopSetPromise: Promise<ReadonlySet<string>> | null;
400
+ zoneHeadingPatterns: RegExp[] | null;
401
+ zoneSigningPatterns: RegExp[] | null;
402
+ zoneInitPromise: Promise<void> | null;
403
+ /**
404
+ * Maps coreference entities to their source entity
405
+ * text. Populated by findCoreferenceSpans, consumed
406
+ * by buildPlaceholderMap for consistent placeholder
407
+ * numbering across aliases and source entities.
408
+ *
409
+ * Keyed by `start:end:label` composite string so
410
+ * lookups survive shallow copies (e.g. from
411
+ * mergeAndDedup's spread operator).
412
+ */
413
+ corefSourceMap: Map<string, string>;
414
+ };
415
+ /** Create a fresh, empty pipeline context. */
416
+ declare const createPipelineContext: () => PipelineContext;
417
+ //#endregion
418
+ //#region src/pipeline.d.ts
419
+ /** Strip leading/trailing whitespace and punctuation. */
420
+ declare const sanitizeEntities: (entities: Entity[]) => Entity[];
421
+ declare const mergeAndDedup: (...layers: Entity[][]) => Entity[];
422
+ type NerInferenceFn = (fullText: string, labels: string[], threshold: number, signal?: AbortSignal) => Promise<Entity[]>;
423
+ /**
424
+ * Options for {@link runPipeline}.
425
+ *
426
+ * @property cachedSearch Pre-built search instance.
427
+ * When provided, `config` and `gazetteerEntries`
428
+ * are not used for building; the caller must
429
+ * ensure the instance matches both parameters.
430
+ */
431
+ type PipelineOptions = {
432
+ fullText: string;
433
+ config: PipelineConfig;
434
+ gazetteerEntries: GazetteerEntry[];
435
+ nerInference?: NerInferenceFn | null;
436
+ onProgress?: (step: string, detail: string) => void;
437
+ cachedSearch?: UnifiedSearchInstance;
438
+ signal?: AbortSignal;
439
+ context?: PipelineContext;
440
+ };
441
+ /**
442
+ * Run the full detection pipeline.
443
+ *
444
+ * Two TextSearch instances scan the text (regex +
445
+ * literals). Results are dispatched to each
446
+ * detector's post-processor by pattern index range.
447
+ *
448
+ * Pass an AbortSignal to cancel the pipeline between
449
+ * stages. Throws a DOMException with name "AbortError"
450
+ * when cancelled.
451
+ *
452
+ * Pass an optional `context` to isolate cached state
453
+ * from other pipeline runs. If omitted, a module-level
454
+ * default context is used (backward compatible).
455
+ */
456
+ declare const runPipeline: (options: PipelineOptions) => Promise<Entity[]>;
457
+ //#endregion
458
+ //#region src/redact.d.ts
459
+ /**
460
+ * Build a stable mapping from entity text to numbered
461
+ * placeholders. Same real-world value always maps to the
462
+ * same placeholder (e.g., "Dr. Muller" and "Dr. Muller"
463
+ * both become [PERSON_1]).
464
+ *
465
+ * Placeholder format: [LABEL_N] where LABEL is uppercase
466
+ * and N is a 1-based counter per label.
467
+ *
468
+ * @param ctx Pipeline context. Must be the same instance
469
+ * passed to `runPipeline` (or `findCoreferenceSpans`)
470
+ * so coreference placeholder links are preserved.
471
+ * Defaults to `defaultContext` for single-tenant usage.
472
+ */
473
+ declare const buildPlaceholderMap: (entities: Entity[], ctx?: PipelineContext) => Map<string, string>;
474
+ /**
475
+ * Apply redactions to the source text, replacing each
476
+ * confirmed entity span using the configured operator.
477
+ *
478
+ * Co-references are consistent: if the same text appears
479
+ * multiple times, all occurrences get the same placeholder.
480
+ *
481
+ * @param ctx Pipeline context. Must be the same instance
482
+ * passed to `runPipeline` (or `findCoreferenceSpans`)
483
+ * so coreference placeholder links are preserved.
484
+ * Defaults to `defaultContext` for single-tenant usage.
485
+ */
486
+ declare const redactText: (fullText: string, entities: Entity[], config?: OperatorConfig, ctx?: PipelineContext) => RedactionResult;
487
+ /**
488
+ * Serialize the redaction key to JSON for export.
489
+ * Includes operator metadata so the export is self-describing.
490
+ */
491
+ declare const exportRedactionKey: (redactionMap: Map<string, string>, operatorMap: Map<string, OperatorType>) => string;
492
+ /**
493
+ * De-anonymise text using a redaction key.
494
+ * Replaces placeholders back with original values.
495
+ * Only works for reversible operators (replace).
496
+ */
497
+ declare const deanonymise: (redactedText: string, redactionMap: Map<string, string>) => string;
498
+ //#endregion
499
+ //#region src/operators.d.ts
500
+ declare const OPERATOR_REGISTRY: {
501
+ readonly replace: AnonymisationOperator;
502
+ readonly redact: AnonymisationOperator;
503
+ };
504
+ /**
505
+ * Default operator config: replace for all labels.
506
+ * Preserves existing pipeline behaviour.
507
+ */
508
+ declare const DEFAULT_OPERATOR_CONFIG: OperatorConfig;
509
+ /**
510
+ * Resolve the operator for a label, falling back to "replace".
511
+ */
512
+ declare const resolveOperator: (config: OperatorConfig, label: string) => OperatorType;
513
+ //#endregion
514
+ //#region src/detectors/legal-forms.d.ts
515
+ /**
516
+ * Build legal form regex pattern strings.
517
+ * Returns an array of regex strings for the unified
518
+ * TextSearch builder. Empty if data package is not
519
+ * installed.
520
+ */
521
+ declare const buildLegalFormPatterns: () => Promise<string[]>;
522
+ /**
523
+ * Process legal form matches from the unified search.
524
+ * Receives all matches; filters to the legal forms
525
+ * slice via sliceStart/sliceEnd.
526
+ */
527
+ declare const processLegalFormMatches: (allMatches: Match[], sliceStart: number, sliceEnd: number, fullText?: string) => Entity[];
528
+ //#endregion
529
+ //#region src/detectors/triggers.d.ts
530
+ /**
531
+ * Build trigger patterns and rules from data configs.
532
+ * Returns string[] for the unified TextSearch
533
+ * builder and the parallel rules array.
534
+ */
535
+ declare const buildTriggerPatterns: () => Promise<{
536
+ patterns: string[];
537
+ rules: TriggerRule[];
538
+ }>;
539
+ /**
540
+ * Process trigger matches from the unified search.
541
+ * Receives all matches; filters to the trigger slice
542
+ * via sliceStart/sliceEnd. Uses fullText for value
543
+ * extraction (the unified search runs on lowercased
544
+ * text, but extraction needs original casing).
545
+ */
546
+ declare const processTriggerMatches: (allMatches: Match[], sliceStart: number, sliceEnd: number, fullText: string, rules: readonly TriggerRule[]) => Entity[];
547
+ //#endregion
548
+ //#region src/detectors/address-seeds.d.ts
549
+ /**
550
+ * Build street type patterns for the unified search.
551
+ * Returns string[] for the unified TextSearch
552
+ * builder. Empty if data package is not installed.
553
+ */
554
+ declare const buildStreetTypePatterns: () => Promise<string[]>;
555
+ /**
556
+ * Process address seeds from the unified search.
557
+ * Receives all matches; filters to the street types
558
+ * slice via sliceStart/sliceEnd. Uses fullText and
559
+ * existingEntities for seed collection, clustering,
560
+ * expansion, and scoring.
561
+ *
562
+ * Runs as a post-processor after all other detectors,
563
+ * using their output as seed sources.
564
+ */
565
+ declare const processAddressSeeds: (allMatches: Match[], sliceStart: number, sliceEnd: number, fullText: string, existingEntities: Entity[]) => Promise<Entity[]>;
566
+ //#endregion
567
+ //#region src/detectors/gazetteer.d.ts
568
+ /**
569
+ * Build TextSearch-compatible patterns from gazetteer
570
+ * entries. Returns:
571
+ * - Exact literal patterns for all terms
572
+ * - Fuzzy patterns (distance: 2) for terms >= 4 chars
573
+ * - Parallel metadata arrays for post-processing
574
+ *
575
+ * Patterns are ordered: all exact first, then all
576
+ * fuzzy. The isFuzzy array marks which are which.
577
+ */
578
+ declare const buildGazetteerPatterns: (entries: GazetteerEntry[]) => {
579
+ patterns: PatternEntry[];
580
+ data: GazetteerData;
581
+ };
582
+ /**
583
+ * Process gazetteer matches from the unified literal
584
+ * search. Receives all matches; filters to the
585
+ * gazetteer slice via sliceStart/sliceEnd.
586
+ *
587
+ * Exact matches get score 0.9; fuzzy matches get
588
+ * 0.85. Fuzzy matches that overlap an exact match
589
+ * are dropped.
590
+ *
591
+ * For exact matches, attempts prefix extension for
592
+ * legal suffixes ("a.s.", "GmbH", "s.r.o." after
593
+ * the matched term).
594
+ */
595
+ declare const processGazetteerMatches: (allMatches: Match[], sliceStart: number, sliceEnd: number, fullText: string, data: GazetteerData) => Entity[];
596
+ //#endregion
597
+ //#region src/detectors/coreference.d.ts
598
+ type DefinedTerm = {
599
+ alias: string;
600
+ label: string; /** Position of the definition in the source text */
601
+ definitionStart: number; /** Original entity text the alias refers to */
602
+ sourceText: string;
603
+ };
604
+ declare const extractDefinedTerms: (fullText: string, entities: Entity[], ctx?: PipelineContext) => Promise<DefinedTerm[]>;
605
+ /**
606
+ * Find all occurrences of defined-term aliases in the
607
+ * full text. Returns Entity spans for each match.
608
+ *
609
+ * Respects word boundaries: "Kupující" must not match
610
+ * inside "Kupujícímu". A match is valid only if the
611
+ * character before the start and after the end are NOT
612
+ * word characters (letter/digit).
613
+ *
614
+ * Populates `ctx.corefSourceMap` with entries linking
615
+ * each coref entity to its source entity text, for
616
+ * consistent placeholder numbering.
617
+ */
618
+ declare const findCoreferenceSpans: (fullText: string, terms: DefinedTerm[], ctx?: PipelineContext) => Entity[];
619
+ //#endregion
620
+ //#region src/detectors/org-propagation.d.ts
621
+ /**
622
+ * After the main detection pass, collect organization
623
+ * entities with a legal form suffix, strip the suffix
624
+ * to get the base name, and re-scan the full text for
625
+ * bare mentions of that base name. Returns new entities
626
+ * for occurrences not already covered.
627
+ */
628
+ declare const propagateOrgNames: (entities: Entity[], fullText: string) => Entity[];
629
+ //#endregion
630
+ //#region src/detectors/names.d.ts
631
+ /**
632
+ * Load name corpus data from per-language dictionary
633
+ * files and legacy config files. Merges all sources.
634
+ *
635
+ * Safe to call multiple times; only loads once per
636
+ * context. Must be called before detectNameCorpus or
637
+ * the getNameCorpus*() accessors are used.
638
+ */
639
+ declare const initNameCorpus: (ctx?: PipelineContext) => Promise<void>;
640
+ /**
641
+ * Detect person names by looking up tokens against the
642
+ * name corpus, then chaining adjacent name-like tokens.
643
+ *
644
+ * Requires initNameCorpus() to have been called first.
645
+ * If not initialized, returns an empty array.
646
+ *
647
+ * Scoring:
648
+ * TITLE + NAME/SURNAME → 0.95
649
+ * NAME + NAME/SURNAME → 0.9
650
+ * SURNAME + NAME/SURNAME → 0.9
651
+ * NAME + CAPITALIZED → 0.7
652
+ * ABBREVIATION + NAME → 0.7
653
+ * Standalone NAME → 0.5 (low confidence)
654
+ * Standalone SURNAME → skip (too ambiguous)
655
+ */
656
+ declare const detectNameCorpus: (fullText: string, ctx?: PipelineContext) => Entity[];
657
+ //#endregion
658
+ //#region src/unified-search.d.ts
659
+ type UnifiedResult = {
660
+ /** All matches from both instances combined. */regexMatches: Match[];
661
+ literalMatches: Match[];
662
+ };
663
+ declare const runUnifiedSearch: (instance: UnifiedSearchInstance, fullText: string) => UnifiedResult;
664
+ //#endregion
665
+ //#region src/regions.d.ts
666
+ /**
667
+ * Geographic regions and country code mappings for
668
+ * scoping deny list dictionaries.
669
+ */
670
+ declare const REGIONS: {
671
+ readonly Global: null;
672
+ readonly International: null;
673
+ readonly Europe: readonly ["AL", "AD", "AT", "BE", "BA", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE", "IT", "XK", "LV", "LI", "LT", "LU", "MD", "ME", "MK", "MT", "MC", "NL", "NO", "PL", "PT", "RO", "RS", "SK", "SI", "ES", "SE", "CH", "UA", "GB"];
674
+ readonly Americas: readonly ["US", "CA", "MX", "BR", "AR", "CL", "CO", "PE", "EC", "VE", "UY", "PY", "BO", "CR", "PA", "DO", "GT", "HN", "SV", "NI", "CU"];
675
+ readonly AsiaPacific: readonly ["AU", "NZ", "JP", "KR", "CN", "TW", "SG", "MY", "TH", "VN", "PH", "ID", "IN", "PK", "BD", "LK", "NP", "HK", "MO"];
676
+ readonly MENA: readonly ["AE", "SA", "IL", "TR", "EG", "JO", "LB", "IQ", "IR", "QA", "KW", "BH", "OM", "MA", "TN", "DZ", "LY", "SY", "YE", "PS"];
677
+ readonly SubSaharanAfrica: readonly ["ZA", "NG", "KE", "GH", "TZ", "ET", "SN", "CI", "CM", "UG", "RW", "MZ", "AO", "ZW", "BW", "NA", "MU"];
678
+ readonly EU: readonly ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"];
679
+ readonly DACH: readonly ["DE", "AT", "CH"];
680
+ readonly Nordics: readonly ["DK", "SE", "NO", "FI", "IS"];
681
+ readonly CEE: readonly ["CZ", "SK", "PL", "HU", "RO", "BG", "HR", "SI", "LT", "LV", "EE"];
682
+ readonly Anglosphere: readonly ["GB", "US", "CA", "AU", "NZ", "IE"];
683
+ readonly Benelux: readonly ["BE", "NL", "LU"];
684
+ readonly GulfStates: readonly ["AE", "SA", "QA", "KW", "BH", "OM"];
685
+ readonly SouthAsia: readonly ["IN", "PK", "BD", "LK", "NP"];
686
+ readonly EastAsia: readonly ["CN", "JP", "KR", "TW"];
687
+ readonly SoutheastAsia: readonly ["SG", "MY", "TH", "VN", "PH", "ID"];
688
+ readonly Oceania: readonly ["AU", "NZ"];
689
+ };
690
+ type RegionId = keyof typeof REGIONS;
691
+ type RegionArrays = { [K in RegionId]: (typeof REGIONS)[K] };
692
+ type NonNullRegion = { [K in RegionId as RegionArrays[K] extends null ? never : K]: RegionArrays[K] };
693
+ type CountryCode = NonNullRegion[keyof NonNullRegion][number];
694
+ /**
695
+ * Expand region names to country codes and merge with
696
+ * explicit country codes. Returns null when both inputs
697
+ * are empty/undefined (meaning "match all countries").
698
+ */
699
+ declare const resolveCountries: (regions?: string[], countries?: string[]) => Set<string> | null;
700
+ //#endregion
701
+ //#region src/filters/false-positives.d.ts
702
+ /**
703
+ * Filter out entities that are likely false positives:
704
+ * template placeholders, clause/section numbers,
705
+ * standalone years, and generic legal role terms.
706
+ *
707
+ * Runs as a post-processing step after all detection
708
+ * layers have merged.
709
+ */
710
+ declare const filterFalsePositives: (entities: Entity[], ctx?: PipelineContext) => Entity[];
711
+ //#endregion
712
+ //#region src/filters/confidence-boost.d.ts
713
+ /**
714
+ * Boost confidence of near-miss NER entities that appear
715
+ * near high-confidence detections (regex, trigger phrase).
716
+ *
717
+ * If an NER entity scored between (threshold - 0.15) and
718
+ * threshold, count how many confirmed entities exist within
719
+ * a 150-char window. Add +0.05 per co-located entity.
720
+ * If the boosted score crosses the threshold, include it.
721
+ *
722
+ * Only mutates score on near-miss entities; high-confidence
723
+ * entities pass through unchanged.
724
+ */
725
+ declare const boostNearMissEntities: (entities: Entity[], threshold: number) => Entity[];
726
+ //#endregion
727
+ //#region src/filters/hotword-rules.d.ts
728
+ type HotwordRule = {
729
+ hotwords: string[];
730
+ targetLabels: string[];
731
+ scoreAdjustment: number;
732
+ reclassifyTo?: string;
733
+ proximityBefore: number;
734
+ proximityAfter: number;
735
+ };
736
+ /**
737
+ * Load hotword rules from the data package.
738
+ * Safe to call multiple times; subsequent calls
739
+ * are no-ops.
740
+ */
741
+ declare const initHotwordRules: () => Promise<void>;
742
+ /**
743
+ * Apply hotword context rules to detected entities.
744
+ *
745
+ * Scans `fullText` once with a single AC automaton
746
+ * for all hotwords across all rules, then checks
747
+ * proximity to each entity. Distance-decayed
748
+ * adjustment: closer hotwords give a stronger boost.
749
+ *
750
+ * Returns a new array; input entities are not mutated.
751
+ */
752
+ declare const applyHotwordRules: (entities: Entity[], fullText: string) => Entity[];
753
+ //#endregion
754
+ //#region src/filters/zone-classifier.d.ts
755
+ type DocumentZone = "header" | "signature" | "body" | "table";
756
+ type ZoneSpan = {
757
+ zone: DocumentZone;
758
+ start: number;
759
+ end: number;
760
+ };
761
+ /**
762
+ * Additive score adjustments per document zone.
763
+ * Header and signature blocks are dense with PII;
764
+ * tables often contain structured identifying data.
765
+ */
766
+ declare const ZONE_SCORE_ADJUSTMENTS: {
767
+ readonly header: 0.1;
768
+ readonly signature: 0.15;
769
+ readonly body: 0;
770
+ readonly table: 0.05;
771
+ };
772
+ /**
773
+ * Ensure config data is loaded. Call once before
774
+ * classifyZones. Safe to call multiple times.
775
+ */
776
+ declare const initZoneClassifier: (ctx?: PipelineContext) => Promise<void>;
777
+ /**
778
+ * Classify a document into zones based on
779
+ * structural heuristics. Zones are non-overlapping
780
+ * and cover the entire text.
781
+ *
782
+ * Must call `initZoneClassifier()` first.
783
+ */
784
+ declare const classifyZones: (fullText: string, ctx?: PipelineContext) => ZoneSpan[];
785
+ /**
786
+ * Apply zone-based score adjustments to entities.
787
+ * Entities in header/signature/table zones get a
788
+ * small additive boost reflecting the higher PII
789
+ * density in those regions.
790
+ *
791
+ * Returns a new array; does not mutate inputs.
792
+ */
793
+ declare const applyZoneAdjustments: (entities: Entity[], zones: ZoneSpan[]) => Entity[];
794
+ //#endregion
795
+ //#region src/gliner/types.d.ts
796
+ /**
797
+ * GLiNER inference types.
798
+ *
799
+ * Forked from gliner@0.0.19 (MIT), stripped to runtime-
800
+ * agnostic core. Original: github.com/Ingvarstep/GLiNER.js
801
+ */
802
+ type EntityResult = {
803
+ spanText: string;
804
+ start: number;
805
+ end: number;
806
+ label: string;
807
+ score: number;
808
+ };
809
+ /**
810
+ * Raw inference output: per-batch array of
811
+ * [spanText, start, end, label, score] tuples.
812
+ */
813
+ type RawInferenceResult = [string, number, number, string, number][][];
814
+ //#endregion
815
+ //#region src/gliner/decoder.d.ts
816
+ /**
817
+ * Decode span-level model logits into entity results.
818
+ */
819
+ declare const decodeSpans: (batchSize: number, inputLength: number, maxWidth: number, numEntities: number, texts: string[], batchIds: number[], batchWordsStartIdx: number[][], batchWordsEndIdx: number[][], idToClass: Record<number, string>, modelOutput: ArrayLike<number>, flatNer: boolean, threshold: number, multiLabel: boolean) => RawInferenceResult;
820
+ //#endregion
821
+ //#region src/gliner/token-decoder.d.ts
822
+ /**
823
+ * Decode token-level BIO logits into entity spans.
824
+ *
825
+ * For each word, checks if the B(egin) logit for any class
826
+ * exceeds the threshold. If so, extends the span by consuming
827
+ * subsequent I(nside) tokens of the same class.
828
+ */
829
+ declare const decodeTokenSpans: (batchSize: number, numWords: number, numEntities: number, texts: string[], batchIds: number[], batchWordsStartIdx: number[][], batchWordsEndIdx: number[][], idToClass: Record<number, string>, modelOutput: ArrayLike<number>, threshold: number) => RawInferenceResult;
830
+ //#endregion
831
+ //#region src/gliner/processor.d.ts
832
+ /** Tokenize text into words with character offsets. */
833
+ declare const tokenizeText: (text: string) => [words: string[], starts: number[], ends: number[]];
834
+ /** Prepare a complete batch for ONNX inference. */
835
+ declare const prepareBatch: (tokenizer: Tokenizer, texts: string[], entities: string[], maxWidth: number) => {
836
+ inputsIds: number[][];
837
+ attentionMasks: number[][];
838
+ wordsMasks: number[][];
839
+ textLengths: number[];
840
+ spanIdxs: number[][][];
841
+ spanMasks: boolean[][];
842
+ idToClass: Record<number, string>;
843
+ batchTokens: string[][];
844
+ batchWordsStartIdx: number[][];
845
+ batchWordsEndIdx: number[][];
846
+ };
847
+ //#endregion
848
+ //#region src/util/chunker.d.ts
849
+ /**
850
+ * Split text into overlapping chunks for GLiNER's
851
+ * ~512 token context window. Character-based splitting
852
+ * (rough approximation of token limits).
853
+ *
854
+ * Tries to break at sentence boundaries when possible.
855
+ */
856
+ declare const chunkText: (text: string) => string[];
857
+ /**
858
+ * Compute the byte offset of each chunk within the
859
+ * original document text.
860
+ */
861
+ declare const computeChunkOffsets: (fullText: string, chunks: string[]) => number[];
862
+ /**
863
+ * Merge entities from overlapping chunks back to
864
+ * document-level offsets. Deduplicates entities that
865
+ * appear in overlap regions (keeps highest score).
866
+ *
867
+ * Dedup invariant: each incoming entity is compared
868
+ * against the highest-scored same-label near-dup in
869
+ * its proximity window. If it loses, it is dropped.
870
+ * This does NOT guarantee that all pairwise near-dup
871
+ * relationships in the output are resolved; a lower-
872
+ * scored entity can survive if the bridging entity
873
+ * that would have replaced it was itself dropped by
874
+ * a higher-scored match.
875
+ *
876
+ * Uses a reverse-scan over the sorted merged array
877
+ * so each entity only compares against nearby
878
+ * predecessors — O(n * w) average where w is the max
879
+ * entities per POSITION_THRESHOLD window, O(n²) worst
880
+ * case when replacements dominate (splice is O(n)).
881
+ */
882
+ declare const mergeChunkEntities: (chunkOffsets: number[], chunkResults: Entity[][]) => Entity[];
883
+ //#endregion
884
+ //#region src/util/levenshtein.d.ts
885
+ /**
886
+ * Compute the Levenshtein edit distance between two
887
+ * strings. O(n*m) time, O(min(n,m)) space using a
888
+ * single-row DP approach.
889
+ */
890
+ declare const levenshtein: (rawA: string, rawB: string) => number;
891
+ //#endregion
892
+ //#region src/util/normalize.d.ts
893
+ /**
894
+ * Normalize typographic variants for search matching.
895
+ *
896
+ * Legal documents (especially Czech/German) use
897
+ * non-breaking spaces, smart quotes, and en/em dashes
898
+ * that differ from their ASCII equivalents. Since all
899
+ * replacements are same-length (single code unit →
900
+ * single code unit), character offsets remain valid.
901
+ *
902
+ * Lives here (application layer) rather than in the
903
+ * AC library: what to normalize is domain-specific.
904
+ *
905
+ * Uses a char-code lookup (`Map<number, number>`) and
906
+ * `Uint16Array` instead of 7 sequential `replaceAll`
907
+ * calls. For a 50 KB document this eliminates ~350 KB
908
+ * of intermediate string allocations.
909
+ *
910
+ * When no replaceable characters are present (common
911
+ * for plain-text inputs), a fast-path scan returns the
912
+ * original string without any allocation. When special
913
+ * characters exist, the string is scanned twice: once
914
+ * to detect, once to build the replacement array.
915
+ */
916
+ declare const normalizeForSearch: (text: string) => string;
917
+ //#endregion
918
+ export { type AnonymisationOperator, CURRENCY_PATTERN_META, type CountryCode, DATE_PATTERN_META, DEFAULT_ENTITY_LABELS, DEFAULT_OPERATOR_CONFIG, DETECTION_SOURCES, DETECTOR_PRIORITY, type DefinitionPattern, type DenyListCategory, type DenyListData, type DetectionSource, type DocumentZone, type Entity, type EntityResult, type GazetteerData, type GazetteerEntry, type HotwordRule, type NameCorpusData, type NerInferenceFn, OPERATOR_REGISTRY, OPERATOR_TYPES, type OperatorConfig, type OperatorType, type PipelineConfig, type PipelineContext, type PipelineOptions, REGEX_META, REGEX_PATTERNS, REGIONS, type RawInferenceResult, type RedactionResult, type RegexMeta, type RegionId, type ReviewDecision, type ReviewedEntity, type TriggerExtension, type TriggerGroupConfig, type TriggerRule, type TriggerStrategy, type TriggerValidation, type UnifiedResult, type UnifiedSearchInstance, ZONE_SCORE_ADJUSTMENTS, type ZoneSpan, applyHotwordRules, applyZoneAdjustments, boostNearMissEntities, buildDenyList, buildGazetteerPatterns, buildLegalFormPatterns, buildPlaceholderMap, buildStreetTypePatterns, buildTriggerPatterns, buildUnifiedSearch, chunkText, classifyZones, computeChunkOffsets, corefKey, createPipelineContext, deanonymise, decodeSpans, decodeTokenSpans, detectNameCorpus, ensureDenyListData, exportRedactionKey, extractDefinedTerms, filterFalsePositives, findCoreferenceSpans, getCurrencyPatterns, getDatePatterns, initHotwordRules, initNameCorpus, initZoneClassifier, levenshtein, mergeAndDedup, mergeChunkEntities, normalizeForSearch, prepareBatch, processAddressSeeds, processDenyListMatches, processGazetteerMatches, processLegalFormMatches, processRegexMatches, processTriggerMatches, propagateOrgNames, redactText, resolveCountries, resolveOperator, runPipeline, runUnifiedSearch, sanitizeEntities, tokenizeText };
919
+ //# sourceMappingURL=wasm.d.mts.map