us-ssn-tools 1.0.0 β 2.0.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/README.md +108 -103
- package/dist/generate.d.ts +7 -11
- package/dist/generate.js +23 -28
- package/dist/index.d.ts +2 -3
- package/dist/index.js +3 -5
- package/dist/mask.d.ts +23 -15
- package/dist/mask.js +17 -42
- package/dist/normalize.d.ts +30 -12
- package/dist/normalize.js +34 -53
- package/dist/utils.d.ts +6 -1
- package/dist/utils.js +11 -1
- package/dist/validate.d.ts +13 -31
- package/dist/validate.js +120 -155
- package/dist/yup.d.ts +8 -3
- package/dist/yup.js +30 -19
- package/dist/zod.d.ts +10 -3
- package/dist/zod.js +34 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,16 +4,18 @@ A small, well-tested TypeScript library for **working with U.S. Social Security
|
|
|
4
4
|
|
|
5
5
|
It provides:
|
|
6
6
|
|
|
7
|
-
* β
**Validation** (strict + typing-as-you-go)
|
|
8
|
-
* π **Normalization** (
|
|
9
|
-
* π **Masking** (
|
|
7
|
+
* β
**Validation** (strict + typing-as-you-go, boolean API)
|
|
8
|
+
* π **Normalization** (deterministic, UI-friendly formatting)
|
|
9
|
+
* π **Masking** (privacy-first, best-effort, never validates)
|
|
10
10
|
* π² **Generation** (pre-2011, post-2011, random, and publicly advertised SSNs)
|
|
11
11
|
* π§© **Zod & Yup adapters** for form validation
|
|
12
12
|
|
|
13
|
-
> **Design
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
> Normalization
|
|
13
|
+
> **Design principles**
|
|
14
|
+
>
|
|
15
|
+
> * Validation answers *βis this valid?β* β nothing more.
|
|
16
|
+
> * Normalization formats input for display and typing UX.
|
|
17
|
+
> * Masking never leaks digits and never enforces validity.
|
|
18
|
+
> * Public safety beats convenience.
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
@@ -35,8 +37,8 @@ yarn add us-ssn-tools
|
|
|
35
37
|
|
|
36
38
|
```ts
|
|
37
39
|
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
isValidSsn,
|
|
41
|
+
normaliseSsn,
|
|
40
42
|
formatSsnFromDigits,
|
|
41
43
|
maskSsn,
|
|
42
44
|
generateSsn,
|
|
@@ -50,81 +52,86 @@ import { yupSsnTyping, yupSsnSubmit } from "us-ssn-tools/yup";
|
|
|
50
52
|
|
|
51
53
|
## Validation
|
|
52
54
|
|
|
53
|
-
### `
|
|
55
|
+
### `isValidSsn(input, options): boolean`
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
Checks whether an SSN is valid according to U.S. rules.
|
|
58
|
+
|
|
59
|
+
* Returns **`true` or `false` only**
|
|
60
|
+
* Does **not** normalize
|
|
61
|
+
* Can validate *partial input* (βvalid so farβ)
|
|
56
62
|
|
|
57
63
|
```ts
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (result.ok) {
|
|
61
|
-
console.log(result.normalized); // "123-45-6789"
|
|
62
|
-
} else {
|
|
63
|
-
console.log(result.error); // e.g. "INVALID_AREA"
|
|
64
|
-
console.log(result.message); // human-readable explanation
|
|
65
|
-
}
|
|
64
|
+
isValidSsn("123-45-6789"); // true
|
|
65
|
+
isValidSsn("123456789"); // false (dashes required by default)
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
### Options
|
|
69
69
|
|
|
70
70
|
```ts
|
|
71
71
|
type ValidateSsnOptions = {
|
|
72
|
-
|
|
73
|
-
allowPartial?: boolean;
|
|
74
|
-
ruleMode?: "pre2011" | "post2011"
|
|
72
|
+
requireDashes?: boolean; // default: true
|
|
73
|
+
allowPartial?: boolean; // default: false
|
|
74
|
+
ruleMode?: "pre2011" | "post2011"; // default: "post2011"
|
|
75
75
|
};
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
### Examples
|
|
79
79
|
|
|
80
80
|
```ts
|
|
81
|
-
|
|
81
|
+
isValidSsn("9", { allowPartial: true });
|
|
82
|
+
// true (still potentially valid)
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
//
|
|
84
|
+
isValidSsn("900", { allowPartial: true });
|
|
85
|
+
// false (invalid area)
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
//
|
|
87
|
+
isValidSsn("773-12-3456", { ruleMode: "pre2011" });
|
|
88
|
+
// false
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
//
|
|
90
|
+
isValidSsn("773-12-3456", { ruleMode: "post2011" });
|
|
91
|
+
// true
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
---
|
|
94
95
|
|
|
95
96
|
## Normalization
|
|
96
97
|
|
|
97
|
-
### `
|
|
98
|
+
### `normaliseSsn(input, options): string`
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
Formats input for **UI display**.
|
|
101
|
+
It does **not validate** and never throws.
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
* Extracts digits
|
|
104
|
+
* Optionally inserts dashes
|
|
105
|
+
* Supports typing-as-you-go
|
|
106
|
+
* Allows overflow digits if desired
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
```ts
|
|
109
|
+
normaliseSsn("1234"); // "123-4"
|
|
110
|
+
normaliseSsn("123456789"); // "123-45-6789"
|
|
111
|
+
normaliseSsn("SSN: 12a3"); // "123"
|
|
109
112
|
```
|
|
110
113
|
|
|
111
114
|
### Options
|
|
112
115
|
|
|
113
116
|
```ts
|
|
114
|
-
type
|
|
115
|
-
allowPartial?: boolean;
|
|
116
|
-
|
|
117
|
+
type NormaliseSsnOptions = {
|
|
118
|
+
allowPartial?: boolean; // default: true
|
|
119
|
+
digitsOnly?: boolean; // default: false
|
|
120
|
+
enforceLength?: boolean; // default: false
|
|
117
121
|
};
|
|
118
122
|
```
|
|
119
123
|
|
|
120
124
|
### Examples
|
|
121
125
|
|
|
122
126
|
```ts
|
|
123
|
-
|
|
124
|
-
//
|
|
127
|
+
normaliseSsn("123456789", { digitsOnly: true });
|
|
128
|
+
// "123456789"
|
|
129
|
+
|
|
130
|
+
normaliseSsn("12345678999");
|
|
131
|
+
// "123-45-678999"
|
|
125
132
|
|
|
126
|
-
|
|
127
|
-
//
|
|
133
|
+
normaliseSsn("12345678999", { enforceLength: true });
|
|
134
|
+
// "123-45-6789"
|
|
128
135
|
```
|
|
129
136
|
|
|
130
137
|
---
|
|
@@ -134,7 +141,6 @@ normalizeSsnInput("123-45-6", { allowPartial: true });
|
|
|
134
141
|
### `formatSsnFromDigits(digits)`
|
|
135
142
|
|
|
136
143
|
Formats a **digit string** into SSN shape.
|
|
137
|
-
This function **does not validate**.
|
|
138
144
|
|
|
139
145
|
```ts
|
|
140
146
|
formatSsnFromDigits("123"); // "123"
|
|
@@ -142,16 +148,20 @@ formatSsnFromDigits("1234"); // "123-4"
|
|
|
142
148
|
formatSsnFromDigits("123456789"); // "123-45-6789"
|
|
143
149
|
```
|
|
144
150
|
|
|
145
|
-
|
|
151
|
+
No validation is performed.
|
|
146
152
|
|
|
147
153
|
---
|
|
148
154
|
|
|
149
155
|
## Masking (UI-safe)
|
|
150
156
|
|
|
151
|
-
### `maskSsn(input, options)`
|
|
157
|
+
### `maskSsn(input, options): string`
|
|
158
|
+
|
|
159
|
+
Masks digits after normalization.
|
|
152
160
|
|
|
153
|
-
|
|
154
|
-
|
|
161
|
+
* Always normalizes first
|
|
162
|
+
* Never validates
|
|
163
|
+
* Never reveals digits unless explicitly allowed
|
|
164
|
+
* Safe for **any** UI context
|
|
155
165
|
|
|
156
166
|
```ts
|
|
157
167
|
maskSsn("123-45-6789");
|
|
@@ -165,87 +175,80 @@ maskSsn("123-45-6789", { revealLast4: true });
|
|
|
165
175
|
|
|
166
176
|
```ts
|
|
167
177
|
type MaskSsnOptions = {
|
|
168
|
-
allowPartial?: boolean;
|
|
169
|
-
revealLast4?: boolean;
|
|
170
|
-
maskChar?: string;
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
allowPartial?: boolean; // default: true
|
|
179
|
+
revealLast4?: boolean; // default: false
|
|
180
|
+
maskChar?: string; // default: "*"
|
|
181
|
+
digitsOnly?: boolean; // default: false
|
|
182
|
+
enforceLength?: boolean; // default: false
|
|
173
183
|
};
|
|
174
184
|
```
|
|
175
185
|
|
|
176
|
-
###
|
|
186
|
+
### Typing behavior
|
|
177
187
|
|
|
178
188
|
```ts
|
|
179
|
-
maskSsn("123"
|
|
189
|
+
maskSsn("123");
|
|
180
190
|
// "***"
|
|
181
191
|
|
|
182
|
-
maskSsn("123-45-6", {
|
|
192
|
+
maskSsn("123-45-6", { revealLast4: true });
|
|
183
193
|
// "***-**-6"
|
|
184
194
|
```
|
|
185
195
|
|
|
186
|
-
###
|
|
196
|
+
### Guarantees
|
|
187
197
|
|
|
188
|
-
* βοΈ
|
|
198
|
+
* βοΈ Digits are **always masked**
|
|
189
199
|
* βοΈ Dashes are never masked
|
|
190
|
-
* βοΈ
|
|
200
|
+
* βοΈ Invalid input is still safely masked
|
|
201
|
+
* βοΈ No validation logic inside masking
|
|
191
202
|
|
|
192
203
|
---
|
|
193
204
|
|
|
194
205
|
## Generation
|
|
195
206
|
|
|
196
|
-
### `generateSsn(options)`
|
|
197
|
-
Absolutely β thatβs an important point, and itβs worth stating **clearly but professionally**, without sounding alarmist.
|
|
207
|
+
### `generateSsn(options): string`
|
|
198
208
|
|
|
199
|
-
|
|
209
|
+
Generates SSNs for testing and development.
|
|
200
210
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
```ts
|
|
212
|
+
generateSsn();
|
|
213
|
+
// one of the publicly advertised SSNs
|
|
214
|
+
```
|
|
204
215
|
|
|
205
|
-
|
|
216
|
+
### Options
|
|
206
217
|
|
|
207
218
|
```ts
|
|
208
|
-
|
|
209
|
-
//
|
|
219
|
+
type GenerateSsnOptions = {
|
|
220
|
+
mode?: "public" | "pre2011" | "post2011" | "any"; // default: "public"
|
|
221
|
+
digitsOnly?: boolean; // default: false
|
|
222
|
+
};
|
|
210
223
|
```
|
|
211
224
|
|
|
212
225
|
### β οΈ Important note on public usage
|
|
213
226
|
|
|
214
227
|
> **Only use `mode: "public"` for any SSNs that may be displayed publicly.**
|
|
215
228
|
|
|
216
|
-
SSNs generated with `"pre2011"`, `"post2011"`, or `"any"`
|
|
217
|
-
|
|
218
|
-
If such a value is:
|
|
229
|
+
SSNs generated with `"pre2011"`, `"post2011"`, or `"any"` are **syntactically valid** and may correspond to **real individuals**.
|
|
219
230
|
|
|
220
|
-
|
|
221
|
-
* shown in screenshots
|
|
222
|
-
* logged to public consoles
|
|
223
|
-
* included in sample data or demos
|
|
231
|
+
If such values are:
|
|
224
232
|
|
|
225
|
-
|
|
233
|
+
* shown in documentation
|
|
234
|
+
* used in screenshots
|
|
235
|
+
* logged publicly
|
|
236
|
+
* included in demos or examples
|
|
226
237
|
|
|
227
|
-
|
|
238
|
+
you risk **exposing a real person to identity theft**.
|
|
228
239
|
|
|
229
|
-
|
|
240
|
+
The `"public"` mode returns **historically documented, publicly advertised SSNs** that are known to be unsafe for real-world use but safe for examples.
|
|
230
241
|
|
|
231
242
|
```ts
|
|
232
|
-
// β
Safe for docs, demos, screenshots
|
|
243
|
+
// β
Safe for docs, demos, screenshots
|
|
233
244
|
generateSsn({ mode: "public" });
|
|
234
245
|
|
|
235
|
-
// β
|
|
246
|
+
// β Never display publicly
|
|
236
247
|
generateSsn({ mode: "any" });
|
|
237
248
|
generateSsn({ mode: "pre2011" });
|
|
238
249
|
generateSsn({ mode: "post2011" });
|
|
239
250
|
```
|
|
240
251
|
|
|
241
|
-
Use non-public modes **only** for:
|
|
242
|
-
|
|
243
|
-
* internal testing
|
|
244
|
-
* private development environments
|
|
245
|
-
* automated test data that is never exposed
|
|
246
|
-
|
|
247
|
-
This distinction exists to protect real people, not just to satisfy validation rules.
|
|
248
|
-
|
|
249
252
|
---
|
|
250
253
|
|
|
251
254
|
## Zod Adapters
|
|
@@ -256,7 +259,7 @@ This distinction exists to protect real people, not just to satisfy validation r
|
|
|
256
259
|
const schema = zodSsnTyping();
|
|
257
260
|
|
|
258
261
|
schema.parse("1234"); // "123-4"
|
|
259
|
-
schema.parse("
|
|
262
|
+
schema.parse("900"); // β throws
|
|
260
263
|
```
|
|
261
264
|
|
|
262
265
|
### Submit (strict)
|
|
@@ -284,14 +287,14 @@ await yupSsnSubmit().validate("123456789");
|
|
|
284
287
|
|
|
285
288
|
## Testing & Guarantees
|
|
286
289
|
|
|
287
|
-
* βοΈ
|
|
288
|
-
* βοΈ
|
|
289
|
-
* βοΈ Strict separation between:
|
|
290
|
+
* βοΈ Extensive table-driven Jest tests
|
|
291
|
+
* βοΈ Clear separation between:
|
|
290
292
|
|
|
291
293
|
* validation
|
|
292
|
-
*
|
|
294
|
+
* normalization
|
|
293
295
|
* masking
|
|
294
296
|
* generation
|
|
297
|
+
* βοΈ UI-safe defaults everywhere
|
|
295
298
|
|
|
296
299
|
---
|
|
297
300
|
|
|
@@ -299,7 +302,7 @@ await yupSsnSubmit().validate("123456789");
|
|
|
299
302
|
|
|
300
303
|
* β No storage or encryption
|
|
301
304
|
* β No non-US SSNs (yet)
|
|
302
|
-
* β No
|
|
305
|
+
* β No mutation of user input beyond formatting
|
|
303
306
|
|
|
304
307
|
---
|
|
305
308
|
|
|
@@ -307,13 +310,15 @@ await yupSsnSubmit().validate("123456789");
|
|
|
307
310
|
|
|
308
311
|
ISC
|
|
309
312
|
|
|
313
|
+
---
|
|
314
|
+
|
|
310
315
|
## Support This Project
|
|
311
316
|
|
|
312
|
-
If you find this project useful, consider supporting
|
|
317
|
+
If you find this project useful, consider supporting it:
|
|
313
318
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
319
|
+
* [Sponsor on GitHub](https://github.com/sponsors/backupbrain)
|
|
320
|
+
* [Buy Me a Coffee](https://www.buymeacoffee.com/backupbrain)
|
|
321
|
+
* [Ko-fi](https://ko-fi.com/backupbrain)
|
|
322
|
+
* [Thanks.dev](https://thanks.dev/u/gh/backupbrain)
|
|
318
323
|
|
|
319
|
-
Your support is greatly appreciated
|
|
324
|
+
Your support is greatly appreciated π
|
package/dist/generate.d.ts
CHANGED
|
@@ -2,22 +2,18 @@ export type GenerateSsnMode = 'pre2011' | 'post2011' | 'any' | 'public';
|
|
|
2
2
|
export interface GenerateSsnOptions {
|
|
3
3
|
/**
|
|
4
4
|
* What kind of SSN to generate.
|
|
5
|
-
* - "
|
|
5
|
+
* - "public" (default): returns one of the publicly-advertised SSNs (intentionally invalid)
|
|
6
|
+
* - "any": may produce either pre2011-valid or post2011-valid
|
|
6
7
|
* - "pre2011": uses stricter pre-2011 area rules
|
|
7
8
|
* - "post2011": uses post-2011 relaxed area rules
|
|
8
|
-
* - "public": returns one of the publicly-advertised SSNs (intentionally invalid)
|
|
9
9
|
*/
|
|
10
10
|
mode?: GenerateSsnMode;
|
|
11
|
-
/** Output format (default: "dashed") */
|
|
12
|
-
format?: 'dashed' | 'digits';
|
|
13
11
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* If true, output is digits-only (#########).
|
|
13
|
+
* If false, output is dashed (###-##-####).
|
|
14
|
+
*
|
|
15
|
+
* Default: false
|
|
16
16
|
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* For mode="public": choose a specific advertised SSN, otherwise random.
|
|
20
|
-
*/
|
|
21
|
-
publicValue?: '078-05-1120' | '721-07-4426' | '219-09-9999';
|
|
17
|
+
digitsOnly?: boolean;
|
|
22
18
|
}
|
|
23
19
|
export declare function generateSsn(opts?: GenerateSsnOptions): string;
|
package/dist/generate.js
CHANGED
|
@@ -7,28 +7,27 @@ const PUBLICLY_ADVERTISED = [
|
|
|
7
7
|
'219-09-9999',
|
|
8
8
|
];
|
|
9
9
|
function generateSsn(opts = {}) {
|
|
10
|
-
var _a, _b
|
|
11
|
-
const mode = (_a = opts.mode) !== null && _a !== void 0 ? _a : '
|
|
12
|
-
const
|
|
13
|
-
const rng = (_c = opts.rng) !== null && _c !== void 0 ? _c : defaultRng;
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const mode = (_a = opts.mode) !== null && _a !== void 0 ? _a : 'public';
|
|
12
|
+
const digitsOnly = (_b = opts.digitsOnly) !== null && _b !== void 0 ? _b : false;
|
|
14
13
|
if (mode === 'public') {
|
|
15
|
-
const chosen =
|
|
16
|
-
return
|
|
14
|
+
const chosen = PUBLICLY_ADVERTISED[randomInt(0, PUBLICLY_ADVERTISED.length - 1)];
|
|
15
|
+
return digitsOnly ? chosen.replace(/-/g, '') : chosen;
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
const effectiveMode = mode === 'any' ? (randomFloat() < 0.5 ? 'pre2011' : 'post2011') : mode;
|
|
18
|
+
while (true) {
|
|
19
|
+
const area = generateArea(effectiveMode); // "001".."899" with constraints
|
|
20
|
+
const group = generateNonZeroFixedWidth(2); // "01".."99"
|
|
21
|
+
const serial = generateNonZeroFixedWidth(4); // "0001".."9999"
|
|
22
|
+
const dashed = `${area}-${group}-${serial}`;
|
|
23
|
+
// Avoid returning publicly advertised SSNs unless explicitly requested.
|
|
24
|
+
if (PUBLICLY_ADVERTISED.includes(dashed))
|
|
25
|
+
continue;
|
|
26
|
+
return digitsOnly ? dashed.replace(/-/g, '') : dashed;
|
|
27
27
|
}
|
|
28
|
-
return format === 'digits' ? dashed.replace(/-/g, '') : dashed;
|
|
29
28
|
}
|
|
30
29
|
/* ---------------------------- helpers ---------------------------- */
|
|
31
|
-
function generateArea(mode
|
|
30
|
+
function generateArea(mode) {
|
|
32
31
|
// Base rules always:
|
|
33
32
|
// - not 000
|
|
34
33
|
// - not 666
|
|
@@ -40,7 +39,7 @@ function generateArea(mode, rng) {
|
|
|
40
39
|
//
|
|
41
40
|
// Strategy: generate until it passes constraints (fast, tiny rejection rate).
|
|
42
41
|
while (true) {
|
|
43
|
-
const n = randomInt(
|
|
42
|
+
const n = randomInt(1, 899); // 001..899
|
|
44
43
|
if (n === 666)
|
|
45
44
|
continue;
|
|
46
45
|
if (mode === 'pre2011') {
|
|
@@ -52,28 +51,24 @@ function generateArea(mode, rng) {
|
|
|
52
51
|
return pad3(n);
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
|
-
function generateNonZeroFixedWidth(
|
|
54
|
+
function generateNonZeroFixedWidth(width) {
|
|
56
55
|
if (width === 2) {
|
|
57
|
-
// 01..99
|
|
58
|
-
const n = randomInt(rng, 1, 99);
|
|
56
|
+
const n = randomInt(1, 99); // 01..99
|
|
59
57
|
return n.toString().padStart(2, '0');
|
|
60
58
|
}
|
|
61
|
-
// 0001..9999
|
|
62
|
-
const n = randomInt(rng, 1, 9999);
|
|
59
|
+
const n = randomInt(1, 9999); // 0001..9999
|
|
63
60
|
return n.toString().padStart(4, '0');
|
|
64
61
|
}
|
|
65
62
|
function pad3(n) {
|
|
66
63
|
return n.toString().padStart(3, '0');
|
|
67
64
|
}
|
|
68
|
-
function randomInt(
|
|
65
|
+
function randomInt(min, max) {
|
|
69
66
|
// inclusive min/max
|
|
70
|
-
|
|
71
|
-
return Math.floor(r * (max - min + 1)) + min;
|
|
67
|
+
return Math.floor(randomFloat() * (max - min + 1)) + min;
|
|
72
68
|
}
|
|
73
|
-
function
|
|
69
|
+
function randomFloat() {
|
|
74
70
|
var _a;
|
|
75
71
|
// Prefer crypto when available; fall back to Math.random.
|
|
76
|
-
// Works in browsers; in Node 19+ crypto.getRandomValues exists on globalThis.crypto.
|
|
77
72
|
const g = globalThis;
|
|
78
73
|
if ((_a = g.crypto) === null || _a === void 0 ? void 0 : _a.getRandomValues) {
|
|
79
74
|
const buf = new Uint32Array(1);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { isValidSsn } from './validate';
|
|
2
|
+
export { normalizeSsn } from './normalize';
|
|
3
3
|
export { maskSsn } from './mask';
|
|
4
|
-
export { formatSsnFromDigits } from './utils';
|
|
5
4
|
export { generateSsn } from './generate';
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateSsn = exports.
|
|
3
|
+
exports.generateSsn = exports.maskSsn = exports.normalizeSsn = exports.isValidSsn = void 0;
|
|
4
4
|
// Exporting all functions
|
|
5
5
|
var validate_1 = require("./validate");
|
|
6
|
-
Object.defineProperty(exports, "
|
|
6
|
+
Object.defineProperty(exports, "isValidSsn", { enumerable: true, get: function () { return validate_1.isValidSsn; } });
|
|
7
7
|
var normalize_1 = require("./normalize");
|
|
8
|
-
Object.defineProperty(exports, "
|
|
8
|
+
Object.defineProperty(exports, "normalizeSsn", { enumerable: true, get: function () { return normalize_1.normalizeSsn; } });
|
|
9
9
|
var mask_1 = require("./mask");
|
|
10
10
|
Object.defineProperty(exports, "maskSsn", { enumerable: true, get: function () { return mask_1.maskSsn; } });
|
|
11
|
-
var utils_1 = require("./utils");
|
|
12
|
-
Object.defineProperty(exports, "formatSsnFromDigits", { enumerable: true, get: function () { return utils_1.formatSsnFromDigits; } });
|
|
13
11
|
var generate_1 = require("./generate");
|
|
14
12
|
Object.defineProperty(exports, "generateSsn", { enumerable: true, get: function () { return generate_1.generateSsn; } });
|
package/dist/mask.d.ts
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
export interface MaskSsnOptions {
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Allow masking partial SSNs (typing-as-you-go).
|
|
4
|
+
* Default: true
|
|
5
|
+
*/
|
|
3
6
|
allowPartial?: boolean;
|
|
4
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Reveal serial digits as they are typed:
|
|
9
|
+
* - before serial (<=5 digits): reveal nothing
|
|
10
|
+
* - serial (6..9 digits): reveal up to 4 digits
|
|
11
|
+
*
|
|
12
|
+
* Default: false
|
|
13
|
+
*/
|
|
5
14
|
revealLast4?: boolean;
|
|
6
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Mask character (default: "*").
|
|
17
|
+
* Only the first character is used.
|
|
18
|
+
*/
|
|
7
19
|
maskChar?: string;
|
|
8
|
-
/** Accept digit-only input (default: true). */
|
|
9
|
-
allowNoDashes?: boolean;
|
|
10
20
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - "normalize": always insert dashes in ###-##-#### style (even for digits-only)
|
|
14
|
-
*
|
|
15
|
-
* Default: "normalize"
|
|
21
|
+
* Output digits only (no dashes).
|
|
22
|
+
* Default: false
|
|
16
23
|
*/
|
|
17
|
-
|
|
24
|
+
digitsOnly?: boolean;
|
|
18
25
|
/**
|
|
19
|
-
* If true
|
|
20
|
-
*
|
|
21
|
-
*
|
|
26
|
+
* If true, cap to 9 digits (SSN length).
|
|
27
|
+
* If false, allow overflow digits (UI testing / paste scenarios).
|
|
28
|
+
*
|
|
29
|
+
* Default: false
|
|
22
30
|
*/
|
|
23
|
-
|
|
31
|
+
enforceLength?: boolean;
|
|
24
32
|
}
|
|
25
33
|
export declare function maskSsn(input: string, opts?: MaskSsnOptions): string;
|
package/dist/mask.js
CHANGED
|
@@ -5,51 +5,26 @@ const normalize_1 = require("./normalize");
|
|
|
5
5
|
const utils_1 = require("./utils");
|
|
6
6
|
function maskSsn(input, opts = {}) {
|
|
7
7
|
var _a, _b, _c, _d, _e;
|
|
8
|
-
const allowPartial = (_a = opts.allowPartial) !== null && _a !== void 0 ? _a :
|
|
8
|
+
const allowPartial = (_a = opts.allowPartial) !== null && _a !== void 0 ? _a : true;
|
|
9
9
|
const revealLast4 = (_b = opts.revealLast4) !== null && _b !== void 0 ? _b : false;
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// from the best-effort masked string is not possible (digits are already masked),
|
|
21
|
-
// so we simply return the best-effort output.
|
|
22
|
-
return masked;
|
|
23
|
-
}
|
|
24
|
-
const digits = normalized.digits; // 0..9 digits (partial) or 9 digits (full)
|
|
25
|
-
// Determine how many digits to reveal (last 4) if present.
|
|
10
|
+
const digitsOnly = (_c = opts.digitsOnly) !== null && _c !== void 0 ? _c : false;
|
|
11
|
+
const enforceLength = (_d = opts.enforceLength) !== null && _d !== void 0 ? _d : false;
|
|
12
|
+
const maskChar = ((_e = opts.maskChar) !== null && _e !== void 0 ? _e : '*').slice(0, 1) || '*';
|
|
13
|
+
// 1) Normalize β DIGITS ONLY
|
|
14
|
+
const digits = (0, normalize_1.normalizeSsn)(input, {
|
|
15
|
+
allowPartial,
|
|
16
|
+
digitsOnly: true,
|
|
17
|
+
enforceLength,
|
|
18
|
+
});
|
|
19
|
+
// 2) Mask
|
|
26
20
|
const total = digits.length;
|
|
27
|
-
//
|
|
28
|
-
const serialTyped = Math.max(0, total - 5);
|
|
21
|
+
// Reveal only serial digits (positions 6β9)
|
|
22
|
+
const serialTyped = Math.max(0, total - 5);
|
|
29
23
|
const revealCount = revealLast4 ? Math.min(4, serialTyped) : 0;
|
|
30
24
|
const maskedCount = Math.max(0, total - revealCount);
|
|
31
25
|
const maskedDigits = maskChar.repeat(maskedCount) + digits.slice(maskedCount);
|
|
32
|
-
// Output formatting
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
// dashMode === "preserve"
|
|
38
|
-
if (hadDashes) {
|
|
39
|
-
// Input had dashes -> keep dashed presentation (normalized positions).
|
|
40
|
-
return (0, utils_1.formatSsnFromDigits)(maskedDigits);
|
|
41
|
-
}
|
|
42
|
-
// Input had no dashes -> keep digits-only.
|
|
43
|
-
return maskedDigits;
|
|
44
|
-
}
|
|
45
|
-
function maskBestEffort(input, opts) {
|
|
46
|
-
const m = opts.maskChar;
|
|
47
|
-
let out = '';
|
|
48
|
-
for (const ch of input) {
|
|
49
|
-
if (ch >= '0' && ch <= '9')
|
|
50
|
-
out += m;
|
|
51
|
-
else
|
|
52
|
-
out += ch; // dashes and any other characters preserved
|
|
53
|
-
}
|
|
54
|
-
return out;
|
|
26
|
+
// 3) Output formatting
|
|
27
|
+
if (digitsOnly)
|
|
28
|
+
return maskedDigits;
|
|
29
|
+
return (0, utils_1.formatSsnWithOverflow)(maskedDigits);
|
|
55
30
|
}
|