@island.is/kennitala
Version:
Icelandic social security number (kennitölur) utilities for servers and clients
299 lines (293 loc) • 9.13 kB
JavaScript
;
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
});