@obosbbl/validation
Version:
A collection of validation methods for OBOS
198 lines (195 loc) • 6.43 kB
JavaScript
/** Strip all formatting, leaving only numbers and letters */ function stripFormatting(value) {
return value.replace(/[^a-zA-Z0-9]/g, '');
}
/**
* Used to validate Norwegian bank account numbers, organization numbers and national identity numbers.
* See https://no.wikipedia.org/wiki/MOD11
*/ function mod11(value, weights) {
// Since the control digit is a single value, the lengths should be equal
if (weights.length + 1 !== value.length) {
return false;
}
let sum = 0;
weights.forEach((weight, index)=>{
sum += weight * Number(value[index]);
});
let controlNumber = 11 - sum % 11;
if (controlNumber === 11) {
controlNumber = 0;
}
return controlNumber === Number(value[value.length - 1]);
}
function isValidDate(year, month, day, /** Whether to check the year as part of the date validation. */ validateYear = false) {
// biome-ignore lint/style/noParameterAssign: months are zero index 🤷♂️
month -= 1;
// important to use UTC so the user's timezone doesn't affect the validation
const date = new Date(Date.UTC(year, month, day));
const validYear = validateYear ? date.getUTCFullYear() === year : true;
return date && validYear && date.getUTCMonth() === month && date.getUTCDate() === day;
}
/**
* Validates that the input value is a Norwegian postal (zip) code.
* @example
* ```
* validatePostalCode('0000') // => true
* ```
*/ function validatePostalCode(value, options = {}) {
if (options.allowFormatting) {
// biome-ignore lint/style/noParameterAssign:
value = stripFormatting(value);
}
return /^\d{4}$/.test(value);
}
/**
* Validates that the input value is a Norwegian phone number.
*
* Supports mobile only validation.
* @example
* ```
* validatePhoneNumber('00000000') // => true
* validatePhoneNumber('90000000', { mobileOnly: true }) // => true
* ```
*/ function validatePhoneNumber(value, options = {}) {
if (options.allowFormatting) {
// biome-ignore lint/style/noParameterAssign:
value = stripFormatting(value);
}
const isPhoneNumber = /^\d{8}$/.test(value);
if (options.mobileOnly) {
// Norwegian mobile phone numbers start with 4 or 9
// See https://nkom.no/telefoni-og-telefonnummer/telefonnummer-og-den-norske-nummerplan/alle-nummerserier-for-norske-telefonnumre
return isPhoneNumber && [
'4',
'9'
].includes(value.charAt(0));
}
return isPhoneNumber;
}
/**
* Validates that the input value is a {@link https://www.brreg.no/om-oss/registrene-vare/om-enhetsregisteret/organisasjonsnummeret/ Norwegian organization number}.
* @example
* ```
* validateOrganizationNumber('000000000') // => true
* ```
*/ function validateOrganizationNumber(value, options = {}) {
if (options.allowFormatting) {
// biome-ignore lint/style/noParameterAssign:
value = stripFormatting(value);
}
/** References:
* https://www.brreg.no/om-oss/registrene-vare/om-enhetsregisteret/organisasjonsnummeret/
* https://no.wikipedia.org/wiki/Organisasjonsnummer
*/ return mod11(value, [
3,
2,
7,
6,
5,
4,
3,
2
]);
}
/**
* Validates that the input value is an OBOS membership number.
* @example
* ```
* validateObosMembershipNumber('0000000') // => true
* ```
*/ function validateObosMembershipNumber(value, options = {}) {
if (options.allowFormatting) {
// biome-ignore lint/style/noParameterAssign:
value = stripFormatting(value);
}
return /^\d{7}$/.test(value);
}
/**
* Validates that the input value is a Norwegian national identity number (fødselsnummer or d-nummer).
*
* It validates the control digits and checks if the date of birth is valid.
*
* @example
* ```
* validatePersonalIdentityNumber('DDMMYYXXXXX') // => true
* ```
*/ function validateNationalIdentityNumber(value, options = {}) {
if (options.allowFormatting) {
// biome-ignore lint/style/noParameterAssign:
value = stripFormatting(value);
}
// Norwegian national identity numbers use mod 11 with two control digits.
// The first one is calculated for the first 10d digits
// while the last one uses all 11 digits
const valueForControlDigit1 = value.slice(0, -1);
const controlDigit1 = mod11(valueForControlDigit1, [
3,
7,
6,
1,
8,
9,
4,
5,
2
]);
const controlDigit2 = mod11(value, [
5,
4,
3,
2,
7,
6,
5,
4,
3,
2
]);
if (!controlDigit1 || !controlDigit2) {
return false;
}
// copy/inspiration from NAV https://github.com/navikt/fnrvalidator/blob/77e57f0bc8e3570ddc2f0a94558c58d0f7259fe0/src/validator.ts#L108
let day = Number(value.substring(0, 2));
const month = Number(value.substring(2, 4));
let year = Number(value.substring(4, 6));
// 1900 isn't a leap year, but 2000 is. Since JS two digits years to the Date constructor is an offset from the year 1900
// we need to special handle that case. For other cases it doesn't really matter if the year is 1925 or 2025.
if (year === 0) {
year = 2000;
}
// for a d-number the day is increased by 40. Eg the 31st of a month would be 71, or the 3rd would be 43.
// thus we need to subtract 40 to get the correct day of the month
if (day > 40) {
day = day - 40;
}
return isValidDate(year, month, day);
}
/**
* Validates that the input value is a Norwegian bank account number (kontonummer).
*
* It validates the control digit.
*
* @example
* ```
* validateAccountNumber('XXXXXXXXXXX') // => true
* ```
*/ function validateAccountNumber(value, options = {}) {
if (options.allowFormatting) {
// biome-ignore lint/style/noParameterAssign:
value = stripFormatting(value);
}
// Norwegian bank account numbers use mod 11 with one control digits.
// The first one is calculated for all 11 digits
return mod11(value, [
5,
4,
3,
2,
7,
6,
5,
4,
3,
2
]);
}
export { validateAccountNumber, validateNationalIdentityNumber, validateObosMembershipNumber, validateOrganizationNumber, validatePhoneNumber, validatePostalCode };