saudi-utils 0.1.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 +220 -0
- package/dist/address.d.ts +12 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +118 -0
- package/dist/address.js.map +1 -0
- package/dist/banking.d.ts +6 -0
- package/dist/banking.d.ts.map +1 -0
- package/dist/banking.js +66 -0
- package/dist/banking.js.map +1 -0
- package/dist/business.d.ts +10 -0
- package/dist/business.d.ts.map +1 -0
- package/dist/business.js +73 -0
- package/dist/business.js.map +1 -0
- package/dist/identity.d.ts +11 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +84 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/ascii.d.ts +3 -0
- package/dist/internal/ascii.d.ts.map +1 -0
- package/dist/internal/ascii.js +9 -0
- package/dist/internal/ascii.js.map +1 -0
- package/dist/internal/identity-checksum.d.ts +3 -0
- package/dist/internal/identity-checksum.d.ts.map +1 -0
- package/dist/internal/identity-checksum.js +20 -0
- package/dist/internal/identity-checksum.js.map +1 -0
- package/dist/internal/input.d.ts +10 -0
- package/dist/internal/input.d.ts.map +1 -0
- package/dist/internal/input.js +10 -0
- package/dist/internal/input.js.map +1 -0
- package/dist/internal/mod97.d.ts +3 -0
- package/dist/internal/mod97.d.ts.map +1 -0
- package/dist/internal/mod97.js +14 -0
- package/dist/internal/mod97.js.map +1 -0
- package/dist/telecom.d.ts +9 -0
- package/dist/telecom.d.ts.map +1 -0
- package/dist/telecom.js +104 -0
- package/dist/telecom.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Saudi Utils contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Saudi Utils
|
|
2
|
+
|
|
3
|
+
`saudi-utils` is a small, strict TypeScript library for offline validation and explicit
|
|
4
|
+
normalization of common Saudi identity, banking, business, telecom, and National Address data.
|
|
5
|
+
It provides deterministic errors, evidence-labelled rules, and no runtime dependencies.
|
|
6
|
+
|
|
7
|
+
> [!IMPORTANT]
|
|
8
|
+
> A successful result proves only the documented offline structure and, where stated, checksum.
|
|
9
|
+
> It does **not** prove issuance, ownership, existence, activity, registration, account status,
|
|
10
|
+
> address existence, phone reachability, or any current authoritative status.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install saudi-utils
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
`saudi-utils` is ESM-only, supports Node.js 22 and newer, and includes TypeScript declarations.
|
|
21
|
+
There is no default export.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { formatIban, getSaudiIdType, normalizeIban, validateSaudiIban } from "saudi-utils";
|
|
25
|
+
|
|
26
|
+
getSaudiIdType("1000000008"); // "national-id" (synthetic checksum-valid example)
|
|
27
|
+
|
|
28
|
+
const iban = normalizeIban("sa03 8000 0000 6080 1016 7519");
|
|
29
|
+
// "SA0380000000608010167519"
|
|
30
|
+
|
|
31
|
+
validateSaudiIban(iban);
|
|
32
|
+
// { valid: true, value: "SA0380000000608010167519" }
|
|
33
|
+
|
|
34
|
+
formatIban(iban);
|
|
35
|
+
// "SA03 8000 0000 6080 1016 7519"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The package exposes one side-effect-free ESM root. Pure named exports and
|
|
39
|
+
`"sideEffects": false` support tree shaking. Runtime code uses standard JavaScript and has zero
|
|
40
|
+
runtime dependencies.
|
|
41
|
+
|
|
42
|
+
## Validation and normalization
|
|
43
|
+
|
|
44
|
+
Validators are deliberately strict. They accept `unknown`, but only primitive strings can be
|
|
45
|
+
valid identifiers. They do not trim, coerce, remove punctuation, change case, or translate
|
|
46
|
+
Unicode numerals. Canonical numeric values use ASCII digits and preserve leading zeroes.
|
|
47
|
+
|
|
48
|
+
Explicit normalizers accept only their documented variants:
|
|
49
|
+
|
|
50
|
+
- `normalizeIban` removes ASCII spaces, uppercases ASCII letters, then validates the result.
|
|
51
|
+
- `normalizePhoneNumber` accepts specific unseparated Saudi national and country-code forms.
|
|
52
|
+
- `normalizeShortAddress` uppercases ASCII letters and removes at most one permitted ASCII space.
|
|
53
|
+
|
|
54
|
+
All return `null` for unsupported or invalid input and are idempotent after success.
|
|
55
|
+
`formatIban` is only a formatter: it accepts a valid, canonical Saudi IBAN and returns `null` for
|
|
56
|
+
lowercase, spaced, invalid, or non-Saudi input. Call `normalizeIban` first when normalization is
|
|
57
|
+
required.
|
|
58
|
+
|
|
59
|
+
Detailed validators return the shared result:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
type ValidationResult =
|
|
63
|
+
{ valid: true; value: string } | { valid: false; code: ValidationErrorCode };
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Errors use deterministic precedence:
|
|
67
|
+
|
|
68
|
+
1. `REQUIRED`
|
|
69
|
+
2. `INVALID_TYPE`
|
|
70
|
+
3. `INVALID_LENGTH`
|
|
71
|
+
4. `INVALID_FORMAT`
|
|
72
|
+
5. `INVALID_PREFIX`
|
|
73
|
+
6. `INVALID_CHECKSUM`
|
|
74
|
+
|
|
75
|
+
`undefined`, `null`, and `""` are required failures. Whitespace is not silently treated as empty.
|
|
76
|
+
Boolean `is*` helpers are thin wrappers around their detailed validators.
|
|
77
|
+
|
|
78
|
+
## API reference
|
|
79
|
+
|
|
80
|
+
Every public value is a named root export. See
|
|
81
|
+
[the detailed API reference](https://github.com/alialaraby/saudi-utils/blob/main/docs/api.md) for
|
|
82
|
+
exact signatures, accepted representations, normalization rules, and return behavior.
|
|
83
|
+
|
|
84
|
+
| Domain | Public exports |
|
|
85
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| Identity | `isNationalId`, `validateNationalId`, `isIqama`, `validateIqama`, `isSaudiId`, `validateSaudiId`, `getSaudiIdType`, `isBorderId`, `validateBorderId` |
|
|
87
|
+
| Banking | `isSaudiIban`, `validateSaudiIban`, `normalizeIban`, `formatIban` |
|
|
88
|
+
| Business and tax | `isVatNumber`, `validateVatNumber`, `isTin`, `validateTin`, `isUnifiedNationalNumber`, `validateUnifiedNationalNumber`, `isCommercialRegistration`, `validateCommercialRegistration` |
|
|
89
|
+
| Telecom | `isMobileNumber`, `validateMobileNumber`, `isLandlineNumber`, `validateLandlineNumber`, `isTollFreeNumber`, `validateTollFreeNumber`, `normalizePhoneNumber` |
|
|
90
|
+
| National Address | `isPostalCode`, `validatePostalCode`, `isBuildingNumber`, `validateBuildingNumber`, `isAdditionalNumber`, `validateAdditionalNumber`, `isShortAddress`, `validateShortAddress`, `normalizeShortAddress`, `validateNationalAddress` |
|
|
91
|
+
| Types | `ValidationErrorCode`, `ValidationResult`, `ValidationEvidence`, `NormalizedSaudiPhone`, `NationalAddress`, `NationalAddressValidationResult` |
|
|
92
|
+
|
|
93
|
+
### Canonical representations
|
|
94
|
+
|
|
95
|
+
| Value | Accepted canonical representation | Checksum |
|
|
96
|
+
| ------------------------------ | ---------------------------------------------------------------------------------------- | --------------------------- |
|
|
97
|
+
| National ID | 10 ASCII digits beginning with `1` | Alternating-double checksum |
|
|
98
|
+
| Iqama | 10 ASCII digits beginning with `2` | Alternating-double checksum |
|
|
99
|
+
| Border ID | 10 ASCII digits beginning with `3` or `4` | None |
|
|
100
|
+
| Saudi IBAN | 24 uppercase ASCII alphanumeric characters beginning with `SA`; positions 3–6 are digits | ISO 13616 MOD-97-10 |
|
|
101
|
+
| VAT Registration Number | 15 ASCII digits beginning and ending with `3` | None |
|
|
102
|
+
| TIN | 10 ASCII digits | None |
|
|
103
|
+
| Unified National Number | 10 ASCII digits beginning with `7` | None |
|
|
104
|
+
| Legacy Commercial Registration | 10 ASCII digits | None |
|
|
105
|
+
| Mobile | `05XXXXXXXX` or `+9665XXXXXXXX` | None |
|
|
106
|
+
| Landline | National or `+966` form using `011`, `012`, `013`, `014`, `016`, or `017` | None |
|
|
107
|
+
| Toll-free | National `800XXXXXXX` only | None |
|
|
108
|
+
| Postal code | 5 ASCII digits | None |
|
|
109
|
+
| Building/additional number | 4 ASCII digits | None |
|
|
110
|
+
| Short Address | 4 uppercase ASCII letters followed by 4 ASCII digits | None |
|
|
111
|
+
|
|
112
|
+
### Evidence levels and important limitations
|
|
113
|
+
|
|
114
|
+
Rules are classified as `official-checksum`, `official-structural`,
|
|
115
|
+
`official-structural-community-checksum`, or `best-known-structural`. The complete source and
|
|
116
|
+
limitation register is in the
|
|
117
|
+
[validation evidence document](https://github.com/alialaraby/saudi-utils/blob/main/docs/evidence.md).
|
|
118
|
+
|
|
119
|
+
- **Border ID is provisional:** only the best-known 10-digit structure and `3`/`4` prefixes are
|
|
120
|
+
checked. No checksum or complete public normative schema is claimed.
|
|
121
|
+
- **TIN is structural only:** any 10 ASCII digits pass. No checksum or richer authoritative offline
|
|
122
|
+
grammar is claimed.
|
|
123
|
+
- **Commercial Registration means the legacy structural format:** the API checks only a 10-digit
|
|
124
|
+
legacy representation. It is not current registry verification and is not an alias for the
|
|
125
|
+
Unified National Number.
|
|
126
|
+
- Phone prefixes describe numbering structure, not the subscriber's current carrier. Number
|
|
127
|
+
portability makes current-carrier inference invalid.
|
|
128
|
+
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
The values labelled synthetic below demonstrate offline rules only; they are not presented as
|
|
132
|
+
issued identifiers. The repository's
|
|
133
|
+
[typechecked quick-start fixture](https://github.com/alialaraby/saudi-utils/blob/main/examples/quick-start.ts)
|
|
134
|
+
also executes its expected results.
|
|
135
|
+
|
|
136
|
+
### Identity
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { getSaudiIdType, validateBorderId, validateNationalId } from "saudi-utils";
|
|
140
|
+
|
|
141
|
+
validateNationalId("1000000008"); // { valid: true, value: "1000000008" } (synthetic)
|
|
142
|
+
getSaudiIdType("1000000008"); // "national-id"
|
|
143
|
+
validateBorderId("3000000000"); // structural success only (synthetic)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### IBAN
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { formatIban, normalizeIban } from "saudi-utils";
|
|
150
|
+
|
|
151
|
+
const iban = normalizeIban("sa03 8000 0000 6080 1016 7519");
|
|
152
|
+
formatIban(iban); // "SA03 8000 0000 6080 1016 7519"
|
|
153
|
+
formatIban("sa03 8000 0000 6080 1016 7519"); // null: formatting does not normalize
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Business and tax
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { validateTin, validateUnifiedNationalNumber, validateVatNumber } from "saudi-utils";
|
|
160
|
+
|
|
161
|
+
validateVatNumber("300000000000003").valid; // true: structural result
|
|
162
|
+
validateTin("0123456789").valid; // true: best-known structural result
|
|
163
|
+
validateUnifiedNationalNumber("7123456789").valid; // true: structural result
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Telecom
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { normalizePhoneNumber, validateMobileNumber } from "saudi-utils";
|
|
170
|
+
|
|
171
|
+
validateMobileNumber("0501234567").valid; // true: no reachability or carrier claim
|
|
172
|
+
normalizePhoneNumber("0501234567");
|
|
173
|
+
// { kind: "mobile", value: "+966501234567" }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### National Address
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { normalizeShortAddress, validateNationalAddress } from "saudi-utils";
|
|
180
|
+
|
|
181
|
+
normalizeShortAddress("abcd 0123"); // "ABCD0123"
|
|
182
|
+
|
|
183
|
+
validateNationalAddress({
|
|
184
|
+
buildingNumber: "0123",
|
|
185
|
+
street: "King Fahd Road",
|
|
186
|
+
district: "Al Olaya",
|
|
187
|
+
city: "Riyadh",
|
|
188
|
+
postalCode: "01234",
|
|
189
|
+
additionalNumber: "0000",
|
|
190
|
+
applicationField: "ignored",
|
|
191
|
+
});
|
|
192
|
+
// Success returns a fresh object containing only the six required fields.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`validateNationalAddress` requires those six own data properties, ignores extra properties, does
|
|
196
|
+
not invoke accessors, and returns a fresh object containing only required fields. Its specialized
|
|
197
|
+
`NationalAddressValidationResult` returns a `NationalAddress` object on success. Text fields must
|
|
198
|
+
be non-empty primitive strings but are not trimmed or linguistically restricted; cross-field and
|
|
199
|
+
address-existence checks are outside the contract.
|
|
200
|
+
|
|
201
|
+
## Development
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm ci
|
|
205
|
+
npm run docs:check
|
|
206
|
+
npm run check
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Available focused commands are `npm test`, `npm run test:coverage`, `npm run typecheck`,
|
|
210
|
+
`npm run lint`, `npm run format:check`, and `npm run build`. Node.js 22 or newer is required.
|
|
211
|
+
|
|
212
|
+
## Project policies
|
|
213
|
+
|
|
214
|
+
- [Validation evidence](https://github.com/alialaraby/saudi-utils/blob/main/docs/evidence.md)
|
|
215
|
+
- [API reference](https://github.com/alialaraby/saudi-utils/blob/main/docs/api.md)
|
|
216
|
+
- [Contributing](https://github.com/alialaraby/saudi-utils/blob/main/CONTRIBUTING.md)
|
|
217
|
+
- [Security policy](https://github.com/alialaraby/saudi-utils/blob/main/SECURITY.md)
|
|
218
|
+
- [Changelog](https://github.com/alialaraby/saudi-utils/blob/main/CHANGELOG.md)
|
|
219
|
+
- [Release process](https://github.com/alialaraby/saudi-utils/blob/main/docs/releasing.md)
|
|
220
|
+
- [MIT License](https://github.com/alialaraby/saudi-utils/blob/main/LICENSE)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NationalAddressValidationResult, ValidationResult } from "./types.js";
|
|
2
|
+
export declare function validatePostalCode(value: unknown): ValidationResult;
|
|
3
|
+
export declare function isPostalCode(value: unknown): value is string;
|
|
4
|
+
export declare function validateBuildingNumber(value: unknown): ValidationResult;
|
|
5
|
+
export declare function isBuildingNumber(value: unknown): value is string;
|
|
6
|
+
export declare function validateAdditionalNumber(value: unknown): ValidationResult;
|
|
7
|
+
export declare function isAdditionalNumber(value: unknown): value is string;
|
|
8
|
+
export declare function validateShortAddress(value: unknown): ValidationResult;
|
|
9
|
+
export declare function isShortAddress(value: unknown): value is string;
|
|
10
|
+
export declare function normalizeShortAddress(value: unknown): string | null;
|
|
11
|
+
export declare function validateNationalAddress(value: unknown): NationalAddressValidationResult;
|
|
12
|
+
//# sourceMappingURL=address.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,+BAA+B,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA6BpF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAEnE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAEvE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAEzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAElE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAgBrE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE9D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAUnE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,+BAA+B,CA8DvF"}
|
package/dist/address.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { isAsciiDigits } from "./internal/ascii.js";
|
|
2
|
+
import { checkStringInput } from "./internal/input.js";
|
|
3
|
+
const ADDRESS_FIELDS = [
|
|
4
|
+
"buildingNumber",
|
|
5
|
+
"street",
|
|
6
|
+
"district",
|
|
7
|
+
"city",
|
|
8
|
+
"postalCode",
|
|
9
|
+
"additionalNumber",
|
|
10
|
+
];
|
|
11
|
+
function validateFixedLengthDigits(value, length) {
|
|
12
|
+
const input = checkStringInput(value);
|
|
13
|
+
if (!input.valid) {
|
|
14
|
+
return input;
|
|
15
|
+
}
|
|
16
|
+
if (input.value.length !== length) {
|
|
17
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
18
|
+
}
|
|
19
|
+
if (!isAsciiDigits(input.value)) {
|
|
20
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
21
|
+
}
|
|
22
|
+
return { valid: true, value: input.value };
|
|
23
|
+
}
|
|
24
|
+
export function validatePostalCode(value) {
|
|
25
|
+
return validateFixedLengthDigits(value, 5);
|
|
26
|
+
}
|
|
27
|
+
export function isPostalCode(value) {
|
|
28
|
+
return validatePostalCode(value).valid;
|
|
29
|
+
}
|
|
30
|
+
export function validateBuildingNumber(value) {
|
|
31
|
+
return validateFixedLengthDigits(value, 4);
|
|
32
|
+
}
|
|
33
|
+
export function isBuildingNumber(value) {
|
|
34
|
+
return validateBuildingNumber(value).valid;
|
|
35
|
+
}
|
|
36
|
+
export function validateAdditionalNumber(value) {
|
|
37
|
+
return validateFixedLengthDigits(value, 4);
|
|
38
|
+
}
|
|
39
|
+
export function isAdditionalNumber(value) {
|
|
40
|
+
return validateAdditionalNumber(value).valid;
|
|
41
|
+
}
|
|
42
|
+
export function validateShortAddress(value) {
|
|
43
|
+
const input = checkStringInput(value);
|
|
44
|
+
if (!input.valid) {
|
|
45
|
+
return input;
|
|
46
|
+
}
|
|
47
|
+
if (input.value.length !== 8) {
|
|
48
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
49
|
+
}
|
|
50
|
+
if (!/^[A-Z]{4}[0-9]{4}$/.test(input.value)) {
|
|
51
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
52
|
+
}
|
|
53
|
+
return { valid: true, value: input.value };
|
|
54
|
+
}
|
|
55
|
+
export function isShortAddress(value) {
|
|
56
|
+
return validateShortAddress(value).valid;
|
|
57
|
+
}
|
|
58
|
+
export function normalizeShortAddress(value) {
|
|
59
|
+
if (typeof value !== "string") {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (!/^[A-Za-z]{4} ?[0-9]{4}$/.test(value)) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return `${value.slice(0, 4).toUpperCase()}${value.slice(-4)}`;
|
|
66
|
+
}
|
|
67
|
+
export function validateNationalAddress(value) {
|
|
68
|
+
if (value === undefined || value === null || value === "") {
|
|
69
|
+
return { valid: false, code: "REQUIRED" };
|
|
70
|
+
}
|
|
71
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
72
|
+
return { valid: false, code: "INVALID_TYPE" };
|
|
73
|
+
}
|
|
74
|
+
const fields = {};
|
|
75
|
+
for (const field of ADDRESS_FIELDS) {
|
|
76
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, field);
|
|
77
|
+
if (descriptor === undefined || !("value" in descriptor) || descriptor.value === "") {
|
|
78
|
+
return { valid: false, code: "REQUIRED" };
|
|
79
|
+
}
|
|
80
|
+
fields[field] = descriptor.value;
|
|
81
|
+
}
|
|
82
|
+
const buildingNumber = fields.buildingNumber;
|
|
83
|
+
const street = fields.street;
|
|
84
|
+
const district = fields.district;
|
|
85
|
+
const city = fields.city;
|
|
86
|
+
const postalCode = fields.postalCode;
|
|
87
|
+
const additionalNumber = fields.additionalNumber;
|
|
88
|
+
if (typeof buildingNumber !== "string" ||
|
|
89
|
+
typeof street !== "string" ||
|
|
90
|
+
typeof district !== "string" ||
|
|
91
|
+
typeof city !== "string" ||
|
|
92
|
+
typeof postalCode !== "string" ||
|
|
93
|
+
typeof additionalNumber !== "string") {
|
|
94
|
+
return { valid: false, code: "INVALID_TYPE" };
|
|
95
|
+
}
|
|
96
|
+
const componentResults = [
|
|
97
|
+
validateBuildingNumber(buildingNumber),
|
|
98
|
+
validatePostalCode(postalCode),
|
|
99
|
+
validateAdditionalNumber(additionalNumber),
|
|
100
|
+
];
|
|
101
|
+
for (const result of componentResults) {
|
|
102
|
+
if (!result.valid) {
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
valid: true,
|
|
108
|
+
value: {
|
|
109
|
+
buildingNumber,
|
|
110
|
+
street,
|
|
111
|
+
district,
|
|
112
|
+
city,
|
|
113
|
+
postalCode,
|
|
114
|
+
additionalNumber,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.js","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ValidationResult } from "./types.js";
|
|
2
|
+
export declare function validateSaudiIban(value: unknown): ValidationResult;
|
|
3
|
+
export declare function isSaudiIban(value: unknown): value is string;
|
|
4
|
+
export declare function normalizeIban(value: unknown): string | null;
|
|
5
|
+
export declare function formatIban(value: unknown): string | null;
|
|
6
|
+
//# sourceMappingURL=banking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banking.d.ts","sourceRoot":"","sources":["../src/banking.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAgBnD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CA4BlE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAwB3D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMxD"}
|
package/dist/banking.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { isAsciiDigits, isUppercaseAsciiAlphanumeric } from "./internal/ascii.js";
|
|
2
|
+
import { checkStringInput } from "./internal/input.js";
|
|
3
|
+
import { mod97 } from "./internal/mod97.js";
|
|
4
|
+
const UPPERCASE_ASCII_LETTERS = /^[A-Z]{2}$/;
|
|
5
|
+
function hasValidIbanChecksum(value) {
|
|
6
|
+
const rearranged = `${value.slice(4)}${value.slice(0, 4)}`;
|
|
7
|
+
let expanded = "";
|
|
8
|
+
for (const character of rearranged) {
|
|
9
|
+
const code = character.charCodeAt(0);
|
|
10
|
+
expanded += code >= 65 && code <= 90 ? String(code - 55) : character;
|
|
11
|
+
}
|
|
12
|
+
return mod97(expanded) === 1;
|
|
13
|
+
}
|
|
14
|
+
export function validateSaudiIban(value) {
|
|
15
|
+
const input = checkStringInput(value);
|
|
16
|
+
if (!input.valid) {
|
|
17
|
+
return input;
|
|
18
|
+
}
|
|
19
|
+
if (input.value.length !== 24) {
|
|
20
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
21
|
+
}
|
|
22
|
+
if (!isUppercaseAsciiAlphanumeric(input.value) ||
|
|
23
|
+
!UPPERCASE_ASCII_LETTERS.test(input.value.slice(0, 2)) ||
|
|
24
|
+
!isAsciiDigits(input.value.slice(2, 6))) {
|
|
25
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
26
|
+
}
|
|
27
|
+
if (!input.value.startsWith("SA")) {
|
|
28
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
29
|
+
}
|
|
30
|
+
if (!hasValidIbanChecksum(input.value)) {
|
|
31
|
+
return { valid: false, code: "INVALID_CHECKSUM" };
|
|
32
|
+
}
|
|
33
|
+
return { valid: true, value: input.value };
|
|
34
|
+
}
|
|
35
|
+
export function isSaudiIban(value) {
|
|
36
|
+
return validateSaudiIban(value).valid;
|
|
37
|
+
}
|
|
38
|
+
export function normalizeIban(value) {
|
|
39
|
+
if (typeof value !== "string") {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
let normalized = "";
|
|
43
|
+
for (const character of value) {
|
|
44
|
+
if (character === " ") {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const code = character.charCodeAt(0);
|
|
48
|
+
if (code >= 97 && code <= 122) {
|
|
49
|
+
normalized += String.fromCharCode(code - 32);
|
|
50
|
+
}
|
|
51
|
+
else if ((code >= 65 && code <= 90) || (code >= 48 && code <= 57)) {
|
|
52
|
+
normalized += character;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return isSaudiIban(normalized) ? normalized : null;
|
|
59
|
+
}
|
|
60
|
+
export function formatIban(value) {
|
|
61
|
+
if (!isSaudiIban(value)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return `${value.slice(0, 4)} ${value.slice(4, 8)} ${value.slice(8, 12)} ${value.slice(12, 16)} ${value.slice(16, 20)} ${value.slice(20, 24)}`;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=banking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banking.js","sourceRoot":"","sources":["../src/banking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,MAAM,uBAAuB,GAAG,YAAY,CAAC;AAE7C,SAAS,oBAAoB,CAAC,KAAa;IACzC,MAAM,UAAU,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAC3D,IAAI,QAAQ,GAAG,EAAE,CAAC;IAElB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrC,QAAQ,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAED,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAEtC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,IACE,CAAC,4BAA4B,CAAC,KAAK,CAAC,KAAK,CAAC;QAC1C,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtD,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACvC,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC;IACpD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,UAAU,GAAG,EAAE,CAAC;IAEpB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAErC,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;YAC9B,UAAU,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAC/C,CAAC;aAAM,IAAI,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;YACpE,UAAU,IAAI,SAAS,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AAChJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ValidationResult } from "./types.js";
|
|
2
|
+
export declare function validateVatNumber(value: unknown): ValidationResult;
|
|
3
|
+
export declare function isVatNumber(value: unknown): value is string;
|
|
4
|
+
export declare function validateTin(value: unknown): ValidationResult;
|
|
5
|
+
export declare function isTin(value: unknown): value is string;
|
|
6
|
+
export declare function validateUnifiedNationalNumber(value: unknown): ValidationResult;
|
|
7
|
+
export declare function isUnifiedNationalNumber(value: unknown): value is string;
|
|
8
|
+
export declare function validateCommercialRegistration(value: unknown): ValidationResult;
|
|
9
|
+
export declare function isCommercialRegistration(value: unknown): value is string;
|
|
10
|
+
//# sourceMappingURL=business.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"business.d.ts","sourceRoot":"","sources":["../src/business.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAoBlE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE3D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAgB5D;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAErD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAoB9E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvE;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAgB/E;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExE"}
|
package/dist/business.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isAsciiDigits } from "./internal/ascii.js";
|
|
2
|
+
import { checkStringInput } from "./internal/input.js";
|
|
3
|
+
export function validateVatNumber(value) {
|
|
4
|
+
const input = checkStringInput(value);
|
|
5
|
+
if (!input.valid) {
|
|
6
|
+
return input;
|
|
7
|
+
}
|
|
8
|
+
if (input.value.length !== 15) {
|
|
9
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
10
|
+
}
|
|
11
|
+
if (!isAsciiDigits(input.value)) {
|
|
12
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
13
|
+
}
|
|
14
|
+
if (!input.value.startsWith("3") || !input.value.endsWith("3")) {
|
|
15
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
16
|
+
}
|
|
17
|
+
return { valid: true, value: input.value };
|
|
18
|
+
}
|
|
19
|
+
export function isVatNumber(value) {
|
|
20
|
+
return validateVatNumber(value).valid;
|
|
21
|
+
}
|
|
22
|
+
export function validateTin(value) {
|
|
23
|
+
const input = checkStringInput(value);
|
|
24
|
+
if (!input.valid) {
|
|
25
|
+
return input;
|
|
26
|
+
}
|
|
27
|
+
if (input.value.length !== 10) {
|
|
28
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
29
|
+
}
|
|
30
|
+
if (!isAsciiDigits(input.value)) {
|
|
31
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
32
|
+
}
|
|
33
|
+
return { valid: true, value: input.value };
|
|
34
|
+
}
|
|
35
|
+
export function isTin(value) {
|
|
36
|
+
return validateTin(value).valid;
|
|
37
|
+
}
|
|
38
|
+
export function validateUnifiedNationalNumber(value) {
|
|
39
|
+
const input = checkStringInput(value);
|
|
40
|
+
if (!input.valid) {
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
if (input.value.length !== 10) {
|
|
44
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
45
|
+
}
|
|
46
|
+
if (!isAsciiDigits(input.value)) {
|
|
47
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
48
|
+
}
|
|
49
|
+
if (!input.value.startsWith("7")) {
|
|
50
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
51
|
+
}
|
|
52
|
+
return { valid: true, value: input.value };
|
|
53
|
+
}
|
|
54
|
+
export function isUnifiedNationalNumber(value) {
|
|
55
|
+
return validateUnifiedNationalNumber(value).valid;
|
|
56
|
+
}
|
|
57
|
+
export function validateCommercialRegistration(value) {
|
|
58
|
+
const input = checkStringInput(value);
|
|
59
|
+
if (!input.valid) {
|
|
60
|
+
return input;
|
|
61
|
+
}
|
|
62
|
+
if (input.value.length !== 10) {
|
|
63
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
64
|
+
}
|
|
65
|
+
if (!isAsciiDigits(input.value)) {
|
|
66
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
67
|
+
}
|
|
68
|
+
return { valid: true, value: input.value };
|
|
69
|
+
}
|
|
70
|
+
export function isCommercialRegistration(value) {
|
|
71
|
+
return validateCommercialRegistration(value).valid;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=business.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"business.js","sourceRoot":"","sources":["../src/business.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ValidationResult } from "./types.js";
|
|
2
|
+
export declare function validateNationalId(value: unknown): ValidationResult;
|
|
3
|
+
export declare function isNationalId(value: unknown): boolean;
|
|
4
|
+
export declare function validateIqama(value: unknown): ValidationResult;
|
|
5
|
+
export declare function isIqama(value: unknown): boolean;
|
|
6
|
+
export declare function validateSaudiId(value: unknown): ValidationResult;
|
|
7
|
+
export declare function isSaudiId(value: unknown): boolean;
|
|
8
|
+
export declare function getSaudiIdType(value: unknown): "national-id" | "iqama" | null;
|
|
9
|
+
export declare function validateBorderId(value: unknown): ValidationResult;
|
|
10
|
+
export declare function isBorderId(value: unknown): boolean;
|
|
11
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAwBnE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAwB9D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAQhE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEjD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,OAAO,GAAG,IAAI,CAQ7E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAoBjE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAElD"}
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { isAsciiDigits } from "./internal/ascii.js";
|
|
2
|
+
import { hasValidSaudiIdentityChecksum } from "./internal/identity-checksum.js";
|
|
3
|
+
import { checkStringInput } from "./internal/input.js";
|
|
4
|
+
export function validateNationalId(value) {
|
|
5
|
+
const input = checkStringInput(value);
|
|
6
|
+
if (!input.valid) {
|
|
7
|
+
return input;
|
|
8
|
+
}
|
|
9
|
+
if (input.value.length !== 10) {
|
|
10
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
11
|
+
}
|
|
12
|
+
if (!isAsciiDigits(input.value)) {
|
|
13
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
14
|
+
}
|
|
15
|
+
if (!input.value.startsWith("1")) {
|
|
16
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
17
|
+
}
|
|
18
|
+
if (!hasValidSaudiIdentityChecksum(input.value)) {
|
|
19
|
+
return { valid: false, code: "INVALID_CHECKSUM" };
|
|
20
|
+
}
|
|
21
|
+
return { valid: true, value: input.value };
|
|
22
|
+
}
|
|
23
|
+
export function isNationalId(value) {
|
|
24
|
+
return validateNationalId(value).valid;
|
|
25
|
+
}
|
|
26
|
+
export function validateIqama(value) {
|
|
27
|
+
const input = checkStringInput(value);
|
|
28
|
+
if (!input.valid) {
|
|
29
|
+
return input;
|
|
30
|
+
}
|
|
31
|
+
if (input.value.length !== 10) {
|
|
32
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
33
|
+
}
|
|
34
|
+
if (!isAsciiDigits(input.value)) {
|
|
35
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
36
|
+
}
|
|
37
|
+
if (!input.value.startsWith("2")) {
|
|
38
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
39
|
+
}
|
|
40
|
+
if (!hasValidSaudiIdentityChecksum(input.value)) {
|
|
41
|
+
return { valid: false, code: "INVALID_CHECKSUM" };
|
|
42
|
+
}
|
|
43
|
+
return { valid: true, value: input.value };
|
|
44
|
+
}
|
|
45
|
+
export function isIqama(value) {
|
|
46
|
+
return validateIqama(value).valid;
|
|
47
|
+
}
|
|
48
|
+
export function validateSaudiId(value) {
|
|
49
|
+
const nationalId = validateNationalId(value);
|
|
50
|
+
if (nationalId.valid || nationalId.code !== "INVALID_PREFIX") {
|
|
51
|
+
return nationalId;
|
|
52
|
+
}
|
|
53
|
+
return validateIqama(value);
|
|
54
|
+
}
|
|
55
|
+
export function isSaudiId(value) {
|
|
56
|
+
return validateSaudiId(value).valid;
|
|
57
|
+
}
|
|
58
|
+
export function getSaudiIdType(value) {
|
|
59
|
+
const result = validateSaudiId(value);
|
|
60
|
+
if (!result.valid) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return result.value.startsWith("1") ? "national-id" : "iqama";
|
|
64
|
+
}
|
|
65
|
+
export function validateBorderId(value) {
|
|
66
|
+
const input = checkStringInput(value);
|
|
67
|
+
if (!input.valid) {
|
|
68
|
+
return input;
|
|
69
|
+
}
|
|
70
|
+
if (input.value.length !== 10) {
|
|
71
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
72
|
+
}
|
|
73
|
+
if (!isAsciiDigits(input.value)) {
|
|
74
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
75
|
+
}
|
|
76
|
+
if (!input.value.startsWith("3") && !input.value.startsWith("4")) {
|
|
77
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
78
|
+
}
|
|
79
|
+
return { valid: true, value: input.value };
|
|
80
|
+
}
|
|
81
|
+
export function isBorderId(value) {
|
|
82
|
+
return validateBorderId(value).valid;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { formatIban, isSaudiIban, normalizeIban, validateSaudiIban } from "./banking.js";
|
|
2
|
+
export { isAdditionalNumber, isBuildingNumber, isPostalCode, isShortAddress, normalizeShortAddress, validateAdditionalNumber, validateBuildingNumber, validateNationalAddress, validatePostalCode, validateShortAddress, } from "./address.js";
|
|
3
|
+
export { isCommercialRegistration, isTin, isUnifiedNationalNumber, isVatNumber, validateCommercialRegistration, validateTin, validateUnifiedNationalNumber, validateVatNumber, } from "./business.js";
|
|
4
|
+
export { getSaudiIdType, isBorderId, isIqama, isNationalId, isSaudiId, validateBorderId, validateIqama, validateNationalId, validateSaudiId, } from "./identity.js";
|
|
5
|
+
export { isLandlineNumber, isMobileNumber, isTollFreeNumber, normalizePhoneNumber, validateLandlineNumber, validateMobileNumber, validateTollFreeNumber, } from "./telecom.js";
|
|
6
|
+
export type { NationalAddress, NationalAddressValidationResult, NormalizedSaudiPhone, ValidationErrorCode, ValidationEvidence, ValidationResult, } from "./types.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEzF,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,KAAK,EACL,uBAAuB,EACvB,WAAW,EACX,8BAA8B,EAC9B,WAAW,EACX,6BAA6B,EAC7B,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,UAAU,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,eAAe,EACf,+BAA+B,EAC/B,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { formatIban, isSaudiIban, normalizeIban, validateSaudiIban } from "./banking.js";
|
|
2
|
+
export { isAdditionalNumber, isBuildingNumber, isPostalCode, isShortAddress, normalizeShortAddress, validateAdditionalNumber, validateBuildingNumber, validateNationalAddress, validatePostalCode, validateShortAddress, } from "./address.js";
|
|
3
|
+
export { isCommercialRegistration, isTin, isUnifiedNationalNumber, isVatNumber, validateCommercialRegistration, validateTin, validateUnifiedNationalNumber, validateVatNumber, } from "./business.js";
|
|
4
|
+
export { getSaudiIdType, isBorderId, isIqama, isNationalId, isSaudiId, validateBorderId, validateIqama, validateNationalId, validateSaudiId, } from "./identity.js";
|
|
5
|
+
export { isLandlineNumber, isMobileNumber, isTollFreeNumber, normalizePhoneNumber, validateLandlineNumber, validateMobileNumber, validateTollFreeNumber, } from "./telecom.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEzF,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,wBAAwB,EACxB,KAAK,EACL,uBAAuB,EACvB,WAAW,EACX,8BAA8B,EAC9B,WAAW,EACX,6BAA6B,EAC7B,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,UAAU,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ascii.d.ts","sourceRoot":"","sources":["../../src/internal/ascii.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnE"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const ASCII_DIGITS = /^[0-9]+$/;
|
|
2
|
+
const UPPERCASE_ASCII_ALPHANUMERIC = /^[A-Z0-9]+$/;
|
|
3
|
+
export function isAsciiDigits(value) {
|
|
4
|
+
return ASCII_DIGITS.test(value);
|
|
5
|
+
}
|
|
6
|
+
export function isUppercaseAsciiAlphanumeric(value) {
|
|
7
|
+
return UPPERCASE_ASCII_ALPHANUMERIC.test(value);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=ascii.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ascii.js","sourceRoot":"","sources":["../../src/internal/ascii.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,UAAU,CAAC;AAChC,MAAM,4BAA4B,GAAG,aAAa,CAAC;AAEnD,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAa;IACxD,OAAO,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-checksum.d.ts","sourceRoot":"","sources":["../../src/internal/identity-checksum.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmBpE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isAsciiDigits } from "./ascii.js";
|
|
2
|
+
/** Precondition: value is an already structurally validated 10-digit ASCII string. */
|
|
3
|
+
export function hasValidSaudiIdentityChecksum(value) {
|
|
4
|
+
if (typeof value !== "string" || value.length !== 10 || !isAsciiDigits(value)) {
|
|
5
|
+
throw new TypeError("Saudi identity checksum requires exactly 10 ASCII digits");
|
|
6
|
+
}
|
|
7
|
+
let total = 0;
|
|
8
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
9
|
+
const digit = value.charCodeAt(index) - 48;
|
|
10
|
+
if (index % 2 === 0) {
|
|
11
|
+
const doubled = digit * 2;
|
|
12
|
+
total += doubled > 9 ? doubled - 9 : doubled;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
total += digit;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return total % 10 === 0;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=identity-checksum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-checksum.js","sourceRoot":"","sources":["../../src/internal/identity-checksum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,sFAAsF;AACtF,MAAM,UAAU,6BAA6B,CAAC,KAAa;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,SAAS,CAAC,0DAA0D,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAE3C,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC;YAC1B,KAAK,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,KAAK,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/internal/input.ts"],"names":[],"mappings":"AAAA,KAAK,kBAAkB,GACnB;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,cAAc,CAAC;CACnC,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAUnE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function checkStringInput(input) {
|
|
2
|
+
if (input === undefined || input === null || input === "") {
|
|
3
|
+
return { valid: false, code: "REQUIRED" };
|
|
4
|
+
}
|
|
5
|
+
if (typeof input !== "string") {
|
|
6
|
+
return { valid: false, code: "INVALID_TYPE" };
|
|
7
|
+
}
|
|
8
|
+
return { valid: true, value: input };
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/internal/input.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QAC1D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAChD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod97.d.ts","sourceRoot":"","sources":["../../src/internal/mod97.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAa3C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isAsciiDigits } from "./ascii.js";
|
|
2
|
+
/** Precondition: value is a non-empty string containing only ASCII digits. */
|
|
3
|
+
export function mod97(value) {
|
|
4
|
+
if (typeof value !== "string" || !isAsciiDigits(value)) {
|
|
5
|
+
throw new TypeError("mod97 requires a non-empty ASCII numeric string");
|
|
6
|
+
}
|
|
7
|
+
let remainder = 0;
|
|
8
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
9
|
+
const digit = value.charCodeAt(index) - 48;
|
|
10
|
+
remainder = (remainder * 10 + digit) % 97;
|
|
11
|
+
}
|
|
12
|
+
return remainder;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=mod97.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod97.js","sourceRoot":"","sources":["../../src/internal/mod97.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,8EAA8E;AAC9E,MAAM,UAAU,KAAK,CAAC,KAAa;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC3C,SAAS,GAAG,CAAC,SAAS,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NormalizedSaudiPhone, ValidationResult } from "./types.js";
|
|
2
|
+
export declare function validateMobileNumber(value: unknown): ValidationResult;
|
|
3
|
+
export declare function isMobileNumber(value: unknown): value is string;
|
|
4
|
+
export declare function validateLandlineNumber(value: unknown): ValidationResult;
|
|
5
|
+
export declare function isLandlineNumber(value: unknown): value is string;
|
|
6
|
+
export declare function validateTollFreeNumber(value: unknown): ValidationResult;
|
|
7
|
+
export declare function isTollFreeNumber(value: unknown): value is string;
|
|
8
|
+
export declare function normalizePhoneNumber(value: unknown): NormalizedSaudiPhone | null;
|
|
9
|
+
//# sourceMappingURL=telecom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telecom.d.ts","sourceRoot":"","sources":["../src/telecom.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiBzE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAuBrE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE9D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAuBvE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAoBvE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI,CAgChF"}
|
package/dist/telecom.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { isAsciiDigits } from "./internal/ascii.js";
|
|
2
|
+
import { checkStringInput } from "./internal/input.js";
|
|
3
|
+
const NATIONAL_PHONE_LENGTH = 10;
|
|
4
|
+
const E164_PHONE_LENGTH = 13;
|
|
5
|
+
function validateNationalOrE164Format(value) {
|
|
6
|
+
if (value.length === NATIONAL_PHONE_LENGTH) {
|
|
7
|
+
return isAsciiDigits(value);
|
|
8
|
+
}
|
|
9
|
+
return value.startsWith("+") && isAsciiDigits(value.slice(1));
|
|
10
|
+
}
|
|
11
|
+
function hasE164SaudiPrefix(value) {
|
|
12
|
+
return value.startsWith("+966");
|
|
13
|
+
}
|
|
14
|
+
export function validateMobileNumber(value) {
|
|
15
|
+
const input = checkStringInput(value);
|
|
16
|
+
if (!input.valid) {
|
|
17
|
+
return input;
|
|
18
|
+
}
|
|
19
|
+
if (input.value.length !== NATIONAL_PHONE_LENGTH && input.value.length !== E164_PHONE_LENGTH) {
|
|
20
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
21
|
+
}
|
|
22
|
+
if (!validateNationalOrE164Format(input.value)) {
|
|
23
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
24
|
+
}
|
|
25
|
+
if ((input.value.length === NATIONAL_PHONE_LENGTH && !input.value.startsWith("05")) ||
|
|
26
|
+
(input.value.length === E164_PHONE_LENGTH && !input.value.startsWith("+9665"))) {
|
|
27
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
28
|
+
}
|
|
29
|
+
return { valid: true, value: input.value };
|
|
30
|
+
}
|
|
31
|
+
export function isMobileNumber(value) {
|
|
32
|
+
return validateMobileNumber(value).valid;
|
|
33
|
+
}
|
|
34
|
+
export function validateLandlineNumber(value) {
|
|
35
|
+
const input = checkStringInput(value);
|
|
36
|
+
if (!input.valid) {
|
|
37
|
+
return input;
|
|
38
|
+
}
|
|
39
|
+
if (input.value.length !== NATIONAL_PHONE_LENGTH && input.value.length !== E164_PHONE_LENGTH) {
|
|
40
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
41
|
+
}
|
|
42
|
+
if (!validateNationalOrE164Format(input.value)) {
|
|
43
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
44
|
+
}
|
|
45
|
+
const nationalNumber = input.value.length === NATIONAL_PHONE_LENGTH ? input.value : `0${input.value.slice(4)}`;
|
|
46
|
+
if (!/^01[123467]/.test(nationalNumber)) {
|
|
47
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
48
|
+
}
|
|
49
|
+
return { valid: true, value: input.value };
|
|
50
|
+
}
|
|
51
|
+
export function isLandlineNumber(value) {
|
|
52
|
+
return validateLandlineNumber(value).valid;
|
|
53
|
+
}
|
|
54
|
+
export function validateTollFreeNumber(value) {
|
|
55
|
+
const input = checkStringInput(value);
|
|
56
|
+
if (!input.valid) {
|
|
57
|
+
return input;
|
|
58
|
+
}
|
|
59
|
+
if (input.value.length !== NATIONAL_PHONE_LENGTH) {
|
|
60
|
+
return { valid: false, code: "INVALID_LENGTH" };
|
|
61
|
+
}
|
|
62
|
+
if (!isAsciiDigits(input.value)) {
|
|
63
|
+
return { valid: false, code: "INVALID_FORMAT" };
|
|
64
|
+
}
|
|
65
|
+
if (!input.value.startsWith("800")) {
|
|
66
|
+
return { valid: false, code: "INVALID_PREFIX" };
|
|
67
|
+
}
|
|
68
|
+
return { valid: true, value: input.value };
|
|
69
|
+
}
|
|
70
|
+
export function isTollFreeNumber(value) {
|
|
71
|
+
return validateTollFreeNumber(value).valid;
|
|
72
|
+
}
|
|
73
|
+
export function normalizePhoneNumber(value) {
|
|
74
|
+
if (typeof value !== "string") {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if (validateTollFreeNumber(value).valid) {
|
|
78
|
+
return { kind: "toll-free", value: `800${value.slice(3)}` };
|
|
79
|
+
}
|
|
80
|
+
let canonical;
|
|
81
|
+
if (hasE164SaudiPrefix(value)) {
|
|
82
|
+
canonical = value;
|
|
83
|
+
}
|
|
84
|
+
else if (value.startsWith("00966")) {
|
|
85
|
+
canonical = `+966${value.slice(5)}`;
|
|
86
|
+
}
|
|
87
|
+
else if (value.startsWith("966")) {
|
|
88
|
+
canonical = `+966${value.slice(3)}`;
|
|
89
|
+
}
|
|
90
|
+
else if (value.startsWith("0")) {
|
|
91
|
+
canonical = `+966${value.slice(1)}`;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (validateMobileNumber(canonical).valid) {
|
|
97
|
+
return { kind: "mobile", value: canonical };
|
|
98
|
+
}
|
|
99
|
+
if (validateLandlineNumber(canonical).valid) {
|
|
100
|
+
return { kind: "landline", value: canonical };
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=telecom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telecom.js","sourceRoot":"","sources":["../src/telecom.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type ValidationErrorCode = "REQUIRED" | "INVALID_TYPE" | "INVALID_LENGTH" | "INVALID_FORMAT" | "INVALID_PREFIX" | "INVALID_CHECKSUM";
|
|
2
|
+
export type ValidationResult = {
|
|
3
|
+
valid: true;
|
|
4
|
+
value: string;
|
|
5
|
+
} | {
|
|
6
|
+
valid: false;
|
|
7
|
+
code: ValidationErrorCode;
|
|
8
|
+
};
|
|
9
|
+
export type ValidationEvidence = "official-checksum" | "official-structural" | "official-structural-community-checksum" | "best-known-structural";
|
|
10
|
+
export type NormalizedSaudiPhone = {
|
|
11
|
+
kind: "mobile" | "landline";
|
|
12
|
+
value: `+966${string}`;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "toll-free";
|
|
15
|
+
value: `800${string}`;
|
|
16
|
+
};
|
|
17
|
+
export type NationalAddress = {
|
|
18
|
+
buildingNumber: string;
|
|
19
|
+
street: string;
|
|
20
|
+
district: string;
|
|
21
|
+
city: string;
|
|
22
|
+
postalCode: string;
|
|
23
|
+
additionalNumber: string;
|
|
24
|
+
};
|
|
25
|
+
export type NationalAddressValidationResult = {
|
|
26
|
+
valid: true;
|
|
27
|
+
value: NationalAddress;
|
|
28
|
+
} | {
|
|
29
|
+
valid: false;
|
|
30
|
+
code: ValidationErrorCode;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAC3B,UAAU,GACV,cAAc,GACd,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,kBAAkB,CAAC;AAEvB,MAAM,MAAM,gBAAgB,GACxB;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,mBAAmB,CAAC;CAC3B,CAAC;AAEN,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,qBAAqB,GACrB,wCAAwC,GACxC,uBAAuB,CAAC;AAE5B,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,KAAK,EAAE,OAAO,MAAM,EAAE,CAAC;CACxB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,MAAM,EAAE,CAAC;CACvB,CAAC;AAEN,MAAM,MAAM,eAAe,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,+BAA+B,GACvC;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,eAAe,CAAC;CACxB,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,mBAAmB,CAAC;CAC3B,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "saudi-utils",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Strict Saudi identity, IBAN, business, telecom, and address validation utilities",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"saudi",
|
|
7
|
+
"saudi-arabia",
|
|
8
|
+
"ksa",
|
|
9
|
+
"validation",
|
|
10
|
+
"national-id",
|
|
11
|
+
"iqama",
|
|
12
|
+
"iban",
|
|
13
|
+
"zatca",
|
|
14
|
+
"national-address",
|
|
15
|
+
"typescript"
|
|
16
|
+
],
|
|
17
|
+
"author": "Ali Alaraby",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/alialaraby/saudi-utils.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/alialaraby/saudi-utils/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/alialaraby/saudi-utils#readme",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"clean": "node --eval \"import { rmSync } from 'node:fs'; for (const path of ['dist', 'coverage']) rmSync(path, { recursive: true, force: true });\"",
|
|
51
|
+
"build": "npm run clean && tsc --project tsconfig.json",
|
|
52
|
+
"typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.test.json --noEmit",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:coverage": "vitest run --coverage",
|
|
55
|
+
"lint": "eslint .",
|
|
56
|
+
"format": "prettier --write .",
|
|
57
|
+
"format:check": "prettier --check .",
|
|
58
|
+
"docs:check": "npm run build && tsc --project tsconfig.examples.json --noEmit && node examples/quick-start.ts && node scripts/check-doc-links.mjs",
|
|
59
|
+
"publint": "publint --strict",
|
|
60
|
+
"attw": "attw --pack . --profile esm-only",
|
|
61
|
+
"pack:dry-run": "npm pack --dry-run --json",
|
|
62
|
+
"pack:check": "node scripts/check-package-contents.mjs",
|
|
63
|
+
"pack:consumers": "node scripts/check-packed-consumers.mjs",
|
|
64
|
+
"package:check": "npm run build && npm run publint && npm run attw && npm run pack:check && npm run pack:consumers",
|
|
65
|
+
"check": "npm run format:check && npm run lint && npm run typecheck && npm run test:coverage && npm run build"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
69
|
+
"@vitest/coverage-v8": "^5.0.1",
|
|
70
|
+
"eslint": "^10.10.0",
|
|
71
|
+
"prettier": "^3.9.6",
|
|
72
|
+
"publint": "^0.3.24",
|
|
73
|
+
"typescript": "^6.0.3",
|
|
74
|
+
"typescript-eslint": "^8.70.0",
|
|
75
|
+
"vitest": "^5.0.1"
|
|
76
|
+
}
|
|
77
|
+
}
|