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
@@ -95,7 +95,7 @@ exports.CUES = [
95
95
  /(?<![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],
96
96
  // National identity number.
97
97
  [types_js_1.EntityType.NATIONAL_ID,
98
- /(?<![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],
98
+ /(?<![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],
99
99
  // Cyprus: "ID number" in both official languages, plus the Greek abbreviation
100
100
  // ΑΔΤ (Αστυνομική Ταυτότητα). Neither has a phone reading.
101
101
  [types_js_1.EntityType.NATIONAL_ID,
@@ -116,16 +116,16 @@ exports.CUES = [
116
116
  /(?<![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],
117
117
  // Health insurance: the insured person.
118
118
  [types_js_1.EntityType.HEALTH_INSURANCE,
119
- /(?<![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],
119
+ /(?<![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],
120
120
  // Healthcare provider: the clinician or practice, not the patient — the Dutch
121
121
  // AGB-code, the German LANR, the UK GMC number.
122
122
  [types_js_1.EntityType.HEALTHCARE_PROVIDER,
123
- /(?<![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],
123
+ /(?<![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],
124
124
  // Company / trade register. "Companies House" and "Company Registration
125
125
  // Number" are written out: the tail allows a run-on *or* one qualifier word,
126
126
  // never a second word, so a three-word label needs to be spelled.
127
127
  [types_js_1.EntityType.CHAMBER_OF_COMMERCE,
128
- /(?<![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],
128
+ /(?<![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],
129
129
  // VAT.
130
130
  [types_js_1.EntityType.VAT,
131
131
  /(?<![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],
@@ -135,6 +135,15 @@ exports.CUES = [
135
135
  [types_js_1.EntityType.BANK_ACCOUNT,
136
136
  /(?<![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],
137
137
  // Passport.
138
+ // Residence permit. No permit patterns exist, so a permit number was filed
139
+ // under whichever ID, passport or phone pattern fitted it (rules-engine#53).
140
+ // The tail admits the Belgian card category ("nr. B 565992336"), unmasked.
141
+ [types_js_1.EntityType.RESIDENCE_PERMIT,
142
+ /(?<![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],
143
+ // Driving licence: Polish labels; the document number has the passport
144
+ // shape, so without this cue it lost the span to the passport rule (rules-engine#77).
145
+ [types_js_1.EntityType.DRIVERS_LICENSE,
146
+ /(?<![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],
138
147
  [types_js_1.EntityType.PASSPORT,
139
148
  /(?<![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],
140
149
  // BIC / SWIFT.
@@ -151,7 +160,7 @@ exports.CUES = [
151
160
  // only thing that identifies one, which is why the engine emits this type
152
161
  // from here and nowhere else.
153
162
  [types_js_1.EntityType.INTERNAL_ID,
154
- /(?<![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],
163
+ /(?<![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],
155
164
  // Credential. Typed SECRET because that is the type this SDK has; the
156
165
  // training pipeline's report asks for CREDENTIAL, which does not exist here.
157
166
  [types_js_1.EntityType.SECRET,
@@ -197,6 +206,13 @@ const RETYPABLE = new Set([
197
206
  * "Betriebsstättennr. 260326822" both landed, each reported as Czech.
198
207
  */
199
208
  const RETYPABLE_UNCORROBORATED = new Set([types_js_1.EntityType.NATIONAL_ID]);
209
+ /**
210
+ * Retypable to RESIDENCE_PERMIT by a permit label touching the span, whatever
211
+ * the country support: a German eAT number fits the identity-card pattern and a
212
+ * Dutch permit number the passport pattern (rules-engine#53). Not a Spanish
213
+ * NIE, which the canon keeps NATIONAL_ID.
214
+ */
215
+ const RETYPABLE_BY_PERMIT_LABEL = new Set([types_js_1.EntityType.NATIONAL_ID, types_js_1.EntityType.PASSPORT]);
200
216
  /**
201
217
  * Types a label can assert on its own.
202
218
  *
@@ -210,7 +226,7 @@ exports.CUE_TARGETS = new Set([
210
226
  types_js_1.EntityType.HEALTH_INSURANCE, types_js_1.EntityType.HEALTHCARE_PROVIDER,
211
227
  types_js_1.EntityType.CHAMBER_OF_COMMERCE, types_js_1.EntityType.VAT, types_js_1.EntityType.POSTAL_CODE,
212
228
  types_js_1.EntityType.PASSPORT, types_js_1.EntityType.INTERNAL_ID, types_js_1.EntityType.BANK_ACCOUNT,
213
- types_js_1.EntityType.SECRET,
229
+ types_js_1.EntityType.SECRET, types_js_1.EntityType.RESIDENCE_PERMIT,
214
230
  ]);
215
231
  /**
216
232
  * Which of those a label may also *rescue* — re-admit after its checksum
@@ -247,13 +263,17 @@ exports.RESCUE_TARGETS = new Set([
247
263
  * `countryScore` is the document's support for the country the winning pattern
248
264
  * came from; see RETYPABLE_UNCORROBORATED.
249
265
  */
250
- function retypedBy(text, start, entityType, countryScore) {
266
+ function retypedBy(text, start, entityType, countryScore, country = null) {
251
267
  const type = String(entityType);
252
- if (!RETYPABLE.has(type)
253
- && !(RETYPABLE_UNCORROBORATED.has(type) && countryScore === 0))
268
+ const general = RETYPABLE.has(type)
269
+ || (RETYPABLE_UNCORROBORATED.has(type) && countryScore === 0);
270
+ const byPermit = RETYPABLE_BY_PERMIT_LABEL.has(type) && country !== "ES";
271
+ if (!general && !byPermit)
254
272
  return null;
255
273
  const cued = cuedType(text, start);
256
274
  if (cued === null || cued === entityType || !exports.CUE_TARGETS.has(String(cued)))
257
275
  return null;
276
+ if (!general && cued !== types_js_1.EntityType.RESIDENCE_PERMIT)
277
+ return null;
258
278
  return cued;
259
279
  }
@@ -280,12 +280,20 @@ class RuleEngine {
280
280
  if (this.matcher.validate(m)) {
281
281
  validated.push({ match: m, hasValidValidator: m.patternDef.validator !== null });
282
282
  }
283
- else if (m.patternDef.validator !== null && !m.patternDef.requiresContext) {
284
- failedSpans.push({ start: m.start, end: m.end, code: m.countryCode,
285
- etype: String(m.patternDef.entityType) });
286
- if (cues_js_1.RESCUE_TARGETS.has(String(m.patternDef.entityType))
287
- && (0, cues_js_1.cuedType)(text, m.start) === m.patternDef.entityType)
283
+ else if (m.patternDef.validator !== null) {
284
+ const cued = cues_js_1.RESCUE_TARGETS.has(String(m.patternDef.entityType))
285
+ && (0, cues_js_1.cuedType)(text, m.start) === m.patternDef.entityType;
286
+ if (!m.patternDef.requiresContext) {
287
+ failedSpans.push({ start: m.start, end: m.end, code: m.countryCode,
288
+ etype: String(m.patternDef.entityType) });
289
+ if (cued)
290
+ rescued.push(m);
291
+ }
292
+ else if (cued) {
293
+ // A label-gated pattern is rescued too, but only by a cue touching the
294
+ // value; it casts no demotion zone (rules-engine#75).
288
295
  rescued.push(m);
296
+ }
289
297
  }
290
298
  }
291
299
  // Evidence pass: work out which countries this document belongs to, from
@@ -495,7 +503,7 @@ class RuleEngine {
495
503
  // the value is filed as a Cypriot identity number. Dropping it also clears
496
504
  // outOfScope, which would otherwise claim the detection belongs to a
497
505
  // country the caller did not declare while naming no country at all.
498
- const retyped = (0, cues_js_1.retypedBy)(text, match.start, entityType, countryScores.get(match.countryCode) ?? 0);
506
+ const retyped = (0, cues_js_1.retypedBy)(text, match.start, entityType, countryScores.get(match.countryCode) ?? 0, match.countryCode);
499
507
  if (retyped !== null) {
500
508
  entityType = retyped;
501
509
  country = null;