euredact 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/NOTICE +11 -0
  2. package/README.md +658 -45
  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 +131 -18
  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 +138 -48
  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 +418 -23
  19. package/dist/cjs/rules/validators.js +103 -0
  20. package/dist/cjs/sdk.js +150 -44
  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 +90 -5
  28. package/dist/esm/cloud/client.js +126 -26
  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 +138 -48
  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 +417 -22
  51. package/dist/esm/rules/validators.d.ts +14 -0
  52. package/dist/esm/rules/validators.js +100 -0
  53. package/dist/esm/sdk.d.ts +63 -19
  54. package/dist/esm/sdk.js +148 -44
  55. package/dist/esm/types.d.ts +58 -2
  56. package/dist/esm/types.js +4 -1
  57. package/package.json +12 -4
@@ -3,6 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.COUNTRY_CONFIGS = void 0;
4
4
  const types_js_1 = require("../../types.js");
5
5
  const validators_js_1 = require("../validators.js");
6
+ const suppressors_js_1 = require("../suppressors.js");
7
+ /** Day, month as a word, year ("4. März 1948"), behind a date label (rules-engine#115). */
8
+ function writtenDatePattern() {
9
+ const names = new Set(suppressors_js_1.MONTH_NAMES.split("|").filter(Boolean));
10
+ for (const m of [...names])
11
+ names.add(m.charAt(0).toUpperCase() + m.slice(1));
12
+ const months = [...names].sort((a, b) => b.length - a.length || (a < b ? -1 : a > b ? 1 : 0)).join("|");
13
+ return String.raw `\b(?:0?[1-9]|[12][0-9]|3[01])\.?\s+(?:` + months + String.raw `)\.?\s+(?:19|20)\d{2}\b`;
14
+ }
6
15
  /**
7
16
  * Build a country-independent IBAN pattern from the ISO 13616 length table.
8
17
  *
@@ -105,23 +114,56 @@ const SECRET_CONTEXT = [
105
114
  // ---------------------------------------------------------------------------
106
115
  // DOB context keywords (reused across SHARED patterns)
107
116
  // ---------------------------------------------------------------------------
117
+ // Every way the documents this engine sees introduce a date of birth.
118
+ //
119
+ // DOB recall was 62.8% -- 33,441 unmasked birth dates -- because this covered
120
+ // seven languages of thirty-one. Eleven countries scored exactly 100% (the
121
+ // listed languages) while twenty sat at 42-51%, which is what proved it was
122
+ // coverage rather than capability (rules-engine#38).
123
+ //
124
+ // Matching is substring, not word-boundary, so every entry was screened against
125
+ // the 29.2-million-character corpus for occurrences inside a longer word. Two
126
+ // were rejected: `fædd` lives inside `fæddur`, and `born` was found inside
127
+ // `gabornagy` and `gabornemeth` -- Hungarian names as e-mail local parts -- so
128
+ // `"born "` with the trailing space is listed instead.
129
+ //
130
+ // DOB_CONTEXT is gone: it was a second, shorter copy missing `nato il`,
131
+ // `nascido`, `geburtstag` and more, so ISO dates were gated on fewer keywords
132
+ // than DD/MM/YYYY ones for no stated reason.
108
133
  const DOB_CONTEXT = [
134
+ // Germanic / Romance -- the original set, kept verbatim.
109
135
  "geboren", "geboortedatum", "date de naissance", "né le", "née le",
110
136
  "né(e) le", "nee le", "nee(e) le", "date of birth", "DOB",
111
137
  "Geburtsdatum", "geboren am", "geboren op", "nascido", "nacido",
112
138
  "data di nascita", "nato il", "nata il", "geb.", "geb.datum", "geb ",
113
139
  "birth date", "birthday", "naissance", "geboorte", "geburtstag",
114
- ];
115
- const DOB_ISO_CONTEXT = [
116
- "geboren", "geboortedatum", "date de naissance", "né le", "née le",
117
- "né(e) le", "nee le", "date of birth", "DOB", "Geburtsdatum",
118
- "geboren am", "geboren op", "geb.", "birth date", "birthday",
119
- "naissance", "geboorte",
140
+ // English and Italian forms the list was missing.
141
+ "born ", "d.o.b", "nato/a il",
142
+ // Nordic.
143
+ "født", "fødselsdato", "född", "födelsedatum",
144
+ "syntynyt", "syntymäaika", "fæddur", "fæðingardagur",
145
+ // Greek -- Greece and Cyprus.
146
+ "γεννηθείς", "γεννήθηκε", "ημερομηνία γέννησης",
147
+ // West Slavic.
148
+ "urodzony", "urodzona", "data urodzenia",
149
+ "narozen", "datum narození", "narodený", "dátum narodenia",
150
+ // Hungarian, Romanian, Bulgarian.
151
+ "született", "születési", "născut", "născută", "data nașterii",
152
+ "роден", "родена", "дата на раждане",
153
+ // South Slavic.
154
+ "rođen", "rođena", "datum rođenja", "rojen", "rojena", "datum rojstva",
155
+ // Baltic.
156
+ "sündinud", "sünniaeg", "dzimis", "dzimusi", "dzimšanas datums",
157
+ "gimęs", "gimusi", "gimimo data",
158
+ // Maltese, Irish, Turkish (Cyprus).
159
+ "twieled", "twieldet", "data tat-twelid",
160
+ "rugadh", "dáta breithe", "doğum tarihi",
120
161
  ];
121
162
  const DATE_OF_DEATH_CONTEXT = [
122
163
  "overleden", "overlijdensdatum", "date de décès", "décédé le",
123
164
  "date of death", "Sterbedatum", "verstorben am", "gestorven",
124
165
  "death date", "died on", "mort le", "décès",
166
+ "datum van overlijden", "décédée le", "Sterbetag",
125
167
  ];
126
168
  // ---------------------------------------------------------------------------
127
169
  // Country configurations
@@ -176,6 +218,9 @@ const SHARED = {
176
218
  p(types_js_1.EntityType.BIC, String.raw `\b[A-Z]{4} [A-Z]{2} [A-Z0-9]{2}(?: [A-Z0-9]{3})?\b`, "bic", "BIC/SWIFT code (space-separated groups)"),
177
219
  p(types_js_1.EntityType.CREDIT_CARD, String.raw `\b(?:4[0-9]{3}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}|5[1-5][0-9]{2}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}|3[47][0-9]{2}[\s\-]?[0-9]{6}[\s\-]?[0-9]{5})\b`, "luhn"),
178
220
  p(types_js_1.EntityType.VIN, String.raw `\b[A-HJ-NPR-Z0-9]{17}\b`, "vin"),
221
+ // Behind its label: any case, or in its three sections 3-6-8 (rules-engine#110).
222
+ p(types_js_1.EntityType.VIN, String.raw `\b[A-HJ-NPR-Za-hj-npr-z0-9]{13}\d{4}\b`, "vin", "VIN — any case, behind its label", ["VIN", "FIN:", "FIN-Nr", "FIN-Nummer", "Fahrgestellnummer", "Fahrzeug-Identifizierungsnummer", "Fahrzeugidentifizierungsnummer", "châssis", "chassis", "chassisnummer", "chasisnummer", "numéro de série", "vehicle identification", "numero di telaio", "telaio", "bastidor", "número de bastidor", "VIN-code", "VIN-kod"], true),
223
+ p(types_js_1.EntityType.VIN, String.raw `\b[A-HJ-NPR-Za-hj-npr-z0-9]{3}[\s\-][A-HJ-NPR-Za-hj-npr-z0-9]{6}[\s\-][A-HJ-NPR-Za-hj-npr-z0-9]{4}\d{4}\b`, "vin", "VIN — in sections, behind its label", ["VIN", "FIN:", "FIN-Nr", "FIN-Nummer", "Fahrgestellnummer", "Fahrzeug-Identifizierungsnummer", "Fahrzeugidentifizierungsnummer", "châssis", "chassis", "chassisnummer", "chasisnummer", "numéro de série", "vehicle identification", "numero di telaio", "telaio", "bastidor", "número de bastidor", "VIN-code", "VIN-kod"], true),
179
224
  p(types_js_1.EntityType.IP_ADDRESS, String.raw `\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b`),
180
225
  // Branch order is load-bearing: alternation is leftmost-first, not
181
226
  // longest-match, so the form that consumes most is tried first, and the
@@ -190,6 +235,8 @@ const SHARED = {
190
235
  p(types_js_1.EntityType.MAC_ADDRESS, String.raw `\b[0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\b`, null, "Cisco"),
191
236
  p(types_js_1.EntityType.IMEI, String.raw `\b\d{15}\b`, "imei"),
192
237
  p(types_js_1.EntityType.IMEI, String.raw `\b\d{2}[\-\s]\d{6}[\-\s]\d{6}[\-\s]\d\b`, "imei", "formatted"),
238
+ // Behind "IMEI": 14 digits, the 16-digit IMEISV, or 15 with a failed check (rules-engine#110).
239
+ p(types_js_1.EntityType.IMEI, String.raw `\b\d{14,16}\b`, null, "IMEI / IMEISV — behind its label", ["IMEI"], true),
193
240
  p(types_js_1.EntityType.GPS_COORDINATES, String.raw `-?(?:[1-8]?\d(?:\.\d{4,})|90(?:\.0{4,}))\s*[,;/]\s*-?(?:1[0-7]\d(?:\.\d{4,})|180(?:\.0{4,})|\d{1,2}(?:\.\d{4,}))`),
194
241
  p(types_js_1.EntityType.UUID, String.raw `\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\b`),
195
242
  p(types_js_1.EntityType.SOCIAL_HANDLE, String.raw `(?<![\p{L}\p{N}_])@\p{L}[\p{L}\p{N}_.]{1,29}\b`),
@@ -223,7 +270,12 @@ const SHARED = {
223
270
  // --- Secret / API Key (connection strings with embedded credentials) ---
224
271
  p(types_js_1.EntityType.SECRET, String.raw `(?:mongodb|mysql|postgres(?:ql)?|redis|amqp|rabbitmq):\/\/[^\s:]{1,256}:[^\s@]{1,256}@[^\s]+`, null, "Connection string with credentials"),
225
272
  // --- Secret / API Key (assignment-based: KEY=value or KEY: value) ---
226
- p(types_js_1.EntityType.SECRET, String.raw `(?<=[:=] )\S{8,}|(?<=[:=])\S{8,}`, "high_entropy", "Assigned secret value", SECRET_CONTEXT, true),
273
+ // The final character may not be sentence punctuation. `\S` does not stop
274
+ // at a full stop, so "Reisepass: CA1234567." was claimed as the
275
+ // ten-character span "CA1234567." -- a passport number plus the sentence's
276
+ // period, masked as a credential because a "credentials" word sat within
277
+ // the context window (rules-engine#35).
278
+ p(types_js_1.EntityType.SECRET, String.raw `(?<=[:=] )\S{7,}[^\s.,;:!?)'"]|(?<=[:=])\S{7,}[^\s.,;:!?)'"]`, "high_entropy", "Assigned secret value", SECRET_CONTEXT, true),
227
279
  // --- Secret / API Key (entropy-based fallback for longer tokens) ---
228
280
  // Only the *trailing* \b is replaced. It could not match when the token run
229
281
  // ended on "-", "+" or "/", so the engine backtracked across two
@@ -241,19 +293,24 @@ const SHARED = {
241
293
  p(types_js_1.EntityType.SECRET, String.raw `\b[A-Za-z0-9_\-+/]{24,}[A-Za-z0-9_\-+/=]*(?![A-Za-z0-9_\-+/=])`, "high_entropy", "High-entropy token", SECRET_CONTEXT, true),
242
294
  p(types_js_1.EntityType.DOB, String.raw `\b(?:0[1-9]|[12][0-9]|3[01])[/.\-](?:0[1-9]|1[0-2])[/.\-](?:19|20)\d{2}\b`, null, "DD/MM/YYYY", DOB_CONTEXT, true),
243
295
  p(types_js_1.EntityType.DATE_OF_DEATH, String.raw `\b(?:0[1-9]|[12][0-9]|3[01])[/.\-](?:0[1-9]|1[0-2])[/.\-](?:19|20)\d{2}\b`, null, "", DATE_OF_DEATH_CONTEXT, true),
244
- p(types_js_1.EntityType.DOB, String.raw `\b(?:19|20)\d{2}[/.\-](?:0[1-9]|1[0-2])[/.\-](?:0[1-9]|[12][0-9]|3[01])\b`, null, "ISO YYYY-MM-DD", DOB_ISO_CONTEXT, true),
296
+ // Written-month dates, behind the same labels (rules-engine#115).
297
+ p(types_js_1.EntityType.DOB, writtenDatePattern(), null, "Date with a written month", DOB_CONTEXT, true),
298
+ p(types_js_1.EntityType.DATE_OF_DEATH, writtenDatePattern(), null, "Date of death with a written month", DATE_OF_DEATH_CONTEXT, true),
299
+ p(types_js_1.EntityType.DOB, String.raw `\b(?:19|20)\d{2}[/.\-](?:0[1-9]|1[0-2])[/.\-](?:0[1-9]|[12][0-9]|3[01])\b`, null, "ISO YYYY-MM-DD", DOB_CONTEXT, true),
245
300
  ],
246
301
  };
247
302
  const NL = {
248
303
  code: "NL",
249
304
  name: "Netherlands",
250
305
  patterns: [
306
+ // Healthcare-provider numbers, behind their label: no shape of their own, so without a pattern nothing claimed them, or a phone or a foreign checksum did (canon HEALTHCARE_PROVIDER; rules-engine#111).
307
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{8,11}\b`, null, "Dutch BIG-registratienummer — 8-11 digits", ["BIG-nummer", "BIG nummer", "BIG-registratie", "BIG registratie", "BIG-nr", "BIG nr", "BIG:"], true),
251
308
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[0-9]{9}\b`, "bsn"),
252
309
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[0-9]{2,4}\.[0-9]{2,4}\.[0-9]{2,4}\b`, "bsn"),
253
310
  p(types_js_1.EntityType.IBAN, String.raw `\bNL\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
254
- p(types_js_1.EntityType.VAT, String.raw `\bNL\d{9}B\d{2}\b`, "vat_nl"),
311
+ p(types_js_1.EntityType.VAT, String.raw `\bNL[\s.]?\d{9}B\d{2}\b`, "vat_nl"),
255
312
  p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{8}\b`, "kvk", "", ["KvK", "Kamer van Koophandel", "kvk-nummer", "KVK-nummer", "handelsregister", "chamber of commerce"], true),
256
- p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d[\s\-]?\d{3}[\s\-]?\d{4}\b`),
313
+ p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d(?:\s-\s|[\s\-])?\d{3}[\s\-]?\d{4}\b`),
257
314
  p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{2}[\s\-]?\d{6,7}\b`),
258
315
  p(types_js_1.EntityType.PHONE, String.raw `\b06[\s\-]?\d{4}[\s\-]?\d{4}\b`),
259
316
  p(types_js_1.EntityType.PHONE, String.raw `\b06\d{8}\b`),
@@ -278,7 +335,7 @@ const BE = {
278
335
  p(types_js_1.EntityType.PASSPORT, String.raw `\b[A-Z]{2}\d{6}\b`, null, "", PASSPORT_CONTEXT, true),
279
336
  p(types_js_1.EntityType.DRIVERS_LICENSE, String.raw `\b\d{10}\b`, null, "", ["rijbewijs", "permis de conduire", "driving licence", "driving license", "rijbewijsnummer"], true),
280
337
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[12][\-\s]?[A-Z]{3}[\-\s]?\d{3}\b`),
281
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["postcode", "code postal", "postnummer", "postal code", "zip", "B-", "adres", "adresse", "wonende", "woonplaats", "rue", "straat", "laan", "avenue", "boulevard", "plein", "steenweg", "chaussée", "domicilié", "gedomicilieerd", "Levering:", "siège"], true),
338
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["postcode", "code postal", "postnummer", "postal code", "zip", "B-", "adres", "adresse", "wonende", "woonplaats", "domicile", "rue", "straat", "laan", "avenue", "boulevard", "plein", "steenweg", "chaussée", "domicilié", "gedomicilieerd", "Levering:", "siège"], true),
282
339
  p(types_js_1.EntityType.POSTAL_CODE, String.raw `(?<=, )[1-9]\d{3}(?= [A-Z])`),
283
340
  p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{1}[\-.]?\d{5}[\-.]?\d{2}[\-.]?\d{3}\b`, null, "", ["RIZIV", "INAMI", "arts", "médecin", "zorgverlener", "prestataire", "dokter", "doctor"], true),
284
341
  ],
@@ -287,13 +344,17 @@ const DE = {
287
344
  code: "DE",
288
345
  name: "Germany",
289
346
  patterns: [
347
+ // Healthcare-provider numbers, behind their label: no shape of their own, so without a pattern nothing claimed them, or a phone or a foreign checksum did (canon HEALTHCARE_PROVIDER; rules-engine#111).
348
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{9}\b`, null, "German LANR — 9 digits", ["LANR", "Lebenslange Arztnummer", "Arztnummer"], true),
349
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{9}\b`, null, "German BSNR — 9 digits", ["BSNR", "Betriebsstättennummer", "Betriebsstätten-Nr"], true),
290
350
  p(types_js_1.EntityType.TAX_ID, String.raw `\b[1-9]\d{10}\b`, "german_tax_id"),
291
351
  p(types_js_1.EntityType.TAX_ID, String.raw `\b[1-9]\d[\s.]?\d{3}[\s.]?\d{3}[\s.]?\d{3}\b`, "german_tax_id"),
292
352
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[CFGHJKLMNPRTVWXYZ][0-9CFGHJKLMNPRTVWXYZ]{8}\d?\b`, null, "", ["Personalausweis", "Personalausweisnummer", "Ausweis", "Ausweisnummer", "Ausweis-Nr", "identity card", "Identitätskarte", "Perso", "PA-Nummer"], true),
293
353
  p(types_js_1.EntityType.IBAN, String.raw `\bDE\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
294
354
  p(types_js_1.EntityType.VAT, String.raw `\bDE\s?\d{9}\b`, "vat_de"),
295
- p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{1,4}[\s/\-]?\d{3,8}\b`),
296
- p(types_js_1.EntityType.PHONE, String.raw `\+49\s?\d{2,5}[\s/\-]?\d{3,8}\b`),
355
+ // Prefix may be set off by " / "; up to three trailing two-digit groups (rules-engine#51).
356
+ p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{1,4}(?:\s?[/\-]\s?|\s)?\d{3,8}\b(?:[ \-]\d{2}\b(?![.,/:\-]\d)){0,3}`),
357
+ p(types_js_1.EntityType.PHONE, String.raw `\+49\s?\d{2,5}(?:\s?[/\-]\s?|\s)?\d{3,8}\b(?:[ \-]\d{2}\b(?![.,/:\-]\d)){0,3}`),
297
358
  p(types_js_1.EntityType.PASSPORT, String.raw `\b[CFGHJK][0-9CFGHJKLMNPRTVWXYZ]{8}\d?\b`, null, "", PASSPORT_CONTEXT, true),
298
359
  // The separator after the city code is mandatory. When it was optional, a
299
360
  // contiguous letter run split across both groups and the hyphen was
@@ -303,7 +364,7 @@ const DE = {
303
364
  // separates the city code: "B-AB 1234", "M-XY 99".
304
365
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-ZÄÖÜ]{1,3}[\s\-][A-Z]{1,2}[\s\-]?\d{1,4}[EH]?\b`),
305
366
  p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{5}\b`, null, "", ["PLZ", "Postleitzahl", "postal code", "postcode", "Anschrift", "Adresse", "Wohnort", "Ort", "Stadt", "wohnt", "wohnhaft", "Straße", "Str.", "Weg", "Platz", "Allee", "Ring"], true),
306
- p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\bHR[AB]\s?\d{4,6}\b`),
367
+ p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\bHR[AB]\s?\d{4,6}(?:\s[A-Z]\b)?`),
307
368
  p(types_js_1.EntityType.SSN, String.raw `\b\d{2}[\s]?\d{6}[\s]?[A-Z][\s]?\d{3}\b`, null, "", ["Rentenversicherung", "RV-Nummer", "Sozialversicherung", "SV-Nummer", "Versicherungsnummer", "Rentenversicherungsnummer", "Sozialversicherungsnummer", "SVNR", "SV-Nr", "RVNR"], true),
308
369
  p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{2,3}/\d{3,4}/\d{4,5}\b`, null, "", ["Steuernummer", "St.-Nr", "StNr", "Finanzamt", "Steuer"], true),
309
370
  p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{13}\b`, null, "", ["Steuernummer", "St.-Nr", "StNr", "Finanzamt", "Steuer"], true),
@@ -314,15 +375,16 @@ const AT = {
314
375
  code: "AT",
315
376
  name: "Austria",
316
377
  patterns: [
317
- p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{4}\s?\d{6}\b`, "austrian_svnr"),
378
+ // SSN: Austria has no national ID; the SVNr is its separate social-insurance number (rules-engine#111).
379
+ p(types_js_1.EntityType.SSN, String.raw `\b\d{4}\s?\d{6}\b`, "austrian_svnr"),
318
380
  p(types_js_1.EntityType.IBAN, String.raw `\bAT\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
319
- p(types_js_1.EntityType.VAT, String.raw `\bATU\d{8}\b`),
381
+ p(types_js_1.EntityType.VAT, String.raw `\bATU[\s.]?\d{8}\b`),
320
382
  p(types_js_1.EntityType.PHONE, String.raw `\b0\d{3,4}[\s\-]?\d{5,8}\b`),
321
383
  // Short area codes (Vienna is "01") split across three groups:
322
384
  // "01 53460 2215". The pattern above requires 3-4 digits after the trunk
323
385
  // 0, so it never matched these. Both separators are mandatory here, which
324
386
  // keeps "01 2025" (a date fragment) out.
325
- p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,4}[\s\-/]\d{3,7}[\s\-/]\d{2,6}\b`, null, "Austrian national phone — short area code, grouped"),
387
+ p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,4}[\s\-/]\d{3,7}[\s\-/]\d{2,6}\b(?![.,/:]\d)`, null, "Austrian national phone — short area code, grouped"),
326
388
  p(types_js_1.EntityType.PHONE, String.raw `\+43\s?\d{3,4}[\s\-]?\d{5,8}`),
327
389
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-ZÄÖÜ]{1,2}\s?\d{1,5}\s?[A-Z]{1,2}\b`, null, "", ["Kennzeichen", "Nummernschild", "Kfz-Kennzeichen"], true),
328
390
  p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["PLZ", "Postleitzahl", "Adresse", "Anschrift", "Straße", "Str.", "Gasse", "Weg", "Platz", "Postal:", "Wohnort"], true),
@@ -334,6 +396,8 @@ const CH = {
334
396
  name: "Switzerland",
335
397
  patterns: [
336
398
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b756\.\d{4}\.\d{4}\.\d{2}\b`, "swiss_ahv"),
399
+ // Dotted, check digit not required when the document names it nearby (rules-engine#82).
400
+ p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b756\.\d{4}\.\d{4}\.\d{2}\b`, null, "Swiss AHV number — dotted, beside AVS/AHV", ["AVS", "AHV"], true),
337
401
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b756\d{10}\b`, "swiss_ahv"),
338
402
  p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\bCHE[\-\s]?\d{3}\.?\d{3}\.?\d{3}\b`),
339
403
  p(types_js_1.EntityType.VAT, String.raw `\bCHE[\-\s]?\d{3}\.?\d{3}\.?\d{3}\s?(?:MWST|TVA|IVA)\b`),
@@ -342,13 +406,16 @@ const CH = {
342
406
  p(types_js_1.EntityType.PHONE, String.raw `\+41\s?\d{2}\s?\d{3}\s?\d{2}\s?\d{2}`),
343
407
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}\s?\d{1,6}\b`, null, "", ["Kontrollschild", "Nummernschild", "plaque", "immatriculation", "Kennzeichen"], true),
344
408
  p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{3}\b`, null, "", ["PLZ", "Postleitzahl", "code postal", "NPA", "Adresse", "Anschrift", "adresse", "rue", "Straße", "Str.", "Gasse", "chemin", "Postal:", "Wohnort"], true),
345
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `(?<=, )[1-9]\d{3}(?= [A-Z])`),
409
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `(?:(?<=, )|(?<=, CH-)|(?<=^CH-)|(?<=\nCH-))[1-9]\d{3}(?= [A-Z])`), // also "…, CH-8004 Zürich" and a "CH-8004 Zürich" line (rules-engine#58)
346
410
  ],
347
411
  };
348
412
  const FR = {
349
413
  code: "FR",
350
414
  name: "France",
351
415
  patterns: [
416
+ // Healthcare-provider numbers, behind their label: no shape of their own, so without a pattern nothing claimed them, or a phone or a foreign checksum did (canon HEALTHCARE_PROVIDER; rules-engine#111).
417
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{11}\b`, null, "French RPPS — 11 digits", ["RPPS", "Répertoire Partagé des Professionnels"], true),
418
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{9}\b`, null, "French ADELI — 9 digits", ["ADELI"], true),
352
419
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[12]\s?\d{2}\s?(?:0[1-9]|1[0-2]|[2-9]\d)\s?\d{2}[0-9AB]?\s?\d{3}\s?\d{3}\s?\d{2}\b`, "french_nir"),
353
420
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{12}\b`, null, "", ["carte d'identité", "CNI", "carte nationale", "identity card", "pièce d'identité"], true),
354
421
  p(types_js_1.EntityType.IBAN, String.raw `\bFR\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
@@ -357,7 +424,7 @@ const FR = {
357
424
  p(types_js_1.EntityType.PHONE, String.raw `\+33\s?[1-9][\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}[\s.\-]?\d{2}`),
358
425
  p(types_js_1.EntityType.PASSPORT, String.raw `\b\d{2}[A-Z]{2}\d{5}\b`, null, "", PASSPORT_CONTEXT, true),
359
426
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}[\-\s]?\d{3}[\-\s]?[A-Z]{2}\b`),
360
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b(?:0[1-9]|[1-8]\d|9[0-5]|97[1-6])\d{3}\b`, null, "", ["code postal", "CP", "postal code", "postcode", "adresse", "domicilié", "résidant", "rue", "avenue", "boulevard", "place", "chemin", "allée", "impasse", "ville"], true),
427
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b(?:0[1-9]|[1-8]\d|9[0-5]|97[1-6])\d{3}\b`, null, "", ["code postal", "CP", "postal code", "postcode", "adresse", "domicile", "domicilié", "résidant", "rue", "avenue", "boulevard", "place", "chemin", "allée", "impasse", "ville"], true),
361
428
  p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{3}\s?\d{3}\s?\d{3}\b`, null, "", ["SIREN", "siren", "RCS", "entreprise", "immatricul", "numéro d'entreprise", "registre du commerce"], true),
362
429
  p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{3}\s?\d{3}\s?\d{3}\s?\d{5}\b`, null, "", ["SIRET", "siret", "établissement", "immatricul"], true),
363
430
  p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{13}\b`, null, "", ["numéro fiscal", "SPI", "référence fiscale", "avis d'impôt", "impôt sur le revenu", "déclaration fiscale", "fiscal"], true),
@@ -390,9 +457,9 @@ const SE = {
390
457
  p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,3}[\-\s]?\d{2,3}[\s]?\d{2,3}[\s]?\d{2}\b`),
391
458
  p(types_js_1.EntityType.PHONE, String.raw `\+46\s?\d{1,3}[\s\-]?\d{2,3}[\s\-]?\d{2,3}[\s\-]?\d{2}`),
392
459
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{3}\s?\d{2}[A-Z0-9]\b`),
393
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{3}\s?\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé", "plats", "torg", "stigen", "Bostadsadress"], true),
394
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\s\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
395
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{6}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
460
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{3}\s?\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "Postal:", "Address:", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé", "plats", "torg", "stigen", "Bostadsadress"], true),
461
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\s\d{2}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "Postal:", "Address:", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
462
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{6}\b`, null, "", ["postnummer", "postort", "postkod", "adress", "Postal:", "Address:", "bostadsadress", "gatuadress", "boende", "gatan", "vägen", "gata", "väg", "allé"], true),
396
463
  p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{6}-?\d{4}\b`, null, "", ["organisationsnummer", "org.nr", "org nr", "Bolagsverket", "registreringsnummer"], true),
397
464
  ],
398
465
  };
@@ -408,7 +475,7 @@ const NO = {
408
475
  p(types_js_1.EntityType.PHONE, String.raw `\b[2-9]\d\s?\d{2}\s?\d{2}\s?\d{2}\b`),
409
476
  p(types_js_1.EntityType.PHONE, String.raw `\+47\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{2}`),
410
477
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}\s?\d{5}\b`),
411
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\b`, null, "", ["postnummer", "poststed", "postnr", "adresse", "bostedsadresse", "gate", "vei", "veien", "gata", "plass", "stien", "allé"], true),
478
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{4}\b`, null, "", ["postnummer", "poststed", "postnr", "adresse", "bostedsadresse", "gate", "vei", "veien", "gata", "plass", "stien", "allé", "Postal:", "Address:"], true),
412
479
  ],
413
480
  };
414
481
  const FI = {
@@ -417,7 +484,7 @@ const FI = {
417
484
  patterns: [
418
485
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{6}[-+ABCDEFYXWVU]\d{3}[0-9A-FHJK-NPR-Y]\b`, "finnish_hetu"),
419
486
  p(types_js_1.EntityType.IBAN, String.raw `\bFI\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
420
- p(types_js_1.EntityType.VAT, String.raw `\bFI\d{8}\b`),
487
+ p(types_js_1.EntityType.VAT, String.raw `\bFI[\s.]?\d{8}\b`),
421
488
  p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{7}-\d\b`, "finnish_business_id"),
422
489
  p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,2}\s?\d{3,4}\s?\d{3,4}\b`),
423
490
  p(types_js_1.EntityType.PHONE, String.raw `\+358\s?\d{1,3}[\s\-]?\d{3,4}[\s\-]?\d{3,4}`),
@@ -435,7 +502,7 @@ const IS = {
435
502
  p(types_js_1.EntityType.PHONE, String.raw `\b[3-9]\d{6}\b`, null, "", ["sími", "símanúmer", "farsími", "gsm", "phone", "tel", "hringja", "hringdu", "ná í", "nás á", "nás", "SMS", "sent to"], true),
436
503
  p(types_js_1.EntityType.PHONE, String.raw `\+354\s?\d{3}\s?\d{4}`),
437
504
  p(types_js_1.EntityType.LICENSE_PLATE, String.raw `\b[A-Z]{2}\s?\d{3}\b`, null, "", ["skráningarnúmer", "bílnúmer", "ökutæki", "bifreið", "plata"], true),
438
- p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{2}\b`, null, "", ["póstnúmer", "póstfang", "staður", "heimilisfang"], true),
505
+ p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b[1-9]\d{2}\b`, null, "", ["póstnúmer", "póstfang", "staður", "heimilisfang", "Postal:", "Address:"], true),
439
506
  ],
440
507
  };
441
508
  const IT = {
@@ -445,7 +512,7 @@ const IT = {
445
512
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[A-Z]{6}\d{2}[ABCDEHLMPRST]\d{2}[A-Z]\d{3}[A-Z]\b`, "italian_cf"),
446
513
  p(types_js_1.EntityType.IBAN, String.raw `\bIT\d{2}\s?[A-Z]\d{3}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
447
514
  p(types_js_1.EntityType.IBAN, String.raw `\bIT\d{2}[A-Z]\d{22}\b`, "iban"),
448
- p(types_js_1.EntityType.VAT, String.raw `\bIT\d{11}\b`),
515
+ p(types_js_1.EntityType.VAT, String.raw `\bIT[\s.]?\d{11}\b`),
449
516
  p(types_js_1.EntityType.PHONE, String.raw `\b3\d{2}[\s\-]?\d{3}[\s\-]?\d{4}\b`),
450
517
  p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,3}[\s\-]?\d{6,8}\b`),
451
518
  p(types_js_1.EntityType.PHONE, String.raw `\b0\d{1,2}[\s\-]\d{3}[\s\-]\d{4}\b`),
@@ -461,7 +528,7 @@ const ES = {
461
528
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{8}[A-Z]\b`, "spanish_dni"),
462
529
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[XYZ]\d{7}[A-Z]\b`, "spanish_nie"),
463
530
  p(types_js_1.EntityType.IBAN, String.raw `\bES\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
464
- p(types_js_1.EntityType.VAT, String.raw `\bES[A-Z0-9]\d{7}[A-Z0-9]\b`),
531
+ p(types_js_1.EntityType.VAT, String.raw `\bES[\s.]?[A-Z0-9]\d{7}[A-Z0-9]\b`),
465
532
  p(types_js_1.EntityType.PHONE, String.raw `\b[679]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
466
533
  p(types_js_1.EntityType.PHONE, String.raw `\b[679]\d{8}\b`),
467
534
  p(types_js_1.EntityType.PHONE, String.raw `\b[679]\d[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}\b`),
@@ -484,7 +551,7 @@ const PT = {
484
551
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-35-9]\d{8}\b`, "portuguese_nif"),
485
552
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-35-9]\d{2}[\s.]\d{3}[\s.]\d{3}\b`, "portuguese_nif"),
486
553
  p(types_js_1.EntityType.IBAN, String.raw `\bPT\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{5}\b`, "iban"),
487
- p(types_js_1.EntityType.VAT, String.raw `\bPT\d{9}\b`),
554
+ p(types_js_1.EntityType.VAT, String.raw `\bPT[\s.]?\d{9}\b`),
488
555
  p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
489
556
  p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{8}\b`),
490
557
  p(types_js_1.EntityType.PHONE, String.raw `\+351\s?[29]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
@@ -496,7 +563,8 @@ const LU = {
496
563
  code: "LU",
497
564
  name: "Luxembourg",
498
565
  patterns: [
499
- p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b(?:19|20)\d{2}\s(?:0[1-9]|1[0-2])\s(?:0[1-9]|[12]\d|3[01])\s\d{3}\s\d{2}\b`),
566
+ // Grouped: one horizontal space is optional at each boundary of YYYY MM DD XXX XX (rules-engine#49).
567
+ p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b(?:19|20)\d{2}[^\S\r\n]?(?:0[1-9]|1[0-2])[^\S\r\n]?(?:0[1-9]|[12]\d|3[01])[^\S\r\n]?\d{3}[^\S\r\n]?\d{2}\b`),
500
568
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{5}\b`),
501
569
  p(types_js_1.EntityType.IBAN, String.raw `\bLU\d{2}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\b`, "iban"),
502
570
  p(types_js_1.EntityType.VAT, String.raw `\bLU\s?\d{8}\b`, "vat_lu"),
@@ -513,14 +581,32 @@ const PL = {
513
581
  code: "PL",
514
582
  name: "Poland",
515
583
  patterns: [
584
+ // Healthcare-provider numbers, behind their label: no shape of their own, so without a pattern nothing claimed them, or a phone or a foreign checksum did (canon HEALTHCARE_PROVIDER; rules-engine#111).
585
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{7}\b`, null, "Polish PWZ — 7 digits", ["PWZ", "prawa wykonywania zawodu", "prawo wykonywania zawodu"], true),
516
586
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{11}\b`, "polish_pesel"),
517
- p(types_js_1.EntityType.TAX_ID, String.raw `\b\d{3}-?\d{3}-?\d{2}-?\d{2}\b`, "polish_nip"),
587
+ // NIP in the company (3-3-2-2) or the personal (3-2-2-3) grouping (rules-engine#94).
588
+ p(types_js_1.EntityType.TAX_ID, String.raw `\b(?:\d{3}-?\d{3}-?\d{2}-?\d{2}|\d{3}-\d{2}-\d{2}-\d{3})\b`, "polish_nip"),
518
589
  p(types_js_1.EntityType.IBAN, String.raw `\bPL\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
519
590
  p(types_js_1.EntityType.IBAN, String.raw `\bPL\d{26}\b`, "iban"),
520
- p(types_js_1.EntityType.VAT, String.raw `\bPL\d{10}\b`),
591
+ // NRB, the domestic account number: the IBAN without "PL" (rules-engine#93).
592
+ p(types_js_1.EntityType.BANK_ACCOUNT, String.raw `\b\d{2}(?: ?\d{4}){6}\b`, "polish_nrb", "Polish NRB"),
593
+ p(types_js_1.EntityType.VAT, String.raw `\bPL[\s.]?\d{10}\b`),
521
594
  p(types_js_1.EntityType.PHONE, String.raw `\b[5-8]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
522
595
  p(types_js_1.EntityType.PHONE, String.raw `\b[5-8]\d{8}\b`),
523
596
  p(types_js_1.EntityType.PHONE, String.raw `\+48\s?[5-8]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
597
+ // Identity card, REGON, driving licence: each behind its own label (rules-engine#75, #76, #77).
598
+ p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[A-Z]{3}\s?\d{6}\b`, "polish_id_card", "Polish identity card", ["dowód osobisty", "dowodu osobistego", "dowodem osobistym", "dowód", "dowodu", "nr dowodu", "identity card", "ID card"], true),
599
+ p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b\d{9}(?:\d{5})?\b`, "polish_regon", "Polish REGON", ["REGON"], true),
600
+ // KRS: ten digits with the leading zeros, so it begins "00", which no dialled number does (rules-engine#90).
601
+ p(types_js_1.EntityType.CHAMBER_OF_COMMERCE, String.raw `\b00\d{8}\b`, null, "Polish KRS", ["KRS"], true),
602
+ p(types_js_1.EntityType.DRIVERS_LICENSE, String.raw `\b\d{3,6}/\d{2}/\d{3,7}\b`, null, "Polish driving licence number — field 5", ["prawo jazdy", "prawa jazdy", "prawem jazdy", "driving licence", "driving license"], true),
603
+ p(types_js_1.EntityType.DRIVERS_LICENSE, String.raw `\b[A-Z]{2}\s?\d{6,7}\b`, null, "Polish driving licence document number", ["prawo jazdy", "prawa jazdy", "prawem jazdy", "driving licence", "driving license"], true),
604
+ // Military booklet: a 2- or 3-letter series + 7 digits, one identifier, also "seria MS nr 6620419" (rules-engine#102).
605
+ p(types_js_1.EntityType.INTERNAL_ID, String.raw `\b[A-Z]{2,3}(?:\s?|\s+nr\.?\s+)\d{7}\b`, null, "Polish military booklet", ["książeczka wojskowa", "książeczki wojskowej", "książeczkę wojskową", "książeczką wojskową", "książeczce wojskowej", "military booklet", "military record book"], true),
606
+ // Residence card and national-visa sticker: 2 letters + 7 digits (rules-engine#103).
607
+ p(types_js_1.EntityType.RESIDENCE_PERMIT, String.raw `\b[A-Z]{2}\s?\d{7}\b`, null, "Polish residence card or visa sticker", ["karta pobytu", "karty pobytu", "kartę pobytu", "kartą pobytu", "CUKR", "naklejka wizowa", "naklejki wizowej", "naklejkę wizową", "naklejką wizową", "residence card", "visa sticker"], true),
608
+ // Case reference (znak sprawy): prefix.class.number.year, one INTERNAL_ID (rules-engine#105).
609
+ p(types_js_1.EntityType.INTERNAL_ID, String.raw `\b[A-ZĄĆĘŁŃÓŚŹŻ]{2,}(?:-[A-Z0-9]{1,6})*(?:\.[A-Z0-9]{1,6})*\.\d{3,4}(?:\.\d{1,6})+\.(?:19|20)\d{2}\b`, null, "Polish case reference", ["znak sprawy", "znak pisma", "znak:", "sygnatura", "sygn.", "sprawa nr", "sprawy nr", "nr sprawy", "decyzj", "postanowieni", "pismo nr"], true),
524
610
  p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{2}-\d{3}\b`, null, "", ["kod pocztowy", "adres", "ulica", "Postal:", "Address:"]),
525
611
  ],
526
612
  };
@@ -528,10 +614,11 @@ const IE = {
528
614
  code: "IE",
529
615
  name: "Ireland",
530
616
  patterns: [
531
- p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{7}[A-W][ABWTXZ]?\b`, "irish_pps"),
617
+ // The PPSN is SSN, never NATIONAL_ID: Ireland has no national ID (canon SSN; rules-engine#111).
618
+ p(types_js_1.EntityType.SSN, String.raw `\b\d{7}[A-W][ABWTXZ]?\b`, "irish_pps"),
532
619
  p(types_js_1.EntityType.IBAN, String.raw `\bIE\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
533
620
  p(types_js_1.EntityType.IBAN, String.raw `\bIE\d{2}[A-Z]{4}\d{14}\b`, "iban"),
534
- p(types_js_1.EntityType.VAT, String.raw `\bIE\d{7}[A-Z]\b`),
621
+ p(types_js_1.EntityType.VAT, String.raw `\bIE[\s.]?\d{7}[A-Z]\b`),
535
622
  p(types_js_1.EntityType.PHONE, String.raw `\b0[89]\d[\s\-]?\d{3}[\s\-]?\d{4}\b`),
536
623
  p(types_js_1.EntityType.PHONE, String.raw `\b0[89]\d{8}\b`),
537
624
  p(types_js_1.EntityType.PHONE, String.raw `\b0[1-9]\d{0,2}[\s\-]?\d{5,8}\b`),
@@ -543,11 +630,14 @@ const UK = {
543
630
  code: "UK",
544
631
  name: "United Kingdom",
545
632
  patterns: [
546
- p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[A-CEGHJ-PR-TW-Z][A-CEGHJ-NPR-TW-Z]\s?\d{2}\s?\d{2}\s?\d{2}\s?[ABCD]\b`),
633
+ // Healthcare-provider numbers, behind their label: no shape of their own, so without a pattern nothing claimed them, or a phone or a foreign checksum did (canon HEALTHCARE_PROVIDER; rules-engine#111).
634
+ p(types_js_1.EntityType.HEALTHCARE_PROVIDER, String.raw `\b\d{7}\b`, null, "UK GMC number — 7 digits", ["GMC number", "GMC no", "GMC reference", "GMC reg", "GMC:", "GMC-Nr", "General Medical Council"], true),
635
+ // UK NINO: a social-security number, so SSN, as the canon defaults it (rules-engine#47).
636
+ p(types_js_1.EntityType.SSN, String.raw `\b[A-CEGHJ-PR-TW-Z][A-CEGHJ-NPR-TW-Z]\s?\d{2}\s?\d{2}\s?\d{2}\s?[ABCD]\b`),
547
637
  p(types_js_1.EntityType.HEALTH_INSURANCE, String.raw `\b\d{3}\s?\d{3}\s?\d{4}\b`, "uk_nhs", "", ["NHS", "NHS number", "health number", "NHS no"], true),
548
638
  p(types_js_1.EntityType.IBAN, String.raw `\bGB\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
549
639
  p(types_js_1.EntityType.IBAN, String.raw `\bGB\d{2}[A-Z]{4}\d{14}\b`, "iban"),
550
- p(types_js_1.EntityType.VAT, String.raw `\bGB\d{9}\b`),
640
+ p(types_js_1.EntityType.VAT, String.raw `\bGB[\s.]?\d{3}[\s.]?\d{4}[\s.]?\d{2}\b`),
551
641
  p(types_js_1.EntityType.PHONE, String.raw `\b07[4-9]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
552
642
  p(types_js_1.EntityType.PHONE, String.raw `\b07[4-9]\d{8}\b`),
553
643
  p(types_js_1.EntityType.PHONE, String.raw `\b0[12]\d{2}[\s\-]?\d{3}[\s\-]?\d{4}\b`),
@@ -563,7 +653,7 @@ const BG = {
563
653
  patterns: [
564
654
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{10}\b`, "bulgarian_egn"),
565
655
  p(types_js_1.EntityType.IBAN, String.raw `\bBG\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{2}\b`, "iban"),
566
- p(types_js_1.EntityType.VAT, String.raw `\bBG\d{9,10}\b`),
656
+ p(types_js_1.EntityType.VAT, String.raw `\bBG[\s.]?\d{9,10}\b`),
567
657
  p(types_js_1.EntityType.PHONE, String.raw `\b0?[89][789][\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
568
658
  p(types_js_1.EntityType.PHONE, String.raw `\b0?[89][789]\d{7,8}\b`),
569
659
  p(types_js_1.EntityType.PHONE, String.raw `\+359\s?[89][789][\s\-]?\d{3}[\s\-]?\d{3,4}`),
@@ -578,7 +668,7 @@ const CY = {
578
668
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{8}[A-Z]\b`),
579
669
  p(types_js_1.EntityType.IBAN, String.raw `\bCY\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
580
670
  p(types_js_1.EntityType.IBAN, String.raw `\bCY\d{26}\b`, "iban"),
581
- p(types_js_1.EntityType.VAT, String.raw `\bCY\d{8}[A-Z]\b`),
671
+ p(types_js_1.EntityType.VAT, String.raw `\bCY[\s.]?\d{8}[A-Z]\b`),
582
672
  p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{3}[\s\-]?\d{4}\b`),
583
673
  p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d[\s\-]?\d{3}[\s\-]?\d{3}\b`),
584
674
  p(types_js_1.EntityType.PHONE, String.raw `\b[29]\d{7}\b`),
@@ -594,7 +684,7 @@ const CZ = {
594
684
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{6}/?\d{3,4}\b`, "czech_birth_number"),
595
685
  p(types_js_1.EntityType.IBAN, String.raw `\bCZ\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
596
686
  p(types_js_1.EntityType.IBAN, String.raw `\bCZ\d{22}\b`, "iban"),
597
- p(types_js_1.EntityType.VAT, String.raw `\bCZ\d{8,10}\b`),
687
+ p(types_js_1.EntityType.VAT, String.raw `\bCZ[\s.]?\d{8,10}\b`),
598
688
  p(types_js_1.EntityType.PHONE, String.raw `\b[67]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
599
689
  p(types_js_1.EntityType.PHONE, String.raw `\b[67]\d{8}\b`),
600
690
  p(types_js_1.EntityType.PHONE, String.raw `\+420\s?[67]\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
@@ -608,7 +698,7 @@ const SK = {
608
698
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{6}/?\d{3,4}\b`, "czech_birth_number"),
609
699
  p(types_js_1.EntityType.IBAN, String.raw `\bSK\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
610
700
  p(types_js_1.EntityType.IBAN, String.raw `\bSK\d{22}\b`, "iban"),
611
- p(types_js_1.EntityType.VAT, String.raw `\bSK\d{10}\b`),
701
+ p(types_js_1.EntityType.VAT, String.raw `\bSK[\s.]?\d{10}\b`),
612
702
  p(types_js_1.EntityType.PHONE, String.raw `\b0?9\d{2}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
613
703
  p(types_js_1.EntityType.PHONE, String.raw `\+421\s?9\d{2}[\s\-]?\d{3}[\s\-]?\d{3}`),
614
704
  p(types_js_1.EntityType.POSTAL_CODE, String.raw `\b\d{3}\s?\d{2}\b`, null, "", ["PSČ", "poštové", "adresa", "ulica", "Postal:", "Address:"], true),
@@ -621,7 +711,7 @@ const EE = {
621
711
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-6]\d{10}\b`, "estonian_id"),
622
712
  p(types_js_1.EntityType.IBAN, String.raw `\bEE\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
623
713
  p(types_js_1.EntityType.IBAN, String.raw `\bEE\d{18}\b`, "iban"),
624
- p(types_js_1.EntityType.VAT, String.raw `\bEE\d{9}\b`),
714
+ p(types_js_1.EntityType.VAT, String.raw `\bEE[\s.]?\d{9}\b`),
625
715
  p(types_js_1.EntityType.PHONE, String.raw `\b5\d{3}[\s\-]?\d{4}\b`),
626
716
  p(types_js_1.EntityType.PHONE, String.raw `\b5\d{7}\b`),
627
717
  p(types_js_1.EntityType.PHONE, String.raw `\+372\s?5\d{3}[\s\-]?\d{3,4}`),
@@ -635,7 +725,7 @@ const LT = {
635
725
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-6]\d{10}\b`, "estonian_id"),
636
726
  p(types_js_1.EntityType.IBAN, String.raw `\bLT\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
637
727
  p(types_js_1.EntityType.IBAN, String.raw `\bLT\d{18}\b`, "iban"),
638
- p(types_js_1.EntityType.VAT, String.raw `\bLT\d{9,12}\b`),
728
+ p(types_js_1.EntityType.VAT, String.raw `\bLT[\s.]?\d{9,12}\b`),
639
729
  p(types_js_1.EntityType.PHONE, String.raw `\b6\d{2}[\s\-]?\d{2}[\s\-]?\d{3}\b`),
640
730
  p(types_js_1.EntityType.PHONE, String.raw `\b6\d{7}\b`),
641
731
  p(types_js_1.EntityType.PHONE, String.raw `\b8[\s\-]?6\d{2}[\s\-]?\d{2}[\s\-]?\d{3}\b`),
@@ -651,7 +741,7 @@ const LV = {
651
741
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b(?:0[1-9]|[12]\d|3[01])(?:0[1-9]|1[0-2])\d{7}\b`, null, "", ["personas kods", "PK", "identifikācijas"], true),
652
742
  p(types_js_1.EntityType.IBAN, String.raw `\bLV\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d\b`, "iban"),
653
743
  p(types_js_1.EntityType.IBAN, String.raw `\bLV\d{2}[A-Z]{4}\d{13}\b`, "iban"),
654
- p(types_js_1.EntityType.VAT, String.raw `\bLV\d{11}\b`),
744
+ p(types_js_1.EntityType.VAT, String.raw `\bLV[\s.]?\d{11}\b`),
655
745
  p(types_js_1.EntityType.PHONE, String.raw `\b[2]\d{3}[\s\-]?\d{4}\b`),
656
746
  p(types_js_1.EntityType.PHONE, String.raw `\b[2]\d{7}\b`),
657
747
  p(types_js_1.EntityType.PHONE, String.raw `\+371\s?[2]\d{3}[\s\-]?\d{3,4}`),
@@ -666,7 +756,7 @@ const HR = {
666
756
  p(types_js_1.EntityType.IBAN, String.raw `\bHR\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
667
757
  p(types_js_1.EntityType.IBAN, String.raw `\bHR\d{2}\s\d{4}\s\d{4}\s\d{4}\s\d{5}\b`, "iban"),
668
758
  p(types_js_1.EntityType.IBAN, String.raw `\bHR\d{19}\b`, "iban"),
669
- p(types_js_1.EntityType.VAT, String.raw `\bHR\d{11}\b`),
759
+ p(types_js_1.EntityType.VAT, String.raw `\bHR[\s.]?\d{11}\b`),
670
760
  p(types_js_1.EntityType.PHONE, String.raw `\b0?9[12579][\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
671
761
  p(types_js_1.EntityType.PHONE, String.raw `\b0?9[12579]\d{7,8}\b`),
672
762
  p(types_js_1.EntityType.PHONE, String.raw `\+385\s?9[12579][\s\-]?\d{3}[\s\-]?\d{3,4}`),
@@ -680,7 +770,7 @@ const HU = {
680
770
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{9}\b`, "hungarian_taj"),
681
771
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{3}\s\d{3}\s\d{3}\b`, "hungarian_taj"),
682
772
  p(types_js_1.EntityType.TAX_ID, String.raw `\b8\d{9}\b`, null, "", ["adóazonosító", "adószám", "adóazonosító jel"], true),
683
- p(types_js_1.EntityType.VAT, String.raw `\bHU\d{8,11}\b`, null, "", ["adószám", "ÁFA"]),
773
+ p(types_js_1.EntityType.VAT, String.raw `\bHU[\s.]?\d{8,11}\b`, null, "", ["adószám", "ÁFA"]),
684
774
  p(types_js_1.EntityType.IBAN, String.raw `\bHU\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b`, "iban"),
685
775
  p(types_js_1.EntityType.IBAN, String.raw `\bHU\d{26}\b`, "iban"),
686
776
  p(types_js_1.EntityType.PHONE, String.raw `\b06\s?\d{1,2}[\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
@@ -695,7 +785,7 @@ const RO = {
695
785
  patterns: [
696
786
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b[1-8]\d{12}\b`, "romanian_cnp"),
697
787
  p(types_js_1.EntityType.IBAN, String.raw `\bRO\d{2}[A-Z]{4}\d{16}\b`, "iban"),
698
- p(types_js_1.EntityType.VAT, String.raw `\bRO\d{2,10}\b`),
788
+ p(types_js_1.EntityType.VAT, String.raw `\bRO[\s.]?\d{2,10}\b`),
699
789
  p(types_js_1.EntityType.PHONE, String.raw `\b0?7[2-9]\d{1}[\s\-]?\d{3}[\s\-]?\d{3,4}\b`),
700
790
  p(types_js_1.EntityType.PHONE, String.raw `\b0?7[2-9]\d{7,8}\b`),
701
791
  p(types_js_1.EntityType.PHONE, String.raw `\+40\s?7[2-9]\d{1}[\s\-]?\d{3}[\s\-]?\d{3,4}`),
@@ -710,7 +800,7 @@ const EL = {
710
800
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{11}\b`, "greek_amka"),
711
801
  p(types_js_1.EntityType.IBAN, String.raw `\bGR\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
712
802
  p(types_js_1.EntityType.IBAN, String.raw `\bGR\d{25}\b`, "iban"),
713
- p(types_js_1.EntityType.VAT, String.raw `\bEL\d{9}\b`),
803
+ p(types_js_1.EntityType.VAT, String.raw `\bEL[\s.]?\d{9}\b`),
714
804
  p(types_js_1.EntityType.PHONE, String.raw `\b69\d[\s\-]?\d{3,4}[\s\-]?\d{4}\b`),
715
805
  p(types_js_1.EntityType.PHONE, String.raw `\b69\d{8}\b`),
716
806
  p(types_js_1.EntityType.PHONE, String.raw `\b2\d{1,3}[\s\-]?\d{3,4}[\s\-]?\d{3,4}\b`),
@@ -725,7 +815,7 @@ const SI = {
725
815
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{13}\b`, "slovenian_emso"),
726
816
  p(types_js_1.EntityType.IBAN, String.raw `\bSI\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}\b`, "iban"),
727
817
  p(types_js_1.EntityType.IBAN, String.raw `\bSI\d{17}\b`, "iban"),
728
- p(types_js_1.EntityType.VAT, String.raw `\bSI\d{8}\b`),
818
+ p(types_js_1.EntityType.VAT, String.raw `\bSI[\s.]?\d{8}\b`),
729
819
  p(types_js_1.EntityType.PHONE, String.raw `\b0?[34567]\d{1}[\s\-]?\d{3}[\s\-]?\d{3}\b`),
730
820
  p(types_js_1.EntityType.PHONE, String.raw `\b0?[34567]\d{7}\b`),
731
821
  p(types_js_1.EntityType.PHONE, String.raw `\+386\s?[34567]\d{1}[\s\-]?\d{3}[\s\-]?\d{3}`),
@@ -740,7 +830,7 @@ const MT = {
740
830
  p(types_js_1.EntityType.NATIONAL_ID, String.raw `\b\d{5,7}[MGAPLHBZ]\b`),
741
831
  p(types_js_1.EntityType.IBAN, String.raw `\bMT\d{2}\s?[A-Z]{4}\s?\d{4}\s?\d{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{4}\s?[A-Z0-9]{3}\b`, "iban"),
742
832
  p(types_js_1.EntityType.IBAN, String.raw `\bMT\d{2}[A-Z]{4}\d{5}[A-Z0-9]{18}\b`, "iban"),
743
- p(types_js_1.EntityType.VAT, String.raw `\bMT\d{8}\b`),
833
+ p(types_js_1.EntityType.VAT, String.raw `\bMT[\s.]?\d{8}\b`),
744
834
  p(types_js_1.EntityType.PHONE, String.raw `\b[79]\d{3}[\s\-]?\d{4}\b`),
745
835
  p(types_js_1.EntityType.PHONE, String.raw `\b[79]\d{7}\b`),
746
836
  p(types_js_1.EntityType.PHONE, String.raw `\+356\s?[79]\d{3}[\s\-]?\d{3,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
  }