euredact 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/NOTICE +11 -0
  2. package/README.md +455 -44
  3. package/dist/cjs/cache.js +2 -8
  4. package/dist/cjs/cloud/account.js +180 -0
  5. package/dist/cjs/cloud/batches.js +469 -0
  6. package/dist/cjs/cloud/client.js +124 -14
  7. package/dist/cjs/cloud/config.js +28 -2
  8. package/dist/cjs/cloud/errors.js +39 -2
  9. package/dist/cjs/cloud/index.js +16 -1
  10. package/dist/cjs/index.js +9 -1
  11. package/dist/cjs/platform.browser.js +98 -0
  12. package/dist/cjs/platform.js +140 -0
  13. package/dist/cjs/rules/bicGleif.js +57 -0
  14. package/dist/cjs/rules/bicRegistry.js +37 -11
  15. package/dist/cjs/rules/countries/index.js +83 -13
  16. package/dist/cjs/rules/cues.js +29 -9
  17. package/dist/cjs/rules/engine.js +14 -6
  18. package/dist/cjs/rules/suppressors.js +479 -26
  19. package/dist/cjs/rules/validators.js +50 -0
  20. package/dist/cjs/sdk.js +108 -14
  21. package/dist/cjs/types.js +4 -1
  22. package/dist/esm/cache.js +2 -8
  23. package/dist/esm/cloud/account.d.ts +155 -0
  24. package/dist/esm/cloud/account.js +175 -0
  25. package/dist/esm/cloud/batches.d.ts +166 -0
  26. package/dist/esm/cloud/batches.js +464 -0
  27. package/dist/esm/cloud/client.d.ts +83 -1
  28. package/dist/esm/cloud/client.js +119 -22
  29. package/dist/esm/cloud/config.d.ts +6 -0
  30. package/dist/esm/cloud/config.js +27 -2
  31. package/dist/esm/cloud/errors.d.ts +26 -1
  32. package/dist/esm/cloud/errors.js +35 -1
  33. package/dist/esm/cloud/index.d.ts +4 -2
  34. package/dist/esm/cloud/index.js +4 -2
  35. package/dist/esm/index.d.ts +2 -2
  36. package/dist/esm/index.js +1 -1
  37. package/dist/esm/platform.browser.d.ts +27 -0
  38. package/dist/esm/platform.browser.js +92 -0
  39. package/dist/esm/platform.d.ts +20 -0
  40. package/dist/esm/platform.js +101 -0
  41. package/dist/esm/rules/bicGleif.d.ts +4 -0
  42. package/dist/esm/rules/bicGleif.js +53 -0
  43. package/dist/esm/rules/bicRegistry.d.ts +25 -11
  44. package/dist/esm/rules/bicRegistry.js +36 -11
  45. package/dist/esm/rules/countries/index.js +83 -13
  46. package/dist/esm/rules/cues.d.ts +1 -1
  47. package/dist/esm/rules/cues.js +29 -9
  48. package/dist/esm/rules/engine.js +14 -6
  49. package/dist/esm/rules/suppressors.d.ts +1 -0
  50. package/dist/esm/rules/suppressors.js +478 -25
  51. package/dist/esm/rules/validators.d.ts +14 -0
  52. package/dist/esm/rules/validators.js +47 -0
  53. package/dist/esm/sdk.d.ts +77 -4
  54. package/dist/esm/sdk.js +108 -17
  55. package/dist/esm/types.d.ts +64 -6
  56. package/dist/esm/types.js +4 -1
  57. package/package.json +13 -4
@@ -90,7 +90,7 @@ export const CUES = [
90
90
  /(?<![A-Za-z0-9_])(?:phone|tel|telephone|téléphone|telefoon|telefon|telefono|teléfono|tlf|mobil|mobile|gsm|handy|sími|puh|móvil|cell|tālrunis|τηλέφωνο|τηλ)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
91
91
  // National identity number.
92
92
  [EntityType.NATIONAL_ID,
93
- /(?<![A-Za-z0-9_])(?:bsn|burgerservicenummer(?:\s*\(bsn\))?|personnummer|rijksregisternummer|nationaal\s*nummer|numéro\s*national|national\s*(?:id|number)|identiteitsnummer|henkilötunnus|kennitala|cpr(?:-?nummer)?|nif|nie|dni|rr|nn|nir|insz|niss|nis|matricule|ausweisnummer|personalausweis|osobní\s*číslo|ЕГН|EGN)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
93
+ /(?<![A-Za-z0-9_])(?:bsn(?!r)|burgerservicenummer(?:\s*\(bsn\))?|personnummer|rijksregisternummer|nationaal\s*nummer|numéro\s*national|national\s*(?:id|number)|identiteitsnummer|henkilötunnus|kennitala|cpr(?:-?nummer)?|nif|nie|dnie?(?![A-Za-z]|[^\x00-\x7F])|rr|nn|nir|insz|niss|nis|matricule|id\s*card(?:\s*(?:no|nr|number))?|ausweisnummer|personalausweis|osobní\s*číslo|ЕГН|EGN|dow[óo]d(?:u|em)?\s*osobist(?:y|ego|ym)|nr\s*dowodu|ahv(?:-?(?:nr|nummer))?|avs)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
94
94
  // Cyprus: "ID number" in both official languages, plus the Greek abbreviation
95
95
  // ΑΔΤ (Αστυνομική Ταυτότητα). Neither has a phone reading.
96
96
  [EntityType.NATIONAL_ID,
@@ -111,16 +111,16 @@ export const CUES = [
111
111
  /(?<![A-Za-z0-9_])(?:αφμ|α\.φ\.μ\.|steuerliche\s*identifikations-?nummer|steuer-?identifikations-?nummer|steuer-?id-?nr\.?|steuer-?id|steuernummer|st\.?-?nr|stnr|tin|finanzamt\s+ist|tax\s*(?:id|no|number)|daňové\s*číslo|numer\s*podatkowy)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
112
112
  // Health insurance: the insured person.
113
113
  [EntityType.HEALTH_INSURANCE,
114
- /(?<![A-Za-z0-9_])(?:versichertennummer|krankenversicherung|kvnr|kv-?nr|mutualité|ziekenfonds|aansluitingsnummer|medical\s*card(?:\s*(?:no|number))?)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
114
+ /(?<![A-Za-z0-9_])(?:versichertennummer|krankenversicherung|kvnr|kv-?nr|mutualité|ziekenfonds|aansluitingsnummer|medical\s*card(?:\s*(?:no|number))?|nhs(?:\s*(?:no|number))?)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
115
115
  // Healthcare provider: the clinician or practice, not the patient — the Dutch
116
116
  // AGB-code, the German LANR, the UK GMC number.
117
117
  [EntityType.HEALTHCARE_PROVIDER,
118
- /(?<![A-Za-z0-9_])(?:agb-?code|lanr|bsnr|gmc(?:\s*(?:no|number|reg))?|big-?nummer|zorgverlener(?:snummer)?)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
118
+ /(?<![A-Za-z0-9_])(?:agb-?code|lanr|bsnr|gmc(?:\s*(?:no|number|reg))?|big(?:-?registratie)?-?(?:nummer|nr)|zorgverlener(?:snummer)?|lebenslange\s*arztnummer|betriebsst[äa]ttennummer|rpps|adeli|pwz)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
119
119
  // Company / trade register. "Companies House" and "Company Registration
120
120
  // Number" are written out: the tail allows a run-on *or* one qualifier word,
121
121
  // never a second word, so a three-word label needs to be spelled.
122
122
  [EntityType.CHAMBER_OF_COMMERCE,
123
- /(?<![A-Za-z0-9_])(?:siren|siret|kbo|bce|kvk|ondernemingsnummer|ondernemingen\s+onder\s+nummer|kruispuntbank|numéro\s*d'entreprise|enterprise\s*number|handelsregister|companies\s*house(?:\s*(?:registration|reg|no|number))?|company\s*registration(?:\s*(?:number|no))?|company\s*(?:no|number|reg)|organisationsnummer|orgnr|i[čc]o|identifikační\s*číslo|ЕИК|eik|bulstat|cvr(?:-?nummer)?|virksomhedsnummer)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
123
+ /(?<![A-Za-z0-9_])(?:siren|siret|kbo|bce|kvk|ondernemingsnummer|ondernemingen\s+onder\s+nummer|kruispuntbank|numéro\s*d'entreprise|enterprise\s*number|handelsregister|companies\s*house(?:\s*(?:registration|reg|no|number))?|company\s*registration(?:\s*(?:number|no))?|company\s*(?:no|number|reg)|organisationsnummer|orgnr|i[čc]o|identifikační\s*číslo|ЕИК|eik|bulstat|cvr(?:-?nummer)?|virksomhedsnummer|regon|krs)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
124
124
  // VAT.
125
125
  [EntityType.VAT,
126
126
  /(?<![A-Za-z0-9_])(?:vat|btw|tva|ust|iva|moms|alv|mwst|dič|vsk)(?:(?:[A-Za-z0-9_.\-]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
@@ -130,6 +130,15 @@ export const CUES = [
130
130
  [EntityType.BANK_ACCOUNT,
131
131
  /(?<![A-Za-z0-9_])(?:sort\s*code|account\s*(?:no|number)|rekeningnummer|kontonummer|numéro\s*de\s*compte|sort-?code)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
132
132
  // Passport.
133
+ // Residence permit. No permit patterns exist, so a permit number was filed
134
+ // under whichever ID, passport or phone pattern fitted it (rules-engine#53).
135
+ // The tail admits the Belgian card category ("nr. B 565992336"), unmasked.
136
+ [EntityType.RESIDENCE_PERMIT,
137
+ /(?<![A-Za-z0-9_])(?:aufenthalts(?:titel|erlaubnis|karte|gestattung)|niederlassungs(?:erlaubnis|bewilligung)|verblijfs(?:vergunning|kaart|document|titel)|titre\s*de\s*s[ée]jour|carte\s*de\s*s[ée]jour|permis\s*de\s*s[ée]jour|carte\s*de\s*r[ée]sident|residence\s*(?:permit|card|document)|biometric\s*residence\s*permit|permesso\s*di\s*soggiorno|carta\s*di\s*soggiorno|kart(?:a|y|ę|ą)\s*pobytu(?:\s*cukr)?|naklejk(?:a|i|ę|ą)\s*wizow(?:a|ej|ą)|uppehållstillstånd|opholdstilladelse|oppholdstillatelse|oleskelulupa)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*(?:[A-Z]\s+)?$/i],
138
+ // Driving licence: Polish labels; the document number has the passport
139
+ // shape, so without this cue it lost the span to the passport rule (rules-engine#77).
140
+ [EntityType.DRIVERS_LICENSE,
141
+ /(?<![A-Za-z0-9_])(?:(?:nr\s*dokumentu\s*)?(?:prawo|prawa|prawem)\s*jazdy|numer\s*blankietu\s*prawa\s*jazdy)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
133
142
  [EntityType.PASSPORT,
134
143
  /(?<![A-Za-z0-9_])(?:passport(?:\s*(?:no|number))?|paspoort(?:nummer)?|reisepass(?:nummer)?|passeport|passnummer)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
135
144
  // BIC / SWIFT.
@@ -146,7 +155,7 @@ export const CUES = [
146
155
  // only thing that identifies one, which is why the engine emits this type
147
156
  // from here and nowhere else.
148
157
  [EntityType.INTERNAL_ID,
149
- /(?<![A-Za-z0-9_])(?:medarbejdernummer|ansattnummer|anställningsnummer|personalnummer|personeelsnummer|matricule\s*salarié|employee\s*(?:no|number|id)|badge(?:\s*(?:no|number))?|betriebsstätten(?:nr|nummer)|grundstücks(?:nr|nummer)|dossier-?(?:nr|nummer)|aktenzeichen|osobní\s*(?:číslo\s*)?zaměstnance)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
158
+ /(?<![A-Za-z0-9_])(?:medarbejdernummer|ansattnummer|anställningsnummer|personalnummer|personeelsnummer|matricule\s*salarié|employee\s*(?:no|number|id)|badge(?:\s*(?:no|number))?|betriebsstätten(?:nr|nummer)|grundstücks(?:nr|nummer)|dossier-?(?:nr|nummer)|aktenzeichen|schade(?:claim|nummer|dossier)|schadens?-?(?:nummer|nr)|num[ée]ro\s*de\s*sinistre|numero\s*(?:di\s*)?sinistro|n[úu]mero\s*de\s*siniestro|skade(?:nummer|sag)|claim\s*(?:no|number|id)|collar|osobní\s*(?:číslo\s*)?zaměstnance)(?:(?:[A-Za-z0-9_]|[^\x00-\x7F])*|\s+[^\s:.\-\d,;()\/]{2,20})\s*\)?\s*[:.\-]*\s*$/i],
150
159
  // Credential. Typed SECRET because that is the type this SDK has; the
151
160
  // training pipeline's report asks for CREDENTIAL, which does not exist here.
152
161
  [EntityType.SECRET,
@@ -192,6 +201,13 @@ const RETYPABLE = new Set([
192
201
  * "Betriebsstättennr. 260326822" both landed, each reported as Czech.
193
202
  */
194
203
  const RETYPABLE_UNCORROBORATED = new Set([EntityType.NATIONAL_ID]);
204
+ /**
205
+ * Retypable to RESIDENCE_PERMIT by a permit label touching the span, whatever
206
+ * the country support: a German eAT number fits the identity-card pattern and a
207
+ * Dutch permit number the passport pattern (rules-engine#53). Not a Spanish
208
+ * NIE, which the canon keeps NATIONAL_ID.
209
+ */
210
+ const RETYPABLE_BY_PERMIT_LABEL = new Set([EntityType.NATIONAL_ID, EntityType.PASSPORT]);
195
211
  /**
196
212
  * Types a label can assert on its own.
197
213
  *
@@ -205,7 +221,7 @@ export const CUE_TARGETS = new Set([
205
221
  EntityType.HEALTH_INSURANCE, EntityType.HEALTHCARE_PROVIDER,
206
222
  EntityType.CHAMBER_OF_COMMERCE, EntityType.VAT, EntityType.POSTAL_CODE,
207
223
  EntityType.PASSPORT, EntityType.INTERNAL_ID, EntityType.BANK_ACCOUNT,
208
- EntityType.SECRET,
224
+ EntityType.SECRET, EntityType.RESIDENCE_PERMIT,
209
225
  ]);
210
226
  /**
211
227
  * Which of those a label may also *rescue* — re-admit after its checksum
@@ -242,13 +258,17 @@ export const RESCUE_TARGETS = new Set([
242
258
  * `countryScore` is the document's support for the country the winning pattern
243
259
  * came from; see RETYPABLE_UNCORROBORATED.
244
260
  */
245
- export function retypedBy(text, start, entityType, countryScore) {
261
+ export function retypedBy(text, start, entityType, countryScore, country = null) {
246
262
  const type = String(entityType);
247
- if (!RETYPABLE.has(type)
248
- && !(RETYPABLE_UNCORROBORATED.has(type) && countryScore === 0))
263
+ const general = RETYPABLE.has(type)
264
+ || (RETYPABLE_UNCORROBORATED.has(type) && countryScore === 0);
265
+ const byPermit = RETYPABLE_BY_PERMIT_LABEL.has(type) && country !== "ES";
266
+ if (!general && !byPermit)
249
267
  return null;
250
268
  const cued = cuedType(text, start);
251
269
  if (cued === null || cued === entityType || !CUE_TARGETS.has(String(cued)))
252
270
  return null;
271
+ if (!general && cued !== EntityType.RESIDENCE_PERMIT)
272
+ return null;
253
273
  return cued;
254
274
  }
@@ -242,12 +242,20 @@ export class RuleEngine {
242
242
  if (this.matcher.validate(m)) {
243
243
  validated.push({ match: m, hasValidValidator: m.patternDef.validator !== null });
244
244
  }
245
- else if (m.patternDef.validator !== null && !m.patternDef.requiresContext) {
246
- failedSpans.push({ start: m.start, end: m.end, code: m.countryCode,
247
- etype: String(m.patternDef.entityType) });
248
- if (RESCUE_TARGETS.has(String(m.patternDef.entityType))
249
- && cuedType(text, m.start) === m.patternDef.entityType)
245
+ else if (m.patternDef.validator !== null) {
246
+ const cued = RESCUE_TARGETS.has(String(m.patternDef.entityType))
247
+ && cuedType(text, m.start) === m.patternDef.entityType;
248
+ if (!m.patternDef.requiresContext) {
249
+ failedSpans.push({ start: m.start, end: m.end, code: m.countryCode,
250
+ etype: String(m.patternDef.entityType) });
251
+ if (cued)
252
+ rescued.push(m);
253
+ }
254
+ else if (cued) {
255
+ // A label-gated pattern is rescued too, but only by a cue touching the
256
+ // value; it casts no demotion zone (rules-engine#75).
250
257
  rescued.push(m);
258
+ }
251
259
  }
252
260
  }
253
261
  // Evidence pass: work out which countries this document belongs to, from
@@ -457,7 +465,7 @@ export class RuleEngine {
457
465
  // the value is filed as a Cypriot identity number. Dropping it also clears
458
466
  // outOfScope, which would otherwise claim the detection belongs to a
459
467
  // country the caller did not declare while naming no country at all.
460
- const retyped = retypedBy(text, match.start, entityType, countryScores.get(match.countryCode) ?? 0);
468
+ const retyped = retypedBy(text, match.start, entityType, countryScores.get(match.countryCode) ?? 0, match.countryCode);
461
469
  if (retyped !== null) {
462
470
  entityType = retyped;
463
471
  country = null;
@@ -6,6 +6,7 @@ export interface RawMatch {
6
6
  patternDef: PatternDef;
7
7
  countryCode: string;
8
8
  }
9
+ export declare const MONTH_NAMES: string;
9
10
  /**
10
11
  * Per-document scratch space for suppressors that would otherwise re-derive the
11
12
  * same whole-document facts for every candidate.