fi-pin 0.0.9 → 0.0.10

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 CHANGED
@@ -1,8 +1,10 @@
1
1
  # Finnish Person ID Check
2
2
 
3
+ [![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://github.com/ellerbrock/typescript-badges/)
4
+ [![npm version](https://badge.fury.io/js/fi-pin.svg)](https://badge.fury.io/js/fi-pin)
5
+ [![Maintainability](https://qlty.sh/gh/mharj/projects/hetu/maintainability.svg)](https://qlty.sh/gh/mharj/projects/hetu)
6
+ [![Code Coverage](https://qlty.sh/gh/mharj/projects/hetu/coverage.svg)](https://qlty.sh/gh/mharj/projects/hetu)
3
7
  ![github action](https://github.com/mharj/hetu/actions/workflows/main.yml/badge.svg)
4
- [![Maintainability](https://api.codeclimate.com/v1/badges/3dca350166c6d1ea4105/maintainability)](https://codeclimate.com/github/mharj/hetu/maintainability)
5
- [![Test Coverage](https://api.codeclimate.com/v1/badges/3dca350166c6d1ea4105/test_coverage)](https://codeclimate.com/github/mharj/hetu/test_coverage)
6
8
 
7
9
  ## Overview
8
10
 
package/dist/index.cjs ADDED
@@ -0,0 +1,111 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/index.ts
3
+ const checksumCharacterSet = "0123456789ABCDEFHJKLMNPRSTUVWXY";
4
+ const checkSumMapLength = 31;
5
+ /**
6
+ * Parse string to integer
7
+ * @throws TypeError if value is not a number
8
+ * @param {string | undefined} value - string to parse
9
+ * @returns {number} value as number
10
+ * @since v0.0.7
11
+ */
12
+ function parseStringToInt(value) {
13
+ if (!value) throw new TypeError("Value is undefined");
14
+ const parsedValue = parseInt(value, 10);
15
+ if (isNaN(parsedValue)) throw new TypeError(`Value is not a number: ${value}`);
16
+ return parsedValue;
17
+ }
18
+ /**
19
+ * Validate that value is a string
20
+ * @throws TypeError if value is not a string
21
+ * @param {unknown} value - value to validate
22
+ * @returns {string} value as string
23
+ * @since v0.0.7
24
+ */
25
+ function validateString(value) {
26
+ if (typeof value !== "string") throw new TypeError(`${String(value)} not a string`);
27
+ return value;
28
+ }
29
+ /**
30
+ * Returns the checksum character from the checksum index map.
31
+ * @param {number} index The index of the checksum character.
32
+ * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.
33
+ * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.
34
+ * @since v0.0.7
35
+ */
36
+ function getCheckSum(index) {
37
+ const checkSumChar = checksumCharacterSet[index];
38
+ if (!checkSumChar) throw new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);
39
+ return checkSumChar;
40
+ }
41
+ /**
42
+ * Validates whether a string is a valid Finnish person ID.
43
+ *
44
+ * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:
45
+ * - DDMMYY represents the date of birth.
46
+ * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).
47
+ * - ZZZ is an individual number (odd numbers are males, even numbers are females).
48
+ * - Q is a checksum character.
49
+ *
50
+ * The checksum character is calculated based on the first 10 characters.
51
+ * @param {string} personId The person ID string to validate.
52
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
53
+ * @returns {boolean} True if the person ID is valid, false otherwise.
54
+ * @example
55
+ * isValidPersonId('131052-308T') // true
56
+ * isValidPersonId('131052-3082') // false
57
+ * const personId = '131052-308T';
58
+ * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>
59
+ * // personId type is now: string & z.BRAND<'PersonID'>
60
+ * }
61
+ * @since v0.0.1
62
+ */
63
+ function isValidPersonId(personId) {
64
+ if (typeof personId !== "string" || personId.length !== 11) return false;
65
+ const upperPersonId = personId.toUpperCase();
66
+ try {
67
+ return upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);
68
+ } catch (_error) {
69
+ return false;
70
+ }
71
+ }
72
+ /**
73
+ * Checks whether a person ID belongs to a Finnish male.
74
+ * @param {string} personId The Finnish person ID string.
75
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
76
+ * @returns {boolean} True if the person ID belongs to a male, false otherwise.
77
+ * @throws {TypeError} If the person ID is not valid.
78
+ * @example
79
+ * isMale('131052-309U') // true
80
+ * isMale('131052-308T') // false
81
+ * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>
82
+ * @since v0.0.1
83
+ */
84
+ function isMale(personId) {
85
+ if (!isValidPersonId(personId)) throw new TypeError(`${String(personId)} is not valid person id`);
86
+ return parseStringToInt(personId.slice(7, 10)) % 2 === 1;
87
+ }
88
+ /**
89
+ * Checks whether a person ID belongs to a Finnish female.
90
+ * @param {string} personId The Finnish person ID string.
91
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
92
+ * @returns {boolean} True if the person ID belongs to a female, false otherwise.
93
+ * @throws {TypeError} If the person ID is not valid.
94
+ * @example
95
+ * isFemale('131052-308T') // true
96
+ * isFemale('131052-309U') // false
97
+ * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>
98
+ * @since v0.0.1
99
+ */
100
+ function isFemale(personId) {
101
+ return !isMale(personId);
102
+ }
103
+ //#endregion
104
+ exports.getCheckSum = getCheckSum;
105
+ exports.isFemale = isFemale;
106
+ exports.isMale = isMale;
107
+ exports.isValidPersonId = isValidPersonId;
108
+ exports.parseStringToInt = parseStringToInt;
109
+ exports.validateString = validateString;
110
+
111
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["const checksumCharacterSet = '0123456789ABCDEFHJKLMNPRSTUVWXY';\nconst checkSumMapLength = checksumCharacterSet.length; // is always 31\n\n/**\n * Parse string to integer\n * @throws TypeError if value is not a number\n * @param {string | undefined} value - string to parse\n * @returns {number} value as number\n * @since v0.0.7\n */\nexport function parseStringToInt(value: string | undefined): number {\n\tif (!value) {\n\t\tthrow new TypeError('Value is undefined');\n\t}\n\tconst parsedValue = parseInt(value, 10);\n\tif (isNaN(parsedValue)) {\n\t\tthrow new TypeError(`Value is not a number: ${value}`);\n\t}\n\treturn parsedValue;\n}\n\n/**\n * Validate that value is a string\n * @throws TypeError if value is not a string\n * @param {unknown} value - value to validate\n * @returns {string} value as string\n * @since v0.0.7\n */\nexport function validateString(value: unknown): string {\n\tif (typeof value !== 'string') {\n\t\tthrow new TypeError(`${String(value)} not a string`);\n\t}\n\treturn value;\n}\n\n/**\n * Returns the checksum character from the checksum index map.\n * @param {number} index The index of the checksum character.\n * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.\n * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.\n * @since v0.0.7\n */\nexport function getCheckSum(index: number): string {\n\tconst checkSumChar = checksumCharacterSet[index];\n\tif (!checkSumChar) {\n\t\tthrow new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);\n\t}\n\treturn checkSumChar;\n}\n\n/**\n * Validates whether a string is a valid Finnish person ID.\n *\n * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:\n * - DDMMYY represents the date of birth.\n * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).\n * - ZZZ is an individual number (odd numbers are males, even numbers are females).\n * - Q is a checksum character.\n *\n * The checksum character is calculated based on the first 10 characters.\n * @param {string} personId The person ID string to validate.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID is valid, false otherwise.\n * @example\n * isValidPersonId('131052-308T') // true\n * isValidPersonId('131052-3082') // false\n * const personId = '131052-308T';\n * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>\n * // personId type is now: string & z.BRAND<'PersonID'>\n * }\n * @since v0.0.1\n */\nexport function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (typeof personId !== 'string' || personId.length !== 11) {\n\t\treturn false;\n\t}\n\tconst upperPersonId = personId.toUpperCase();\n\ttry {\n\t\treturn upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);\n\t} catch (_error) {\n\t\treturn false;\n\t}\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish male.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a male, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isMale('131052-309U') // true\n * isMale('131052-308T') // false\n * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>\n * @since v0.0.1\n */\nexport function isMale<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (!isValidPersonId(personId)) {\n\t\tthrow new TypeError(`${String(personId)} is not valid person id`);\n\t}\n\treturn parseStringToInt(personId.slice(7, 10)) % 2 === 1; // check if the individual number is odd\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish female.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a female, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isFemale('131052-308T') // true\n * isFemale('131052-309U') // false\n * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>\n * @since v0.0.1\n */\nexport function isFemale<BRAND = string>(personId: string): personId is string & BRAND {\n\treturn !isMale(personId);\n}\n"],"mappings":";;AAAA,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB;;;;;;;;AAS1B,SAAgB,iBAAiB,OAAmC;CACnE,IAAI,CAAC,OACJ,MAAM,IAAI,UAAU,oBAAoB;CAEzC,MAAM,cAAc,SAAS,OAAO,EAAE;CACtC,IAAI,MAAM,WAAW,GACpB,MAAM,IAAI,UAAU,0BAA0B,OAAO;CAEtD,OAAO;AACR;;;;;;;;AASA,SAAgB,eAAe,OAAwB;CACtD,IAAI,OAAO,UAAU,UACpB,MAAM,IAAI,UAAU,GAAG,OAAO,KAAK,EAAE,cAAc;CAEpD,OAAO;AACR;;;;;;;;AASA,SAAgB,YAAY,OAAuB;CAClD,MAAM,eAAe,qBAAqB;CAC1C,IAAI,CAAC,cACJ,MAAM,IAAI,WAAW,wBAAwB,MAAM,SAAS,EAAE,mBAAmB,OAAO,oBAAoB,CAAC,EAAE,EAAE;CAElH,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,gBAAgC,UAA8C;CAC7F,IAAI,OAAO,aAAa,YAAY,SAAS,WAAW,IACvD,OAAO;CAER,MAAM,gBAAgB,SAAS,YAAY;CAC3C,IAAI;EACH,OAAO,cAAc,QAAQ,YAAY,iBAAiB,cAAc,MAAM,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,CAAC,IAAI,iBAAiB;CACtI,SAAS,QAAQ;EAChB,OAAO;CACR;AACD;;;;;;;;;;;;;AAcA,SAAgB,OAAuB,UAA8C;CACpF,IAAI,CAAC,gBAAgB,QAAQ,GAC5B,MAAM,IAAI,UAAU,GAAG,OAAO,QAAQ,EAAE,wBAAwB;CAEjE,OAAO,iBAAiB,SAAS,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM;AACxD;;;;;;;;;;;;;AAcA,SAAgB,SAAyB,UAA8C;CACtF,OAAO,CAAC,OAAO,QAAQ;AACxB"}
@@ -0,0 +1,77 @@
1
+ //#region src/index.d.ts
2
+ /**
3
+ * Parse string to integer
4
+ * @throws TypeError if value is not a number
5
+ * @param {string | undefined} value - string to parse
6
+ * @returns {number} value as number
7
+ * @since v0.0.7
8
+ */
9
+ declare function parseStringToInt(value: string | undefined): number;
10
+ /**
11
+ * Validate that value is a string
12
+ * @throws TypeError if value is not a string
13
+ * @param {unknown} value - value to validate
14
+ * @returns {string} value as string
15
+ * @since v0.0.7
16
+ */
17
+ declare function validateString(value: unknown): string;
18
+ /**
19
+ * Returns the checksum character from the checksum index map.
20
+ * @param {number} index The index of the checksum character.
21
+ * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.
22
+ * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.
23
+ * @since v0.0.7
24
+ */
25
+ declare function getCheckSum(index: number): string;
26
+ /**
27
+ * Validates whether a string is a valid Finnish person ID.
28
+ *
29
+ * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:
30
+ * - DDMMYY represents the date of birth.
31
+ * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).
32
+ * - ZZZ is an individual number (odd numbers are males, even numbers are females).
33
+ * - Q is a checksum character.
34
+ *
35
+ * The checksum character is calculated based on the first 10 characters.
36
+ * @param {string} personId The person ID string to validate.
37
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
38
+ * @returns {boolean} True if the person ID is valid, false otherwise.
39
+ * @example
40
+ * isValidPersonId('131052-308T') // true
41
+ * isValidPersonId('131052-3082') // false
42
+ * const personId = '131052-308T';
43
+ * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>
44
+ * // personId type is now: string & z.BRAND<'PersonID'>
45
+ * }
46
+ * @since v0.0.1
47
+ */
48
+ declare function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND;
49
+ /**
50
+ * Checks whether a person ID belongs to a Finnish male.
51
+ * @param {string} personId The Finnish person ID string.
52
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
53
+ * @returns {boolean} True if the person ID belongs to a male, false otherwise.
54
+ * @throws {TypeError} If the person ID is not valid.
55
+ * @example
56
+ * isMale('131052-309U') // true
57
+ * isMale('131052-308T') // false
58
+ * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>
59
+ * @since v0.0.1
60
+ */
61
+ declare function isMale<BRAND = string>(personId: string): personId is string & BRAND;
62
+ /**
63
+ * Checks whether a person ID belongs to a Finnish female.
64
+ * @param {string} personId The Finnish person ID string.
65
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
66
+ * @returns {boolean} True if the person ID belongs to a female, false otherwise.
67
+ * @throws {TypeError} If the person ID is not valid.
68
+ * @example
69
+ * isFemale('131052-308T') // true
70
+ * isFemale('131052-309U') // false
71
+ * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>
72
+ * @since v0.0.1
73
+ */
74
+ declare function isFemale<BRAND = string>(personId: string): personId is string & BRAND;
75
+ //#endregion
76
+ export { getCheckSum, isFemale, isMale, isValidPersonId, parseStringToInt, validateString };
77
+ //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -1,75 +1,77 @@
1
+ //#region src/index.d.ts
1
2
  /**
2
- * Parse string to integer
3
- * @throws TypeError if value is not a number
4
- * @param {string | undefined} value - string to parse
5
- * @returns {number} value as number
6
- * @since v0.0.7
7
- */
3
+ * Parse string to integer
4
+ * @throws TypeError if value is not a number
5
+ * @param {string | undefined} value - string to parse
6
+ * @returns {number} value as number
7
+ * @since v0.0.7
8
+ */
8
9
  declare function parseStringToInt(value: string | undefined): number;
9
10
  /**
10
- * Validate that value is a string
11
- * @throws TypeError if value is not a string
12
- * @param {unknown} value - value to validate
13
- * @returns {string} value as string
14
- * @since v0.0.7
15
- */
11
+ * Validate that value is a string
12
+ * @throws TypeError if value is not a string
13
+ * @param {unknown} value - value to validate
14
+ * @returns {string} value as string
15
+ * @since v0.0.7
16
+ */
16
17
  declare function validateString(value: unknown): string;
17
18
  /**
18
- * Returns the checksum character from the checksum index map.
19
- * @param {number} index The index of the checksum character.
20
- * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.
21
- * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.
22
- * @since v0.0.7
23
- */
19
+ * Returns the checksum character from the checksum index map.
20
+ * @param {number} index The index of the checksum character.
21
+ * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.
22
+ * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.
23
+ * @since v0.0.7
24
+ */
24
25
  declare function getCheckSum(index: number): string;
25
26
  /**
26
- * Validates whether a string is a valid Finnish person ID.
27
- *
28
- * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:
29
- * - DDMMYY represents the date of birth.
30
- * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).
31
- * - ZZZ is an individual number (odd numbers are males, even numbers are females).
32
- * - Q is a checksum character.
33
- *
34
- * The checksum character is calculated based on the first 10 characters.
35
- * @param {string} personId The person ID string to validate.
36
- * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
37
- * @returns {boolean} True if the person ID is valid, false otherwise.
38
- * @example
39
- * isValidPersonId('131052-308T') // true
40
- * isValidPersonId('131052-3082') // false
41
- * const personId = '131052-308T';
42
- * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>
43
- * // personId type is now: string & z.BRAND<'PersonID'>
44
- * }
45
- * @since v0.0.1
46
- */
27
+ * Validates whether a string is a valid Finnish person ID.
28
+ *
29
+ * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:
30
+ * - DDMMYY represents the date of birth.
31
+ * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).
32
+ * - ZZZ is an individual number (odd numbers are males, even numbers are females).
33
+ * - Q is a checksum character.
34
+ *
35
+ * The checksum character is calculated based on the first 10 characters.
36
+ * @param {string} personId The person ID string to validate.
37
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
38
+ * @returns {boolean} True if the person ID is valid, false otherwise.
39
+ * @example
40
+ * isValidPersonId('131052-308T') // true
41
+ * isValidPersonId('131052-3082') // false
42
+ * const personId = '131052-308T';
43
+ * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>
44
+ * // personId type is now: string & z.BRAND<'PersonID'>
45
+ * }
46
+ * @since v0.0.1
47
+ */
47
48
  declare function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND;
48
49
  /**
49
- * Checks whether a person ID belongs to a Finnish male.
50
- * @param {string} personId The Finnish person ID string.
51
- * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
52
- * @returns {boolean} True if the person ID belongs to a male, false otherwise.
53
- * @throws {TypeError} If the person ID is not valid.
54
- * @example
55
- * isMale('131052-309U') // true
56
- * isMale('131052-308T') // false
57
- * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>
58
- * @since v0.0.1
59
- */
50
+ * Checks whether a person ID belongs to a Finnish male.
51
+ * @param {string} personId The Finnish person ID string.
52
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
53
+ * @returns {boolean} True if the person ID belongs to a male, false otherwise.
54
+ * @throws {TypeError} If the person ID is not valid.
55
+ * @example
56
+ * isMale('131052-309U') // true
57
+ * isMale('131052-308T') // false
58
+ * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>
59
+ * @since v0.0.1
60
+ */
60
61
  declare function isMale<BRAND = string>(personId: string): personId is string & BRAND;
61
62
  /**
62
- * Checks whether a person ID belongs to a Finnish female.
63
- * @param {string} personId The Finnish person ID string.
64
- * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
65
- * @returns {boolean} True if the person ID belongs to a female, false otherwise.
66
- * @throws {TypeError} If the person ID is not valid.
67
- * @example
68
- * isFemale('131052-308T') // true
69
- * isFemale('131052-309U') // false
70
- * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>
71
- * @since v0.0.1
72
- */
63
+ * Checks whether a person ID belongs to a Finnish female.
64
+ * @param {string} personId The Finnish person ID string.
65
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
66
+ * @returns {boolean} True if the person ID belongs to a female, false otherwise.
67
+ * @throws {TypeError} If the person ID is not valid.
68
+ * @example
69
+ * isFemale('131052-308T') // true
70
+ * isFemale('131052-309U') // false
71
+ * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>
72
+ * @since v0.0.1
73
+ */
73
74
  declare function isFemale<BRAND = string>(personId: string): personId is string & BRAND;
74
-
75
+ //#endregion
75
76
  export { getCheckSum, isFemale, isMale, isValidPersonId, parseStringToInt, validateString };
77
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,55 +1,105 @@
1
- // src/index.ts
2
- var checksumCharacterSet = "0123456789ABCDEFHJKLMNPRSTUVWXY";
3
- var checkSumMapLength = checksumCharacterSet.length;
1
+ //#region src/index.ts
2
+ const checksumCharacterSet = "0123456789ABCDEFHJKLMNPRSTUVWXY";
3
+ const checkSumMapLength = 31;
4
+ /**
5
+ * Parse string to integer
6
+ * @throws TypeError if value is not a number
7
+ * @param {string | undefined} value - string to parse
8
+ * @returns {number} value as number
9
+ * @since v0.0.7
10
+ */
4
11
  function parseStringToInt(value) {
5
- if (!value) {
6
- throw new TypeError("Value is undefined");
7
- }
8
- const parsedValue = parseInt(value, 10);
9
- if (isNaN(parsedValue)) {
10
- throw new TypeError(`Value is not a number: ${value}`);
11
- }
12
- return parsedValue;
12
+ if (!value) throw new TypeError("Value is undefined");
13
+ const parsedValue = parseInt(value, 10);
14
+ if (isNaN(parsedValue)) throw new TypeError(`Value is not a number: ${value}`);
15
+ return parsedValue;
13
16
  }
17
+ /**
18
+ * Validate that value is a string
19
+ * @throws TypeError if value is not a string
20
+ * @param {unknown} value - value to validate
21
+ * @returns {string} value as string
22
+ * @since v0.0.7
23
+ */
14
24
  function validateString(value) {
15
- if (typeof value !== "string") {
16
- throw new TypeError(`${String(value)} not a string`);
17
- }
18
- return value;
25
+ if (typeof value !== "string") throw new TypeError(`${String(value)} not a string`);
26
+ return value;
19
27
  }
28
+ /**
29
+ * Returns the checksum character from the checksum index map.
30
+ * @param {number} index The index of the checksum character.
31
+ * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.
32
+ * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.
33
+ * @since v0.0.7
34
+ */
20
35
  function getCheckSum(index) {
21
- const checkSumChar = checksumCharacterSet[index];
22
- if (!checkSumChar) {
23
- throw new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);
24
- }
25
- return checkSumChar;
36
+ const checkSumChar = checksumCharacterSet[index];
37
+ if (!checkSumChar) throw new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);
38
+ return checkSumChar;
26
39
  }
40
+ /**
41
+ * Validates whether a string is a valid Finnish person ID.
42
+ *
43
+ * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:
44
+ * - DDMMYY represents the date of birth.
45
+ * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).
46
+ * - ZZZ is an individual number (odd numbers are males, even numbers are females).
47
+ * - Q is a checksum character.
48
+ *
49
+ * The checksum character is calculated based on the first 10 characters.
50
+ * @param {string} personId The person ID string to validate.
51
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
52
+ * @returns {boolean} True if the person ID is valid, false otherwise.
53
+ * @example
54
+ * isValidPersonId('131052-308T') // true
55
+ * isValidPersonId('131052-3082') // false
56
+ * const personId = '131052-308T';
57
+ * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>
58
+ * // personId type is now: string & z.BRAND<'PersonID'>
59
+ * }
60
+ * @since v0.0.1
61
+ */
27
62
  function isValidPersonId(personId) {
28
- if (typeof personId !== "string" || personId.length !== 11) {
29
- return false;
30
- }
31
- const upperPersonId = personId.toUpperCase();
32
- try {
33
- return upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);
34
- } catch (_error) {
35
- return false;
36
- }
63
+ if (typeof personId !== "string" || personId.length !== 11) return false;
64
+ const upperPersonId = personId.toUpperCase();
65
+ try {
66
+ return upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);
67
+ } catch (_error) {
68
+ return false;
69
+ }
37
70
  }
71
+ /**
72
+ * Checks whether a person ID belongs to a Finnish male.
73
+ * @param {string} personId The Finnish person ID string.
74
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
75
+ * @returns {boolean} True if the person ID belongs to a male, false otherwise.
76
+ * @throws {TypeError} If the person ID is not valid.
77
+ * @example
78
+ * isMale('131052-309U') // true
79
+ * isMale('131052-308T') // false
80
+ * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>
81
+ * @since v0.0.1
82
+ */
38
83
  function isMale(personId) {
39
- if (!isValidPersonId(personId)) {
40
- throw new TypeError(`${String(personId)} is not valid person id`);
41
- }
42
- return parseStringToInt(personId.slice(7, 10)) % 2 === 1;
84
+ if (!isValidPersonId(personId)) throw new TypeError(`${String(personId)} is not valid person id`);
85
+ return parseStringToInt(personId.slice(7, 10)) % 2 === 1;
43
86
  }
87
+ /**
88
+ * Checks whether a person ID belongs to a Finnish female.
89
+ * @param {string} personId The Finnish person ID string.
90
+ * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
91
+ * @returns {boolean} True if the person ID belongs to a female, false otherwise.
92
+ * @throws {TypeError} If the person ID is not valid.
93
+ * @example
94
+ * isFemale('131052-308T') // true
95
+ * isFemale('131052-309U') // false
96
+ * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>
97
+ * @since v0.0.1
98
+ */
44
99
  function isFemale(personId) {
45
- return !isMale(personId);
100
+ return !isMale(personId);
46
101
  }
47
- export {
48
- getCheckSum,
49
- isFemale,
50
- isMale,
51
- isValidPersonId,
52
- parseStringToInt,
53
- validateString
54
- };
102
+ //#endregion
103
+ export { getCheckSum, isFemale, isMale, isValidPersonId, parseStringToInt, validateString };
104
+
55
105
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const checksumCharacterSet = '0123456789ABCDEFHJKLMNPRSTUVWXY';\nconst checkSumMapLength = checksumCharacterSet.length; // is always 31\n\n/**\n * Parse string to integer\n * @throws TypeError if value is not a number\n * @param {string | undefined} value - string to parse\n * @returns {number} value as number\n * @since v0.0.7\n */\nexport function parseStringToInt(value: string | undefined): number {\n\tif (!value) {\n\t\tthrow new TypeError('Value is undefined');\n\t}\n\tconst parsedValue = parseInt(value, 10);\n\tif (isNaN(parsedValue)) {\n\t\tthrow new TypeError(`Value is not a number: ${value}`);\n\t}\n\treturn parsedValue;\n}\n\n/**\n * Validate that value is a string\n * @throws TypeError if value is not a string\n * @param {unknown} value - value to validate\n * @returns {string} value as string\n * @since v0.0.7\n */\nexport function validateString(value: unknown): string {\n\tif (typeof value !== 'string') {\n\t\tthrow new TypeError(`${String(value)} not a string`);\n\t}\n\treturn value;\n}\n\n/**\n * Returns the checksum character from the checksum index map.\n * @param {number} index The index of the checksum character.\n * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.\n * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.\n * @since v0.0.7\n */\nexport function getCheckSum(index: number): string {\n\tconst checkSumChar = checksumCharacterSet[index];\n\tif (!checkSumChar) {\n\t\tthrow new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);\n\t}\n\treturn checkSumChar;\n}\n\n/**\n * Validates whether a string is a valid Finnish person ID.\n *\n * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:\n * - DDMMYY represents the date of birth.\n * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).\n * - ZZZ is an individual number (odd numbers are males, even numbers are females).\n * - Q is a checksum character.\n *\n * The checksum character is calculated based on the first 10 characters.\n * @param {string} personId The person ID string to validate.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID is valid, false otherwise.\n * @example\n * isValidPersonId('131052-308T') // true\n * isValidPersonId('131052-3082') // false\n * const personId = '131052-308T';\n * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>\n * // personId type is now: string & z.BRAND<'PersonID'>\n * }\n * @since v0.0.1\n */\nexport function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (typeof personId !== 'string' || personId.length !== 11) {\n\t\treturn false;\n\t}\n\tconst upperPersonId = personId.toUpperCase();\n\ttry {\n\t\treturn upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);\n\t} catch (_error) {\n\t\treturn false;\n\t}\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish male.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a male, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isMale('131052-309U') // true\n * isMale('131052-308T') // false\n * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>\n * @since v0.0.1\n */\nexport function isMale<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (!isValidPersonId(personId)) {\n\t\tthrow new TypeError(`${String(personId)} is not valid person id`);\n\t}\n\treturn parseStringToInt(personId.slice(7, 10)) % 2 === 1; // check if the individual number is odd\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish female.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a female, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isFemale('131052-308T') // true\n * isFemale('131052-309U') // false\n * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>\n * @since v0.0.1\n */\nexport function isFemale<BRAND = string>(personId: string): personId is string & BRAND {\n\treturn !isMale(personId);\n}\n"],"mappings":";AAAA,IAAM,uBAAuB;AAC7B,IAAM,oBAAoB,qBAAqB;AASxC,SAAS,iBAAiB,OAAmC;AACnE,MAAI,CAAC,OAAO;AACX,UAAM,IAAI,UAAU,oBAAoB;AAAA,EACzC;AACA,QAAM,cAAc,SAAS,OAAO,EAAE;AACtC,MAAI,MAAM,WAAW,GAAG;AACvB,UAAM,IAAI,UAAU,0BAA0B,KAAK,EAAE;AAAA,EACtD;AACA,SAAO;AACR;AASO,SAAS,eAAe,OAAwB;AACtD,MAAI,OAAO,UAAU,UAAU;AAC9B,UAAM,IAAI,UAAU,GAAG,OAAO,KAAK,CAAC,eAAe;AAAA,EACpD;AACA,SAAO;AACR;AASO,SAAS,YAAY,OAAuB;AAClD,QAAM,eAAe,qBAAqB,KAAK;AAC/C,MAAI,CAAC,cAAc;AAClB,UAAM,IAAI,WAAW,wBAAwB,MAAM,SAAS,CAAC,oBAAoB,OAAO,oBAAoB,CAAC,CAAC,GAAG;AAAA,EAClH;AACA,SAAO;AACR;AAwBO,SAAS,gBAAgC,UAA8C;AAC7F,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,IAAI;AAC3D,WAAO;AAAA,EACR;AACA,QAAM,gBAAgB,SAAS,YAAY;AAC3C,MAAI;AACH,WAAO,cAAc,EAAE,MAAM,YAAY,iBAAiB,cAAc,MAAM,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,CAAC,IAAI,iBAAiB;AAAA,EACtI,SAAS,QAAQ;AAChB,WAAO;AAAA,EACR;AACD;AAcO,SAAS,OAAuB,UAA8C;AACpF,MAAI,CAAC,gBAAgB,QAAQ,GAAG;AAC/B,UAAM,IAAI,UAAU,GAAG,OAAO,QAAQ,CAAC,yBAAyB;AAAA,EACjE;AACA,SAAO,iBAAiB,SAAS,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM;AACxD;AAcO,SAAS,SAAyB,UAA8C;AACtF,SAAO,CAAC,OAAO,QAAQ;AACxB;","names":[]}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["const checksumCharacterSet = '0123456789ABCDEFHJKLMNPRSTUVWXY';\nconst checkSumMapLength = checksumCharacterSet.length; // is always 31\n\n/**\n * Parse string to integer\n * @throws TypeError if value is not a number\n * @param {string | undefined} value - string to parse\n * @returns {number} value as number\n * @since v0.0.7\n */\nexport function parseStringToInt(value: string | undefined): number {\n\tif (!value) {\n\t\tthrow new TypeError('Value is undefined');\n\t}\n\tconst parsedValue = parseInt(value, 10);\n\tif (isNaN(parsedValue)) {\n\t\tthrow new TypeError(`Value is not a number: ${value}`);\n\t}\n\treturn parsedValue;\n}\n\n/**\n * Validate that value is a string\n * @throws TypeError if value is not a string\n * @param {unknown} value - value to validate\n * @returns {string} value as string\n * @since v0.0.7\n */\nexport function validateString(value: unknown): string {\n\tif (typeof value !== 'string') {\n\t\tthrow new TypeError(`${String(value)} not a string`);\n\t}\n\treturn value;\n}\n\n/**\n * Returns the checksum character from the checksum index map.\n * @param {number} index The index of the checksum character.\n * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.\n * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.\n * @since v0.0.7\n */\nexport function getCheckSum(index: number): string {\n\tconst checkSumChar = checksumCharacterSet[index];\n\tif (!checkSumChar) {\n\t\tthrow new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);\n\t}\n\treturn checkSumChar;\n}\n\n/**\n * Validates whether a string is a valid Finnish person ID.\n *\n * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:\n * - DDMMYY represents the date of birth.\n * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).\n * - ZZZ is an individual number (odd numbers are males, even numbers are females).\n * - Q is a checksum character.\n *\n * The checksum character is calculated based on the first 10 characters.\n * @param {string} personId The person ID string to validate.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID is valid, false otherwise.\n * @example\n * isValidPersonId('131052-308T') // true\n * isValidPersonId('131052-3082') // false\n * const personId = '131052-308T';\n * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>\n * // personId type is now: string & z.BRAND<'PersonID'>\n * }\n * @since v0.0.1\n */\nexport function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (typeof personId !== 'string' || personId.length !== 11) {\n\t\treturn false;\n\t}\n\tconst upperPersonId = personId.toUpperCase();\n\ttry {\n\t\treturn upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);\n\t} catch (_error) {\n\t\treturn false;\n\t}\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish male.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a male, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isMale('131052-309U') // true\n * isMale('131052-308T') // false\n * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>\n * @since v0.0.1\n */\nexport function isMale<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (!isValidPersonId(personId)) {\n\t\tthrow new TypeError(`${String(personId)} is not valid person id`);\n\t}\n\treturn parseStringToInt(personId.slice(7, 10)) % 2 === 1; // check if the individual number is odd\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish female.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a female, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isFemale('131052-308T') // true\n * isFemale('131052-309U') // false\n * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>\n * @since v0.0.1\n */\nexport function isFemale<BRAND = string>(personId: string): personId is string & BRAND {\n\treturn !isMale(personId);\n}\n"],"mappings":";AAAA,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB;;;;;;;;AAS1B,SAAgB,iBAAiB,OAAmC;CACnE,IAAI,CAAC,OACJ,MAAM,IAAI,UAAU,oBAAoB;CAEzC,MAAM,cAAc,SAAS,OAAO,EAAE;CACtC,IAAI,MAAM,WAAW,GACpB,MAAM,IAAI,UAAU,0BAA0B,OAAO;CAEtD,OAAO;AACR;;;;;;;;AASA,SAAgB,eAAe,OAAwB;CACtD,IAAI,OAAO,UAAU,UACpB,MAAM,IAAI,UAAU,GAAG,OAAO,KAAK,EAAE,cAAc;CAEpD,OAAO;AACR;;;;;;;;AASA,SAAgB,YAAY,OAAuB;CAClD,MAAM,eAAe,qBAAqB;CAC1C,IAAI,CAAC,cACJ,MAAM,IAAI,WAAW,wBAAwB,MAAM,SAAS,EAAE,mBAAmB,OAAO,oBAAoB,CAAC,EAAE,EAAE;CAElH,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,gBAAgC,UAA8C;CAC7F,IAAI,OAAO,aAAa,YAAY,SAAS,WAAW,IACvD,OAAO;CAER,MAAM,gBAAgB,SAAS,YAAY;CAC3C,IAAI;EACH,OAAO,cAAc,QAAQ,YAAY,iBAAiB,cAAc,MAAM,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,CAAC,IAAI,iBAAiB;CACtI,SAAS,QAAQ;EAChB,OAAO;CACR;AACD;;;;;;;;;;;;;AAcA,SAAgB,OAAuB,UAA8C;CACpF,IAAI,CAAC,gBAAgB,QAAQ,GAC5B,MAAM,IAAI,UAAU,GAAG,OAAO,QAAQ,EAAE,wBAAwB;CAEjE,OAAO,iBAAiB,SAAS,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM;AACxD;;;;;;;;;;;;;AAcA,SAAgB,SAAyB,UAA8C;CACtF,OAAO,CAAC,OAAO,QAAQ;AACxB"}
package/package.json CHANGED
@@ -1,93 +1,53 @@
1
1
  {
2
- "name": "fi-pin",
3
- "version": "0.0.9",
4
- "description": "Finnish Personal Identification Number validate + gender check",
5
- "main": "./dist/index.js",
6
- "module": "./dist/index.mjs",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "require": "./dist/index.js",
12
- "import": "./dist/index.mjs"
13
- }
14
- },
15
- "devDependencies": {
16
- "@cspell/eslint-plugin": "^8.18.0",
17
- "@eslint/js": "^9.23.0",
18
- "@stylistic/eslint-plugin": "^4.2.0",
19
- "@stylistic/eslint-plugin-ts": "^4.2.0",
20
- "@types/node": "^22.13.14",
21
- "@typescript-eslint/eslint-plugin": "^8.28.0",
22
- "@typescript-eslint/parser": "^8.28.0",
23
- "@vitest/coverage-v8": "^3.0.9",
24
- "c8": "^10.1.3",
25
- "eslint": "^9.23.0",
26
- "eslint-config-prettier": "^10.1.1",
27
- "eslint-import-resolver-typescript": "^4.2.5",
28
- "eslint-plugin-import": "^2.31.0",
29
- "eslint-plugin-jsdoc": "^50.6.9",
30
- "eslint-plugin-prettier": "^5.2.5",
31
- "eslint-plugin-sonarjs": "^3.0.2",
32
- "source-map-support": "^0.5.21",
33
- "tsup": "^8.4.0",
34
- "typescript": "^5.8.2",
35
- "typescript-eslint": "^8.28.0",
36
- "vite": "^6.2.3",
37
- "vitest": "^3.0.9",
38
- "zod": "^3.24.2"
39
- },
40
- "mocha": {
41
- "exit": true,
42
- "extension": [
43
- "ts",
44
- "js"
2
+ "name": "fi-pin",
3
+ "version": "0.0.10",
4
+ "description": "Finnish Personal Identification Number validate + gender check",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.mts",
8
+ "exports": {
9
+ "import": {
10
+ "types": "./dist/index.d.mts",
11
+ "default": "./dist/index.mjs"
12
+ },
13
+ "require": {
14
+ "types": "./dist/index.d.cts",
15
+ "default": "./dist/index.cjs"
16
+ },
17
+ "default": "./dist/index.mjs"
18
+ },
19
+ "devDependencies": {
20
+ "@biomejs/biome": "2.5.2",
21
+ "@types/node": "^25.9.4",
22
+ "@vitest/coverage-v8": "^4.1.9",
23
+ "c8": "^11.0.0",
24
+ "tsdown": "^0.22.3",
25
+ "typescript": "^6.0.3",
26
+ "vite": "^8.1.3",
27
+ "vitest": "^4.1.9",
28
+ "zod": "^4.4.3"
29
+ },
30
+ "scripts": {
31
+ "build": "tsdown src/index.ts --sourcemap --format cjs,esm --dts --clean",
32
+ "prepublishOnly": "npm run build",
33
+ "test": "vitest test --typecheck --run --no-isolate --coverage",
34
+ "lint": "biome check",
35
+ "validate": "tsc --noEmit"
36
+ },
37
+ "keywords": [
38
+ "pin",
39
+ "personid",
40
+ "finnish"
45
41
  ],
46
- "recursive": true,
47
- "require": [
48
- "ts-node/register",
49
- "source-map-support/register"
42
+ "files": [
43
+ "dist"
50
44
  ],
51
- "reporters": [
52
- "spec",
53
- "mocha-junit-reporter"
54
- ]
55
- },
56
- "keywords": [
57
- "pin",
58
- "personid"
59
- ],
60
- "files": [
61
- "dist"
62
- ],
63
- "nyc": {
64
- "extension": [
65
- ".ts"
66
- ],
67
- "include": [
68
- "src"
69
- ],
70
- "reporter": [
71
- "text",
72
- "lcovonly"
73
- ],
74
- "all": true
75
- },
76
- "repository": {
77
- "type": "git",
78
- "url": "git+https://github.com/mharj/hetu.git"
79
- },
80
- "author": "mharj",
81
- "license": "MIT",
82
- "bugs": {
83
- "url": "https://github.com/mharj/hetu/issues"
84
- },
85
- "homepage": "https://github.com/mharj/hetu#readme",
86
- "scripts": {
87
- "build": "tsup src/index.ts --sourcemap --format cjs,esm --dts --clean",
88
- "test": "vitest test --run --no-isolate --coverage",
89
- "coverage": "vitest test --run --no-isolate --reporter=dot --coverage --coverage.reporter=lcov",
90
- "lint": "eslint . --ext .ts",
91
- "validate": "tsc --noEmit"
92
- }
93
- }
45
+ "repository": "github:mharj/hetu",
46
+ "author": "mharj",
47
+ "license": "MIT",
48
+ "bugs": {
49
+ "url": "https://github.com/mharj/hetu/issues"
50
+ },
51
+ "homepage": "https://github.com/mharj/hetu#readme",
52
+ "packageManager": "pnpm@10.34.3+sha512.f2c531b08829d7be7f03c90addc266615f9c5477463e4bf1d275cb263ce9ea57cf0d5599110d94649a9bb3fed9f0b5efcea74a7016b61532a24cb9ad87860c1d"
53
+ }
package/dist/index.d.ts DELETED
@@ -1,75 +0,0 @@
1
- /**
2
- * Parse string to integer
3
- * @throws TypeError if value is not a number
4
- * @param {string | undefined} value - string to parse
5
- * @returns {number} value as number
6
- * @since v0.0.7
7
- */
8
- declare function parseStringToInt(value: string | undefined): number;
9
- /**
10
- * Validate that value is a string
11
- * @throws TypeError if value is not a string
12
- * @param {unknown} value - value to validate
13
- * @returns {string} value as string
14
- * @since v0.0.7
15
- */
16
- declare function validateString(value: unknown): string;
17
- /**
18
- * Returns the checksum character from the checksum index map.
19
- * @param {number} index The index of the checksum character.
20
- * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.
21
- * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.
22
- * @since v0.0.7
23
- */
24
- declare function getCheckSum(index: number): string;
25
- /**
26
- * Validates whether a string is a valid Finnish person ID.
27
- *
28
- * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:
29
- * - DDMMYY represents the date of birth.
30
- * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).
31
- * - ZZZ is an individual number (odd numbers are males, even numbers are females).
32
- * - Q is a checksum character.
33
- *
34
- * The checksum character is calculated based on the first 10 characters.
35
- * @param {string} personId The person ID string to validate.
36
- * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
37
- * @returns {boolean} True if the person ID is valid, false otherwise.
38
- * @example
39
- * isValidPersonId('131052-308T') // true
40
- * isValidPersonId('131052-3082') // false
41
- * const personId = '131052-308T';
42
- * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>
43
- * // personId type is now: string & z.BRAND<'PersonID'>
44
- * }
45
- * @since v0.0.1
46
- */
47
- declare function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND;
48
- /**
49
- * Checks whether a person ID belongs to a Finnish male.
50
- * @param {string} personId The Finnish person ID string.
51
- * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
52
- * @returns {boolean} True if the person ID belongs to a male, false otherwise.
53
- * @throws {TypeError} If the person ID is not valid.
54
- * @example
55
- * isMale('131052-309U') // true
56
- * isMale('131052-308T') // false
57
- * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>
58
- * @since v0.0.1
59
- */
60
- declare function isMale<BRAND = string>(personId: string): personId is string & BRAND;
61
- /**
62
- * Checks whether a person ID belongs to a Finnish female.
63
- * @param {string} personId The Finnish person ID string.
64
- * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).
65
- * @returns {boolean} True if the person ID belongs to a female, false otherwise.
66
- * @throws {TypeError} If the person ID is not valid.
67
- * @example
68
- * isFemale('131052-308T') // true
69
- * isFemale('131052-309U') // false
70
- * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>
71
- * @since v0.0.1
72
- */
73
- declare function isFemale<BRAND = string>(personId: string): personId is string & BRAND;
74
-
75
- export { getCheckSum, isFemale, isMale, isValidPersonId, parseStringToInt, validateString };
package/dist/index.js DELETED
@@ -1,85 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- getCheckSum: () => getCheckSum,
24
- isFemale: () => isFemale,
25
- isMale: () => isMale,
26
- isValidPersonId: () => isValidPersonId,
27
- parseStringToInt: () => parseStringToInt,
28
- validateString: () => validateString
29
- });
30
- module.exports = __toCommonJS(index_exports);
31
- var checksumCharacterSet = "0123456789ABCDEFHJKLMNPRSTUVWXY";
32
- var checkSumMapLength = checksumCharacterSet.length;
33
- function parseStringToInt(value) {
34
- if (!value) {
35
- throw new TypeError("Value is undefined");
36
- }
37
- const parsedValue = parseInt(value, 10);
38
- if (isNaN(parsedValue)) {
39
- throw new TypeError(`Value is not a number: ${value}`);
40
- }
41
- return parsedValue;
42
- }
43
- function validateString(value) {
44
- if (typeof value !== "string") {
45
- throw new TypeError(`${String(value)} not a string`);
46
- }
47
- return value;
48
- }
49
- function getCheckSum(index) {
50
- const checkSumChar = checksumCharacterSet[index];
51
- if (!checkSumChar) {
52
- throw new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);
53
- }
54
- return checkSumChar;
55
- }
56
- function isValidPersonId(personId) {
57
- if (typeof personId !== "string" || personId.length !== 11) {
58
- return false;
59
- }
60
- const upperPersonId = personId.toUpperCase();
61
- try {
62
- return upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);
63
- } catch (_error) {
64
- return false;
65
- }
66
- }
67
- function isMale(personId) {
68
- if (!isValidPersonId(personId)) {
69
- throw new TypeError(`${String(personId)} is not valid person id`);
70
- }
71
- return parseStringToInt(personId.slice(7, 10)) % 2 === 1;
72
- }
73
- function isFemale(personId) {
74
- return !isMale(personId);
75
- }
76
- // Annotate the CommonJS export names for ESM import in node:
77
- 0 && (module.exports = {
78
- getCheckSum,
79
- isFemale,
80
- isMale,
81
- isValidPersonId,
82
- parseStringToInt,
83
- validateString
84
- });
85
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const checksumCharacterSet = '0123456789ABCDEFHJKLMNPRSTUVWXY';\nconst checkSumMapLength = checksumCharacterSet.length; // is always 31\n\n/**\n * Parse string to integer\n * @throws TypeError if value is not a number\n * @param {string | undefined} value - string to parse\n * @returns {number} value as number\n * @since v0.0.7\n */\nexport function parseStringToInt(value: string | undefined): number {\n\tif (!value) {\n\t\tthrow new TypeError('Value is undefined');\n\t}\n\tconst parsedValue = parseInt(value, 10);\n\tif (isNaN(parsedValue)) {\n\t\tthrow new TypeError(`Value is not a number: ${value}`);\n\t}\n\treturn parsedValue;\n}\n\n/**\n * Validate that value is a string\n * @throws TypeError if value is not a string\n * @param {unknown} value - value to validate\n * @returns {string} value as string\n * @since v0.0.7\n */\nexport function validateString(value: unknown): string {\n\tif (typeof value !== 'string') {\n\t\tthrow new TypeError(`${String(value)} not a string`);\n\t}\n\treturn value;\n}\n\n/**\n * Returns the checksum character from the checksum index map.\n * @param {number} index The index of the checksum character.\n * @returns {string} The checksum character at the specified index, or undefined if the index is out of bounds.\n * @throws {RangeError} If the index is less than 0 or greater than or equal to checkSumMapLength.\n * @since v0.0.7\n */\nexport function getCheckSum(index: number): string {\n\tconst checkSumChar = checksumCharacterSet[index];\n\tif (!checkSumChar) {\n\t\tthrow new RangeError(`Index out of bounds: ${index.toString()} (valid range: 0-${String(checkSumMapLength - 1)})`);\n\t}\n\treturn checkSumChar;\n}\n\n/**\n * Validates whether a string is a valid Finnish person ID.\n *\n * A valid Finnish person ID consists of 11 characters in the format DDMMYYCZZZQ, where:\n * - DDMMYY represents the date of birth.\n * - C is the century sign ('+' for 1800s, '-' for 1900s, 'A' for 2000s).\n * - ZZZ is an individual number (odd numbers are males, even numbers are females).\n * - Q is a checksum character.\n *\n * The checksum character is calculated based on the first 10 characters.\n * @param {string} personId The person ID string to validate.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID is valid, false otherwise.\n * @example\n * isValidPersonId('131052-308T') // true\n * isValidPersonId('131052-3082') // false\n * const personId = '131052-308T';\n * if (isValidPersonId<z.BRAND<'PersonID'>>(personId)) { // personId type is now: string & z.BRAND<'PersonID'>\n * // personId type is now: string & z.BRAND<'PersonID'>\n * }\n * @since v0.0.1\n */\nexport function isValidPersonId<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (typeof personId !== 'string' || personId.length !== 11) {\n\t\treturn false;\n\t}\n\tconst upperPersonId = personId.toUpperCase();\n\ttry {\n\t\treturn upperPersonId[10] === getCheckSum(parseStringToInt(upperPersonId.slice(0, 6) + upperPersonId.slice(7, 10)) % checkSumMapLength);\n\t} catch (_error) {\n\t\treturn false;\n\t}\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish male.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a male, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isMale('131052-309U') // true\n * isMale('131052-308T') // false\n * isMale<z.BRAND<'PersonIdMale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdMale'>\n * @since v0.0.1\n */\nexport function isMale<BRAND = string>(personId: string): personId is string & BRAND {\n\tif (!isValidPersonId(personId)) {\n\t\tthrow new TypeError(`${String(personId)} is not valid person id`);\n\t}\n\treturn parseStringToInt(personId.slice(7, 10)) % 2 === 1; // check if the individual number is odd\n}\n\n/**\n * Checks whether a person ID belongs to a Finnish female.\n * @param {string} personId The Finnish person ID string.\n * @template BRAND Optional brand type for a more strict type (e.g., use Zod branded type or custom type).\n * @returns {boolean} True if the person ID belongs to a female, false otherwise.\n * @throws {TypeError} If the person ID is not valid.\n * @example\n * isFemale('131052-308T') // true\n * isFemale('131052-309U') // false\n * isFemale<z.BRAND<'PersonIdFemale'>>(personId) // if true, personId type is now: string & z.BRAND<'PersonIdFemale'>\n * @since v0.0.1\n */\nexport function isFemale<BRAND = string>(personId: string): personId is string & BRAND {\n\treturn !isMale(personId);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,uBAAuB;AAC7B,IAAM,oBAAoB,qBAAqB;AASxC,SAAS,iBAAiB,OAAmC;AACnE,MAAI,CAAC,OAAO;AACX,UAAM,IAAI,UAAU,oBAAoB;AAAA,EACzC;AACA,QAAM,cAAc,SAAS,OAAO,EAAE;AACtC,MAAI,MAAM,WAAW,GAAG;AACvB,UAAM,IAAI,UAAU,0BAA0B,KAAK,EAAE;AAAA,EACtD;AACA,SAAO;AACR;AASO,SAAS,eAAe,OAAwB;AACtD,MAAI,OAAO,UAAU,UAAU;AAC9B,UAAM,IAAI,UAAU,GAAG,OAAO,KAAK,CAAC,eAAe;AAAA,EACpD;AACA,SAAO;AACR;AASO,SAAS,YAAY,OAAuB;AAClD,QAAM,eAAe,qBAAqB,KAAK;AAC/C,MAAI,CAAC,cAAc;AAClB,UAAM,IAAI,WAAW,wBAAwB,MAAM,SAAS,CAAC,oBAAoB,OAAO,oBAAoB,CAAC,CAAC,GAAG;AAAA,EAClH;AACA,SAAO;AACR;AAwBO,SAAS,gBAAgC,UAA8C;AAC7F,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,IAAI;AAC3D,WAAO;AAAA,EACR;AACA,QAAM,gBAAgB,SAAS,YAAY;AAC3C,MAAI;AACH,WAAO,cAAc,EAAE,MAAM,YAAY,iBAAiB,cAAc,MAAM,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,CAAC,IAAI,iBAAiB;AAAA,EACtI,SAAS,QAAQ;AAChB,WAAO;AAAA,EACR;AACD;AAcO,SAAS,OAAuB,UAA8C;AACpF,MAAI,CAAC,gBAAgB,QAAQ,GAAG;AAC/B,UAAM,IAAI,UAAU,GAAG,OAAO,QAAQ,CAAC,yBAAyB;AAAA,EACjE;AACA,SAAO,iBAAiB,SAAS,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM;AACxD;AAcO,SAAS,SAAyB,UAA8C;AACtF,SAAO,CAAC,OAAO,QAAQ;AACxB;","names":[]}