Skip to content

Commit

Permalink
docs: add JSDoc annotations to functions (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsevdos authored Mar 10, 2024
1 parent dbce203 commit 2cf7683
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 7 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
## Description

elUtils is a **zero-dependency** modern package, written in TypeScript, fully typed, that solves various geolocation, validation, and date-related issues by providing a broad collection of easy to use functions.
elUtils is a **zero-dependency** modern package, written in TypeScript, strongly-typed, that solves various geolocation, validation, and date-related issues by providing a broad collection of easy to use functions.

Check all the [available functions here](#api) (by module category).

<!-- todo: runs on many JavaScript runtimes (such as node, deno and bun) -->
<!--
todo: runs on many JavaScript runtimes (such as node, deno and bun)
https://paka.dev/npm/@tsevdos/[email protected]/api
-->

## Installation

Expand Down Expand Up @@ -36,7 +39,7 @@ console.log(getAdministrativeRegions());

## API<a id='api'></a>

todo: The library is currently split into specific modules
The library is currently split into the following modules:

- [**`geoUtils`**](https://github.com/tsevdos/elUtils/blob/main/docs/geoUtils.md)

Expand Down
20 changes: 20 additions & 0 deletions src/dateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ type BaseDateTimeOptions = {
format?: Format;
};

/**
* Returns the days based on the provided options.
* @param {BaseDateTimeOptions} options - The options for locale and format.
* @returns {string[]} The days in the specified locale and format.
*/
export function getDays(options: BaseDateTimeOptions = {}) {
const { locale = "el", format = "full" } = options;

Expand All @@ -17,6 +22,11 @@ type MonthsBaseDateTimeOptions = Omit<BaseDateTimeOptions, "format"> & {
format?: Format | "alternative";
};

/**
* Returns the months based on the provided options.
* @param {MonthsBaseDateTimeOptions} options - The options for locale and format.
* @returns {string[]} The months in the specified locale and format.
*/
export function getMonths(options: MonthsBaseDateTimeOptions = {}) {
const { locale = "el", format = "full" } = options;

Expand All @@ -27,12 +37,22 @@ type DateTimeOptionsWithoutMin = Omit<BaseDateTimeOptions, "format"> & {
format?: Exclude<Format, "min">;
};

/**
* Returns the quarters based on the provided options.
* @param {DateTimeOptionsWithoutMin} options - The options for locale and format.
* @returns {string[]} The quarters in the specified locale and format.
*/
export function getQuarters(options: DateTimeOptionsWithoutMin = {}) {
const { locale = "el", format = "full" } = options;

return datesData.quarters[locale][format];
}

/**
* Returns the eras based on the provided options.
* @param {DateTimeOptionsWithoutMin} options - The options for locale and format.
* @returns {string[]} The eras in the specified locale and format.
*/
export function getEras(options: DateTimeOptionsWithoutMin = {}) {
const { locale = "el", format = "full" } = options;

Expand Down
60 changes: 60 additions & 0 deletions src/geoUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ type AdministrativeRegionsOptions = {
level?: "region" | "unit" | "municipality";
};

/**
* Returns the administrative regions based on the provided options.
* @param {AdministrativeRegionsOptions} options - The options for locale, whether to include Mount Athos and the level area ("region" | "unit" | "municipality") to retrieve
* @returns {Region[] | RegionWithoutUnits[]} The administrative regions in the specified locale and the level area to retrieve.
*/
export function getAdministrativeRegions({
locale = "el",
includeMountAthos = false,
Expand All @@ -73,6 +78,11 @@ export function getAdministrativeRegions({

type AdministrativeRegionByIdOptions = { id: number } & AdministrativeRegionsOptions;

/**
* Returns the administrative region with the provided ID.
* @param {AdministrativeRegionByIdOptions} options - The options for ID, locale, whether to include Mount Athos and the level area ("region" | "unit" | "municipality") to retrieve
* @returns {Region | RegionWithoutUnits | undefined} The administrative region with the specified ID, or `undefined` if no such region exists.
*/
export function getAdministrativeRegionById(
options: AdministrativeRegionByIdOptions,
): Region | RegionWithoutUnits | undefined {
Expand All @@ -84,6 +94,11 @@ export function getAdministrativeRegionById(

type AdministrativeRegionByIsoCodeOptions = { isocode: string } & AdministrativeRegionsOptions;

/**
* Returns the administrative region with the provided ISO code.
* @param {AdministrativeRegionByIsoCodeOptions} options - The options for ISO code, locale, whether to include Mount Athos and the level area ("region" | "unit" | "municipality") to retrieve
* @returns {Region | RegionWithoutUnits | undefined} The administrative region with the specified ISO code, or `undefined` if no such region exists.
*/
export function getAdministrativeRegionByIsoCode(
options: AdministrativeRegionByIsoCodeOptions,
): Region | RegionWithoutUnits | undefined {
Expand All @@ -99,6 +114,11 @@ type AdministrativeUnitsOptions = {
level?: "unit" | "municipality";
};

/**
* Returns the administrative units based on the provided options.
* @param {AdministrativeUnitsOptions} options - The options for locale, whether to include Mount Athos, and the level area ("unit" | "municipality") to retrieve
* @returns {Unit[] | UnitWithoutMunicipalities[]} The administrative units in the specified locale and level.
*/
export function getAdministrativeUnits({
locale = "el",
includeMountAthos = false,
Expand All @@ -117,6 +137,11 @@ export function getAdministrativeUnits({

type AdministrativeUnitByIdOptions = { id: number } & AdministrativeUnitsOptions;

/**
* Returns the administrative unit with the provided ID.
* @param {AdministrativeUnitByIdOptions} options - The options for ID, locale, whether to include Mount Athos, and the level area ("unit" | "municipality") to retrieve
* @returns {Unit | UnitWithoutMunicipalities | undefined} The administrative unit with the specified ID, or `undefined` if no such unit exists.
*/
export function getAdministrativeUnitById(
options: AdministrativeUnitByIdOptions,
): Unit | UnitWithoutMunicipalities | undefined {
Expand All @@ -128,6 +153,11 @@ export function getAdministrativeUnitById(

type MunicipalitiesOptions = { locale?: Locale };

/**
* Returns the municipalities in the provided locale.
* @param {MunicipalitiesOptions} options - The options for locale.
* @returns {Municipality[]} The municipalities in the specified locale.
*/
export function getMunicipalities({ locale = "el" }: MunicipalitiesOptions = {}): Municipality[] {
const municipalities = (getAdministrativeUnits({ locale }) as Unit[]).flatMap(({ municipalities }) => [
...municipalities,
Expand All @@ -138,12 +168,22 @@ export function getMunicipalities({ locale = "el" }: MunicipalitiesOptions = {})

type GeographicRegionOptions = { locale?: Locale };

/**
* Returns the geographic regions in the provided locale.
* @param {GeographicRegionOptions} options - The options for locale.
* @returns {GeographicRegion[]} The geographic regions in the specified locale.
*/
export function getGeographicRegions({ locale = "el" }: GeographicRegionOptions = {}): GeographicRegion[] {
return geographicRegions[locale];
}

type GeographicRegionByIdOptions = { id: number } & GeographicRegionOptions;

/**
* Returns the geographic region with the specific ID.
* @param {GeographicRegionByIdOptions} options - The options for ID and locale.
* @returns {GeographicRegion | undefined} The geographic region with the specified ID, or `undefined` if no such region exists.
*/
export function getGeographicRegionById(options: GeographicRegionByIdOptions): GeographicRegion | undefined {
const { id, locale = "el" } = options;

Expand All @@ -152,6 +192,11 @@ export function getGeographicRegionById(options: GeographicRegionByIdOptions): G

type PrefecturesOptions = { locale?: Locale; includeMountAthos?: boolean };

/**
* Returns the prefectures based on the provided options.
* @param {PrefecturesOptions} options - The options for locale and whether to include Mount Athos.
* @returns {Prefecture[]} The prefectures in the specified locale.
*/
export function getPrefectures({ locale = "el", includeMountAthos = false }: PrefecturesOptions = {}): Prefecture[] {
const prefectures = includeMountAthos ? allPrefectures[locale] : prefecturesWithoutMountAthos[locale];

Expand All @@ -160,13 +205,22 @@ export function getPrefectures({ locale = "el", includeMountAthos = false }: Pre

type PrefectureByIdOptions = { id: number } & PrefecturesOptions;

/**
* Returns the prefecture with the provided ID.
* @param {PrefectureByIdOptions} options - The options for ID, locale, and whether to include Mount Athos.
* @returns {Prefecture | undefined} The prefecture with the specified ID, or `undefined` if no such prefecture exists.
*/
export function getPrefectureById(options: PrefectureByIdOptions): Prefecture | undefined {
const { id, locale = "el", includeMountAthos = false } = options;
const prefecturesData = getPrefectures({ locale, includeMountAthos });

return prefecturesData.find((region) => region.id === id);
}

/**
* Returns all postal codes.
* @returns {string[]} An array of all postal codes.
*/
export function getAllPostalCodes(): string[] {
return postalCodes.flatMap(({ postalCodes }) => postalCodes);
}
Expand All @@ -176,6 +230,12 @@ type FindByPostalCodeOptions = {
entity: "prefecture" | "region" | "unit";
};

/**
* Returns the prefecture, region, or unit associated with the provided postal code.
* @param {string} postalCode - The postal code to search for.
* @param {FindByPostalCodeOptions} options - The options for locale and entity type.
* @returns {Prefecture | Region | Unit | undefined} The prefecture, region, or unit associated with the postal code, or `undefined` if no such entity exists.
*/
export function findByPostalCode(
postalCode: string,
options?: Partial<FindByPostalCodeOptions>,
Expand Down
23 changes: 19 additions & 4 deletions src/validationUtils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import postalCodes from "../data/postal-codes.json";

/**
* Validates the given postal code.
* @param {string} postalCode - The postal code to validate.
* @returns {boolean} `true` if the postal code is valid, `false` otherwise.
*/
export function validatePostalCode(postalCode: string): boolean {
const validPostalCodes = postalCodes.flatMap(({ postalCodes }) => [...postalCodes]);

return validPostalCodes.includes(postalCode);
}

/**
* Validates the given AMKA (Social Security Number in Greece).
* @param {string | number} amka - The AMKA to validate.
* @returns {boolean} `true` if the AMKA number is valid, `false` otherwise.
*/
export function validateAMKA(amka: string | number): boolean {
const strAmka = amka.toString();

// AMKA should be 11 digits long
if (/^\d{11}$/.exec(strAmka) === null || strAmka == "00000000000") {
return false;
}

// The first 6 digits is the date-of-birth in DDMMYY format
const day = parseInt(strAmka.substring(0, 2));
const month = parseInt(strAmka.substring(2, 4));
Expand All @@ -21,31 +33,34 @@ export function validateAMKA(amka: string | number): boolean {
if (day > 31) {
return false;
}

if (month > 12) {
return false;
}

const dateObj = new Date(Date.UTC(year, month - 1, day));

// The code above can provide false positives
// For example, JS will translate 30/02/2024 to 01/03/2024 :S
// To prevent this, we make sure that the month of the Date object
// is the same as the input month
if (parseInt(("0" + (dateObj.getUTCMonth() + 1)).slice(-2)) != month) {
if (parseInt(("0" + (dateObj.getUTCMonth() + 1)).slice(-2)) !== month) {
return false;
}

// The last digit is a check digit computed using the Luhn algorithm
// (https://en.wikipedia.org/wiki/Luhn_algorithm):
// Go through every digit of the AMKA number
const sum = strAmka.split("").reduce((a, c, i) => {
const sum = strAmka.split("").reduce((acc, value, index) => {
// Multiply every other digit by 2
let d = parseInt(c) * ((i % 2) + 1);
let d = parseInt(value) * ((index % 2) + 1);

// If it's a 2-digit number, sum its digits
if (d > 9) {
d = d - 9;
}
// Add the result to the sum
return a + d;
return acc + d;
}, 0);

// The sum should be divisible by 10
Expand Down

0 comments on commit 2cf7683

Please sign in to comment.