UNPKG

@island.is/kennitala

Version:

Icelandic social security number (kennitölur) utilities for servers and clients

299 lines (293 loc) 9.13 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { default: () => src_default, format: () => format, generateCompany: () => generateCompany, generatePerson: () => generatePerson, generateTemporary: () => generateTemporary, info: () => info, isCompany: () => isCompany2, isPerson: () => isPerson2, isTemporary: () => isTemporary2, isValid: () => isValid, sanitize: () => sanitize }); module.exports = __toCommonJS(src_exports); // src/utils.ts var MAGIC_NUMBERS = [3, 2, 7, 6, 5, 4, 3, 2, 0, 0]; var padZero = (num) => num < 10 ? `0${num}` : `${num}`; var sanitizeInput = (kennitala) => { return typeof kennitala === "string" && /^\d{6}-?\d{4}$/.test(kennitala) ? kennitala.replace(/\D+/g, "") : void 0; }; var getCentury = (centuryCode) => { switch (centuryCode) { case 0: return "20"; case 9: return "19"; case 8: return "18"; default: return null; } }; var calculateChecksumRemainder = (kt) => { let sum = 0; for (let i = 0; i < 8; i++) { sum += parseInt(kt[i], 10) * MAGIC_NUMBERS[i]; } const remainder = 11 - sum % 11; return remainder === 10 ? null : remainder === 11 ? 0 : remainder; }; // src/validation.ts var evaluate = (kt, entityEvaluationFn) => { if (kt.length !== 10 || entityEvaluationFn && !entityEvaluationFn(kt)) { return false; } const remainder = calculateChecksumRemainder(kt); const checkDigit = parseInt(kt.charAt(8), 10); return remainder !== null && remainder === checkDigit; }; var isValidDate = (kt) => { if (!["0", "9", "8"].includes(kt.substring(9, 10))) return false; if (["710269", "700269", "690269"].includes(kt.substring(0, 6))) return true; let day = parseInt(kt.substring(0, 2), 10); const month = parseInt(kt.substring(2, 4), 10); const yearSuffix = kt.substring(4, 6); const centuryCode = parseInt(kt.substring(9), 10); const yearPrefix = getCentury(centuryCode); if (!yearPrefix) return false; if (isNaN(day) || isNaN(month)) { return false; } if (day > 40 && day <= 71) { day -= 40; } const year = parseInt(`${yearPrefix}${yearSuffix}`, 10); const date = new Date(Date.UTC(year, month - 1, day)); return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day; }; var isPerson = (kt) => { const day = parseInt(kt.substring(0, 2), 10); return day > 0 && day <= 31; }; var isTestPerson = (kt) => { const day = parseInt(kt.substring(0, 2), 10); const digits78 = kt.substring(6, 8); return day > 0 && day <= 31 && (digits78 === "14" || digits78 === "15"); }; var isCompany = (kt) => { const day = parseInt(kt.substring(0, 2), 10); return day > 40 && day <= 71; }; var isTemporary = (kt) => kt.startsWith("8") || kt.startsWith("9"); var getDefaultOptions = (options) => { return { allowTestDataset: !!options && options.allowTestDataset === true }; }; // src/generation.ts var generateKennitala = (date, entityFn, startingIncrement) => { let day = date.getUTCDate(); day = entityFn(day); const month = date.getUTCMonth() + 1; const year = date.getUTCFullYear(); const yearSuffix = year.toString().slice(-2); let kt = `${padZero(day)}${padZero(month)}${yearSuffix}`; const randomAndChecksum = (kt2) => { const digit7 = Math.floor(Math.random() * 10); const digit8 = Math.floor(Math.random() * 10); const tempKt = kt2 + digit7.toString() + digit8.toString(); const remainder = calculateChecksumRemainder(tempKt); return remainder === null ? randomAndChecksum(kt2) : `${digit7}${digit8}${remainder}`; }; const incrementingChecksum = (kt2, incrementFrom) => { let inc = incrementFrom; while (inc < 100) { const digits = padZero(inc).split(""); const digit7 = digits[0]; const digit8 = digits[1]; const tempKt = kt2 + digit7 + digit8; const remainder = calculateChecksumRemainder(tempKt); if (remainder === null) { inc++; continue; } else { return `${digit7}${digit8}${remainder}`; } } return void 0; }; let digits789; if (startingIncrement) { digits789 = incrementingChecksum(kt, startingIncrement); if (!digits789) return ""; } else { digits789 = randomAndChecksum(kt); } kt += digits789; const centuryDigit = year.toString()[1]; kt += centuryDigit; return kt; }; var generatePerson = (date, startingIncrement = 20) => { return generateKennitala(date, personDayDelta, startingIncrement); }; var generateCompany = (date) => { return generateKennitala(date, companyDayDelta); }; var generateTemporary = () => { const digits = "0123456789"; let kt = "89"[Math.floor(Math.random())]; for (let i = 0; i < 9; i++) { kt += digits[Math.floor(Math.random() * digits.length)]; } return kt; }; var personDayDelta = (day) => day; var companyDayDelta = (day) => day + 40; // src/index.ts var isValid = (kennitala, options) => { const kt = sanitizeInput(kennitala); if (!kt) return false; if (isTemporary(kt)) return true; const opts = getDefaultOptions(options); const person = evaluate(kt, isPerson); const testPersonResult = evaluate(kt, isTestPerson); const company = evaluate(kt, isCompany); const dateValid = isValidDate(kt); return dateValid && (person || company || testPersonResult && opts.allowTestDataset === true); }; var isPerson2 = (kennitala, options) => { const kt = sanitizeInput(kennitala); if (!kt) return false; const dateValid = isValidDate(kt); if (isTestPerson(kt) && options?.allowTestDataset) { return dateValid && evaluate(kt, isTestPerson); } else { return dateValid && evaluate(kt, isPerson); } }; var isCompany2 = (kennitala) => { const kt = sanitizeInput(kennitala); if (!kt) return false; const dateValid = isValidDate(kt); return dateValid && evaluate(kt, isCompany); }; var isTemporary2 = (kennitala) => { const kt = sanitizeInput(kennitala); return kt ? isTemporary(kt) : false; }; var sanitize = (kennitala) => sanitizeInput(kennitala) ?? ""; var format = (kennitala, spacer = true) => { const kt = kennitala.replace(/\D+/g, ""); return `${kt.slice(0, 6)}${spacer && kt.length > 6 ? "-" : ""}${kt.slice(6)}`; }; var info = (kennitala, options) => { const kt = sanitizeInput(kennitala); if (!kt) { return { kt: "", valid: false, type: "invalid", birthday: /* @__PURE__ */ new Date(NaN), birthdayReadable: "", age: NaN }; } if (isTemporary2(kt)) { return { kt, valid: true, type: "temporary", birthday: /* @__PURE__ */ new Date(NaN), birthdayReadable: "", age: NaN }; } if (isPerson2(kt, options) || isCompany2(kt)) { let day = parseInt(kt.substring(0, 2), 10); if (day > 40) { day -= 40; } const month = parseInt(kt.substring(2, 4), 10); const yearSuffix = kt.substring(4, 6); const centuryCode = parseInt(kt.substring(9), 10); const yearPrefix = getCentury(centuryCode); const year = parseInt(`${yearPrefix}${yearSuffix}`, 10); const birthday = new Date(Date.UTC(year, month - 1, day)); const today = /* @__PURE__ */ new Date(); const todayUTC = new Date( Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()) ); let age = todayUTC.getUTCFullYear() - birthday.getUTCFullYear(); const m = todayUTC.getUTCMonth() - birthday.getUTCMonth(); const d = todayUTC.getUTCDate() - birthday.getUTCDate(); if (m < 0 || m === 0 && d < 0) { age--; } if (age < 0) { age += 100; } return { kt, valid: true, type: isPerson(kt) ? "person" : "company", birthday, birthdayReadable: birthday.toUTCString(), age }; } return { kt, valid: false, type: "invalid", birthday: /* @__PURE__ */ new Date(NaN), birthdayReadable: "", age: NaN }; }; var src_default = { isValid, isPerson: isPerson2, isCompany: isCompany2, isTemporary: isTemporary2, sanitize, format, info, generatePerson, generateCompany, generateTemporary }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { format, generateCompany, generatePerson, generateTemporary, info, isCompany, isPerson, isTemporary, isValid, sanitize });