swedish-pii 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +476 -0
- package/dist/index.cjs +178495 -0
- package/dist/index.d.cts +163 -0
- package/dist/index.d.ts +163 -0
- package/dist/index.js +178457 -0
- package/package.json +71 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/** All PII labels this library can detect. */
|
|
2
|
+
type PiiLabel = "PER_FIRST" | "PER_LAST" | "AMEX_CREDIT_CARD" | "MASTERCARD_CREDIT_CARD" | "VISA_CREDIT_CARD" | "IBAN_CODE" | "BIC_CODE" | "SE_BANK_NUMBER" | "SE_BANKGIRO" | "SE_PLUSGIRO" | "SE_VAT_NUMBER" | "CRYPTO_WALLET" | "SE_PERSONAL_IDENTITY_NUMBER_MALE" | "SE_PERSONAL_IDENTITY_NUMBER_FEMALE" | "SE_COORDINATION_NUMBER_MALE" | "SE_COORDINATION_NUMBER_FEMALE" | "SE_PASSPORT_NUMBER" | "EMAIL_ADDRESS" | "PHONE_NUMBER" | "SOCIAL_MEDIA" | "SE_STREET_ADDRESS" | "SE_POSTAL_CODE" | "SE_MUNICIPALITY" | "SE_COUNTY" | "SE_CITY" | "SE_PROPERTY_DESIGNATION" | "COORDINATE" | "SE_WORK_ORGANIZATION" | "SE_EDUCATION_ORGANIZATION" | "SE_EDUCATION_PROGRAM" | "SE_WORK_PROFESSION" | "SE_ORGANIZATION_NUMBER" | "MARITAL_STATUS" | "GENETIC_SEX" | "DISABILITY" | "RELIGION" | "SEXUAL_ORIENTATION" | "DEMOGRAPHIC" | "POLITICAL_IDEOLOGIES" | "SE_LABOR_UNION" | "SE_LICENSE_PLATE" | "IP_ADDRESS" | "MAC_ADDRESS" | "DATE" | "TIME" | "SE_CASE_NUMBER" | "AGE";
|
|
3
|
+
/** A single detected PII entity, anchored to the original text. */
|
|
4
|
+
interface PiiEntity {
|
|
5
|
+
/** Unique id within one detection run, e.g. `PHONE_NUMBER_1`. */
|
|
6
|
+
id: string;
|
|
7
|
+
label: PiiLabel;
|
|
8
|
+
/** The matched text exactly as it appears in the input. */
|
|
9
|
+
value: string;
|
|
10
|
+
/** Start offset (inclusive) in the original text. */
|
|
11
|
+
start: number;
|
|
12
|
+
/** End offset (exclusive) in the original text. */
|
|
13
|
+
end: number;
|
|
14
|
+
/**
|
|
15
|
+
* Confidence in (0, 1]. Checksum-validated matches score ~0.95,
|
|
16
|
+
* exact gazetteer hits ~0.85–0.9, plain pattern shapes ~0.6, matches
|
|
17
|
+
* that failed a checksum ~0.45, and context-starved shapes ~0.25
|
|
18
|
+
* (below the default threshold).
|
|
19
|
+
*/
|
|
20
|
+
score: number;
|
|
21
|
+
}
|
|
22
|
+
/** An entity candidate produced by a detector, before ids are assigned. */
|
|
23
|
+
type EntitySpan = Omit<PiiEntity, "id">;
|
|
24
|
+
interface DetectOptions {
|
|
25
|
+
/**
|
|
26
|
+
* When true, matches that carry a checksum (credit cards, personal
|
|
27
|
+
* identity numbers, organization numbers, IBANs) must pass validation,
|
|
28
|
+
* and date-based numbers must encode a real calendar date.
|
|
29
|
+
* Defaults to false so that synthetic/example data is still masked.
|
|
30
|
+
*/
|
|
31
|
+
strict?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Entities scoring below this are dropped. Defaults to
|
|
34
|
+
* DEFAULT_SCORE_THRESHOLD (0.4): checksum failures (0.45) survive,
|
|
35
|
+
* context-starved shapes (0.25) do not. Lower it to catch e.g. bank
|
|
36
|
+
* account numbers in bare CSV columns with no surrounding words;
|
|
37
|
+
* raise it to keep only high-confidence entities.
|
|
38
|
+
* Must be a finite number from 0 through 1; other values throw a
|
|
39
|
+
* RangeError.
|
|
40
|
+
*/
|
|
41
|
+
scoreThreshold?: number;
|
|
42
|
+
}
|
|
43
|
+
/** A detector produces entity spans for one or more labels. */
|
|
44
|
+
interface Detector {
|
|
45
|
+
/** The label(s) this detector can emit — used for documentation/tooling. */
|
|
46
|
+
labels: readonly PiiLabel[];
|
|
47
|
+
detect(text: string, options: Required<DetectOptions>): EntitySpan[];
|
|
48
|
+
}
|
|
49
|
+
interface MaskedValue {
|
|
50
|
+
id: string;
|
|
51
|
+
value: string;
|
|
52
|
+
}
|
|
53
|
+
/** Detected values grouped per label, in order of appearance. */
|
|
54
|
+
type MaskedData = Partial<Record<PiiLabel, MaskedValue[]>>;
|
|
55
|
+
interface MaskResult {
|
|
56
|
+
/** Input text with every detected entity replaced by `<LABEL_n>`. */
|
|
57
|
+
maskedText: string;
|
|
58
|
+
/** Detected values grouped per label. */
|
|
59
|
+
maskedData: MaskedData;
|
|
60
|
+
/** Flat list of detected entities with offsets into the original text. */
|
|
61
|
+
entities: PiiEntity[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Detectors in priority order: when two detectors match overlapping
|
|
66
|
+
* text, the one listed first wins. Names run first, then patterns from
|
|
67
|
+
* most to least specific — in particular, the generic Swedish bank
|
|
68
|
+
* account number (10–11 digits) runs after personnummer, organization
|
|
69
|
+
* numbers, cards and phone numbers, all of which it would otherwise
|
|
70
|
+
* shadow.
|
|
71
|
+
*/
|
|
72
|
+
declare const detectors: Detector[];
|
|
73
|
+
/**
|
|
74
|
+
* Run all detectors in priority order. Each detector sees the input
|
|
75
|
+
* with higher-priority matches blanked out (never with placeholder text,
|
|
76
|
+
* so placeholders can't be re-matched or corrupted). Blanking — rather
|
|
77
|
+
* than dropping overlapping candidates outright — lets a lower-priority
|
|
78
|
+
* detector still match the uncovered remainder: in "Lilla Vägen 7",
|
|
79
|
+
* where "Lilla" is a registered first name, the street detector can
|
|
80
|
+
* still mask "Vägen 7".
|
|
81
|
+
*
|
|
82
|
+
* Every candidate carries a confidence score; candidates below
|
|
83
|
+
* `scoreThreshold` are discarded before overlap resolution.
|
|
84
|
+
*/
|
|
85
|
+
declare function detectPII(text: string, options?: DetectOptions): PiiEntity[];
|
|
86
|
+
/**
|
|
87
|
+
* Mask every detected entity in `text`, replacing it with `<LABEL_n>`.
|
|
88
|
+
* The returned `maskedData` maps each label to its values in order of
|
|
89
|
+
* appearance, and `entities` carries exact offsets into the input.
|
|
90
|
+
*/
|
|
91
|
+
declare function maskPII(text: string, options?: DetectOptions): MaskResult;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Match a "First Last" string against the name lists. Returns the
|
|
95
|
+
* canonical matched names, or null when either part misses.
|
|
96
|
+
*/
|
|
97
|
+
declare function matchFullName(fullName: string): {
|
|
98
|
+
first: string;
|
|
99
|
+
last: string;
|
|
100
|
+
} | null;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Luhn (mod 10) checksum, used by payment card numbers, Swedish personal
|
|
104
|
+
* identity numbers (personnummer), coordination numbers and organization
|
|
105
|
+
* numbers.
|
|
106
|
+
*/
|
|
107
|
+
declare function luhnCheck(digits: string): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Validate the checksum of a Swedish personnummer / samordningsnummer /
|
|
110
|
+
* organisationsnummer. The Luhn sum is always computed over the final
|
|
111
|
+
* 10 digits (century digits are excluded).
|
|
112
|
+
*/
|
|
113
|
+
declare function swedishIdChecksum(value: string): boolean;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* IBAN mod-97 checksum (ISO 13616): move the first four characters to
|
|
117
|
+
* the end, convert letters to numbers (A=10 … Z=35), and the whole
|
|
118
|
+
* number must be ≡ 1 (mod 97). Computed digit-by-digit to stay within
|
|
119
|
+
* safe integer range regardless of IBAN length.
|
|
120
|
+
*/
|
|
121
|
+
declare function ibanChecksum(iban: string): boolean;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* True when year/month/day form a real calendar date (leap years included).
|
|
125
|
+
* Month and day are 1-based.
|
|
126
|
+
*/
|
|
127
|
+
declare function isRealDate(year: number, month: number, day: number): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Validate the date part of a Swedish identity number.
|
|
130
|
+
*
|
|
131
|
+
* @param digits - The identity number's digits (10 or 12, separators removed).
|
|
132
|
+
* @param dayOffset - 60 for samordningsnummer (day is stored as day + 60).
|
|
133
|
+
*/
|
|
134
|
+
declare function isValidIdentityDate(digits: string, dayOffset?: number): boolean;
|
|
135
|
+
|
|
136
|
+
declare function jaroWinkler(s1: string, s2: string): number;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Confidence levels, Presidio-style: recognizers report how sure they
|
|
140
|
+
* are, callers filter with `scoreThreshold`.
|
|
141
|
+
*/
|
|
142
|
+
declare const SCORE: {
|
|
143
|
+
/** Checksum (Luhn / mod-97) or calendar validation passed. */
|
|
144
|
+
readonly VALIDATED: 0.95;
|
|
145
|
+
/** Exact hit in a curated gazetteer (streets, municipalities). */
|
|
146
|
+
readonly EXACT_MATCH: 0.9;
|
|
147
|
+
/** Fuzzy full-name match or context-corroborated shape. */
|
|
148
|
+
readonly CONTEXT: 0.85;
|
|
149
|
+
/** Exact single-word lookup in a large list (names). */
|
|
150
|
+
readonly LOOKUP: 0.55;
|
|
151
|
+
/** A plain pattern/term match with no further evidence. */
|
|
152
|
+
readonly PATTERN: 0.6;
|
|
153
|
+
/** Heuristic shape (capitalized words + street suffix). */
|
|
154
|
+
readonly HEURISTIC: 0.5;
|
|
155
|
+
/** Shape matched but its checksum failed. */
|
|
156
|
+
readonly FAILED_VALIDATION: 0.45;
|
|
157
|
+
/** Shape matched but the expected nearby context is missing. */
|
|
158
|
+
readonly NO_CONTEXT: 0.25;
|
|
159
|
+
};
|
|
160
|
+
/** Entities scoring below this are dropped unless the caller opts in. */
|
|
161
|
+
declare const DEFAULT_SCORE_THRESHOLD = 0.4;
|
|
162
|
+
|
|
163
|
+
export { DEFAULT_SCORE_THRESHOLD, type DetectOptions, type Detector, type EntitySpan, type MaskResult, type MaskedData, type MaskedValue, type PiiEntity, type PiiLabel, SCORE, detectPII, detectors, ibanChecksum, isRealDate, isValidIdentityDate, jaroWinkler, luhnCheck, maskPII, matchFullName, swedishIdChecksum };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/** All PII labels this library can detect. */
|
|
2
|
+
type PiiLabel = "PER_FIRST" | "PER_LAST" | "AMEX_CREDIT_CARD" | "MASTERCARD_CREDIT_CARD" | "VISA_CREDIT_CARD" | "IBAN_CODE" | "BIC_CODE" | "SE_BANK_NUMBER" | "SE_BANKGIRO" | "SE_PLUSGIRO" | "SE_VAT_NUMBER" | "CRYPTO_WALLET" | "SE_PERSONAL_IDENTITY_NUMBER_MALE" | "SE_PERSONAL_IDENTITY_NUMBER_FEMALE" | "SE_COORDINATION_NUMBER_MALE" | "SE_COORDINATION_NUMBER_FEMALE" | "SE_PASSPORT_NUMBER" | "EMAIL_ADDRESS" | "PHONE_NUMBER" | "SOCIAL_MEDIA" | "SE_STREET_ADDRESS" | "SE_POSTAL_CODE" | "SE_MUNICIPALITY" | "SE_COUNTY" | "SE_CITY" | "SE_PROPERTY_DESIGNATION" | "COORDINATE" | "SE_WORK_ORGANIZATION" | "SE_EDUCATION_ORGANIZATION" | "SE_EDUCATION_PROGRAM" | "SE_WORK_PROFESSION" | "SE_ORGANIZATION_NUMBER" | "MARITAL_STATUS" | "GENETIC_SEX" | "DISABILITY" | "RELIGION" | "SEXUAL_ORIENTATION" | "DEMOGRAPHIC" | "POLITICAL_IDEOLOGIES" | "SE_LABOR_UNION" | "SE_LICENSE_PLATE" | "IP_ADDRESS" | "MAC_ADDRESS" | "DATE" | "TIME" | "SE_CASE_NUMBER" | "AGE";
|
|
3
|
+
/** A single detected PII entity, anchored to the original text. */
|
|
4
|
+
interface PiiEntity {
|
|
5
|
+
/** Unique id within one detection run, e.g. `PHONE_NUMBER_1`. */
|
|
6
|
+
id: string;
|
|
7
|
+
label: PiiLabel;
|
|
8
|
+
/** The matched text exactly as it appears in the input. */
|
|
9
|
+
value: string;
|
|
10
|
+
/** Start offset (inclusive) in the original text. */
|
|
11
|
+
start: number;
|
|
12
|
+
/** End offset (exclusive) in the original text. */
|
|
13
|
+
end: number;
|
|
14
|
+
/**
|
|
15
|
+
* Confidence in (0, 1]. Checksum-validated matches score ~0.95,
|
|
16
|
+
* exact gazetteer hits ~0.85–0.9, plain pattern shapes ~0.6, matches
|
|
17
|
+
* that failed a checksum ~0.45, and context-starved shapes ~0.25
|
|
18
|
+
* (below the default threshold).
|
|
19
|
+
*/
|
|
20
|
+
score: number;
|
|
21
|
+
}
|
|
22
|
+
/** An entity candidate produced by a detector, before ids are assigned. */
|
|
23
|
+
type EntitySpan = Omit<PiiEntity, "id">;
|
|
24
|
+
interface DetectOptions {
|
|
25
|
+
/**
|
|
26
|
+
* When true, matches that carry a checksum (credit cards, personal
|
|
27
|
+
* identity numbers, organization numbers, IBANs) must pass validation,
|
|
28
|
+
* and date-based numbers must encode a real calendar date.
|
|
29
|
+
* Defaults to false so that synthetic/example data is still masked.
|
|
30
|
+
*/
|
|
31
|
+
strict?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Entities scoring below this are dropped. Defaults to
|
|
34
|
+
* DEFAULT_SCORE_THRESHOLD (0.4): checksum failures (0.45) survive,
|
|
35
|
+
* context-starved shapes (0.25) do not. Lower it to catch e.g. bank
|
|
36
|
+
* account numbers in bare CSV columns with no surrounding words;
|
|
37
|
+
* raise it to keep only high-confidence entities.
|
|
38
|
+
* Must be a finite number from 0 through 1; other values throw a
|
|
39
|
+
* RangeError.
|
|
40
|
+
*/
|
|
41
|
+
scoreThreshold?: number;
|
|
42
|
+
}
|
|
43
|
+
/** A detector produces entity spans for one or more labels. */
|
|
44
|
+
interface Detector {
|
|
45
|
+
/** The label(s) this detector can emit — used for documentation/tooling. */
|
|
46
|
+
labels: readonly PiiLabel[];
|
|
47
|
+
detect(text: string, options: Required<DetectOptions>): EntitySpan[];
|
|
48
|
+
}
|
|
49
|
+
interface MaskedValue {
|
|
50
|
+
id: string;
|
|
51
|
+
value: string;
|
|
52
|
+
}
|
|
53
|
+
/** Detected values grouped per label, in order of appearance. */
|
|
54
|
+
type MaskedData = Partial<Record<PiiLabel, MaskedValue[]>>;
|
|
55
|
+
interface MaskResult {
|
|
56
|
+
/** Input text with every detected entity replaced by `<LABEL_n>`. */
|
|
57
|
+
maskedText: string;
|
|
58
|
+
/** Detected values grouped per label. */
|
|
59
|
+
maskedData: MaskedData;
|
|
60
|
+
/** Flat list of detected entities with offsets into the original text. */
|
|
61
|
+
entities: PiiEntity[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Detectors in priority order: when two detectors match overlapping
|
|
66
|
+
* text, the one listed first wins. Names run first, then patterns from
|
|
67
|
+
* most to least specific — in particular, the generic Swedish bank
|
|
68
|
+
* account number (10–11 digits) runs after personnummer, organization
|
|
69
|
+
* numbers, cards and phone numbers, all of which it would otherwise
|
|
70
|
+
* shadow.
|
|
71
|
+
*/
|
|
72
|
+
declare const detectors: Detector[];
|
|
73
|
+
/**
|
|
74
|
+
* Run all detectors in priority order. Each detector sees the input
|
|
75
|
+
* with higher-priority matches blanked out (never with placeholder text,
|
|
76
|
+
* so placeholders can't be re-matched or corrupted). Blanking — rather
|
|
77
|
+
* than dropping overlapping candidates outright — lets a lower-priority
|
|
78
|
+
* detector still match the uncovered remainder: in "Lilla Vägen 7",
|
|
79
|
+
* where "Lilla" is a registered first name, the street detector can
|
|
80
|
+
* still mask "Vägen 7".
|
|
81
|
+
*
|
|
82
|
+
* Every candidate carries a confidence score; candidates below
|
|
83
|
+
* `scoreThreshold` are discarded before overlap resolution.
|
|
84
|
+
*/
|
|
85
|
+
declare function detectPII(text: string, options?: DetectOptions): PiiEntity[];
|
|
86
|
+
/**
|
|
87
|
+
* Mask every detected entity in `text`, replacing it with `<LABEL_n>`.
|
|
88
|
+
* The returned `maskedData` maps each label to its values in order of
|
|
89
|
+
* appearance, and `entities` carries exact offsets into the input.
|
|
90
|
+
*/
|
|
91
|
+
declare function maskPII(text: string, options?: DetectOptions): MaskResult;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Match a "First Last" string against the name lists. Returns the
|
|
95
|
+
* canonical matched names, or null when either part misses.
|
|
96
|
+
*/
|
|
97
|
+
declare function matchFullName(fullName: string): {
|
|
98
|
+
first: string;
|
|
99
|
+
last: string;
|
|
100
|
+
} | null;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Luhn (mod 10) checksum, used by payment card numbers, Swedish personal
|
|
104
|
+
* identity numbers (personnummer), coordination numbers and organization
|
|
105
|
+
* numbers.
|
|
106
|
+
*/
|
|
107
|
+
declare function luhnCheck(digits: string): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Validate the checksum of a Swedish personnummer / samordningsnummer /
|
|
110
|
+
* organisationsnummer. The Luhn sum is always computed over the final
|
|
111
|
+
* 10 digits (century digits are excluded).
|
|
112
|
+
*/
|
|
113
|
+
declare function swedishIdChecksum(value: string): boolean;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* IBAN mod-97 checksum (ISO 13616): move the first four characters to
|
|
117
|
+
* the end, convert letters to numbers (A=10 … Z=35), and the whole
|
|
118
|
+
* number must be ≡ 1 (mod 97). Computed digit-by-digit to stay within
|
|
119
|
+
* safe integer range regardless of IBAN length.
|
|
120
|
+
*/
|
|
121
|
+
declare function ibanChecksum(iban: string): boolean;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* True when year/month/day form a real calendar date (leap years included).
|
|
125
|
+
* Month and day are 1-based.
|
|
126
|
+
*/
|
|
127
|
+
declare function isRealDate(year: number, month: number, day: number): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Validate the date part of a Swedish identity number.
|
|
130
|
+
*
|
|
131
|
+
* @param digits - The identity number's digits (10 or 12, separators removed).
|
|
132
|
+
* @param dayOffset - 60 for samordningsnummer (day is stored as day + 60).
|
|
133
|
+
*/
|
|
134
|
+
declare function isValidIdentityDate(digits: string, dayOffset?: number): boolean;
|
|
135
|
+
|
|
136
|
+
declare function jaroWinkler(s1: string, s2: string): number;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Confidence levels, Presidio-style: recognizers report how sure they
|
|
140
|
+
* are, callers filter with `scoreThreshold`.
|
|
141
|
+
*/
|
|
142
|
+
declare const SCORE: {
|
|
143
|
+
/** Checksum (Luhn / mod-97) or calendar validation passed. */
|
|
144
|
+
readonly VALIDATED: 0.95;
|
|
145
|
+
/** Exact hit in a curated gazetteer (streets, municipalities). */
|
|
146
|
+
readonly EXACT_MATCH: 0.9;
|
|
147
|
+
/** Fuzzy full-name match or context-corroborated shape. */
|
|
148
|
+
readonly CONTEXT: 0.85;
|
|
149
|
+
/** Exact single-word lookup in a large list (names). */
|
|
150
|
+
readonly LOOKUP: 0.55;
|
|
151
|
+
/** A plain pattern/term match with no further evidence. */
|
|
152
|
+
readonly PATTERN: 0.6;
|
|
153
|
+
/** Heuristic shape (capitalized words + street suffix). */
|
|
154
|
+
readonly HEURISTIC: 0.5;
|
|
155
|
+
/** Shape matched but its checksum failed. */
|
|
156
|
+
readonly FAILED_VALIDATION: 0.45;
|
|
157
|
+
/** Shape matched but the expected nearby context is missing. */
|
|
158
|
+
readonly NO_CONTEXT: 0.25;
|
|
159
|
+
};
|
|
160
|
+
/** Entities scoring below this are dropped unless the caller opts in. */
|
|
161
|
+
declare const DEFAULT_SCORE_THRESHOLD = 0.4;
|
|
162
|
+
|
|
163
|
+
export { DEFAULT_SCORE_THRESHOLD, type DetectOptions, type Detector, type EntitySpan, type MaskResult, type MaskedData, type MaskedValue, type PiiEntity, type PiiLabel, SCORE, detectPII, detectors, ibanChecksum, isRealDate, isValidIdentityDate, jaroWinkler, luhnCheck, maskPII, matchFullName, swedishIdChecksum };
|