Skip to content

Commit

Permalink
feat: ✨ add validation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardDorian committed Oct 19, 2022
1 parent aac40ce commit 36feb3b
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 23 deletions.
4 changes: 2 additions & 2 deletions types/algorithms.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ declare module 'nodejs-vitals/algorithms' {
* binarySearch(arr, 'c'); // 2
* binarySearch(arr, 'z'); // -1
* ```
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
export function binarySearch<T = any>(array: T[], key: T): number;
/**
Expand All @@ -50,7 +50,7 @@ declare module 'nodejs-vitals/algorithms' {
* const arr = [ 'b', 'y', 'q', 'k' ];
* selectionSort(arr); // [ 'b', 'k', 'q', 'y' ]
* ```
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
export function selectionSort<T = any>(array: T[]): T[];
}
32 changes: 16 additions & 16 deletions types/structures.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,37 @@ declare module 'nodejs-vitals/structures' {
* // If you are using TypeScript you can use the type parameter to specify the type of the queue
* const typedQueue = new Queue<string>();
* ```
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public constructor(maxSize?: number);
/**
* Push a value at the end of the queue
* @param value Value to add to the queue
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public enqueue(value: T): void;
/**
* Removes and returns the first item from the queue
* @returns The item removed from the queue
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public dequeue(): T;
/**
* Returns true if the queue is empty, false otherwise
* @returns True if the queue is empty, false otherwise
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public isEmpty(): boolean;
/**
* Returns true if the queue is full, false otherwise
* @returns True if the queue is full, false otherwise
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public isFull(): boolean;
}
/**
* Node class for the Queue
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
export class QueueNode<T = any> {
/** Value stored in the node */
Expand All @@ -62,13 +62,13 @@ declare module 'nodejs-vitals/structures' {
/**
* Instantiate a new QueueNode
* @param value Value to store in the node
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public constructor(value: T);
}
/**
* Stack data structure
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
export class Stack<T = any> {
/** Number of items currently in the stack */
Expand All @@ -90,44 +90,44 @@ declare module 'nodejs-vitals/structures' {
*
* // If you are using TypeScript you can use the type parameter to specify the type of the stack
* const typedStack = new Queue<string>();
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
* ```
*/
public constructor(maxSize?: number);
/**
* Push a value at the top of the stack
* @param value Value to push onto the stack
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public push(value: T): void;
/**
* Removes and returns the top item from the stack
* @returns The item removed from the stack
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public pop(): T;
/**
* Returns the top item from the stack without removing it
* @returns The top item from the stack
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public peek(): T;
/**
* Returns true if the stack is empty, false otherwise
* @returns True if the stack is empty, false otherwise
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public isEmpty(): boolean;
/**
* Returns true if the stack is full, false otherwise
* @returns True if the stack is full, false otherwise
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public isFull(): boolean;
}
/**
* Node class for the Stack
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
export class StackNode<T = any> {
/** Value stored in the node */
Expand All @@ -137,7 +137,7 @@ declare module 'nodejs-vitals/structures' {
/**
* Instantiate a new StackNode
* @param value Value to store in the node
* @since `nodejs-utilities@1.0.0`
* @since `nodejs-vitals@1.0.0`
*/
public constructor(value: T);
}
Expand Down
10 changes: 5 additions & 5 deletions types/utilities.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ declare module 'nodejs-vitals/utilities' {
* generateRandomNumberE(0, 10); // 3
* generateRandomNumberE(0, 10); // 6
* ```
* @since `nodejs-utilities@1.0.1`
* @since `nodejs-vitals@1.0.1`
*/
export function generateRandomNumberE(min: number, max: number): number;
/**
Expand All @@ -26,23 +26,23 @@ declare module 'nodejs-vitals/utilities' {
* generateRandomNumberI(0, 10); // 10
* generateRandomNumberI(0, 10); // 5
* ```
* @since `nodejs-utilities@1.0.1`
* @since `nodejs-vitals@1.0.1`
*/
export function generateRandomNumberI(min: number, max: number): number;
/**
* Reads and parses as JSON the given
* file path synchronously
* @param path A path to a file
* @returns An object containing the parsed file
* @since `nodejs-utilities@1.0.1`
* @since `nodejs-vitals@1.0.1`
*/
export function readJSONFileSync<T = any>(path: string): T;
/**
* Reads and parses as JSON the given
* file path asynchronously
* @param path A path to a file
* @returns An object containing the parsed file
* @since `nodejs-utilities@1.0.1`
* @since `nodejs-vitals@1.0.1`
*/
export function readJSONFile<T = any>(path: string): Promise<T>;
/**
Expand All @@ -61,7 +61,7 @@ declare module 'nodejs-vitals/utilities' {
* await wait(10 * 1000);
* console.log('Bar, printed 10 seconds after the first log')
* ```
* @since `nodejs-utilities@1.0.1`
* @since `nodejs-vitals@1.0.1`
*/
export function wait(ms: number): Promise<void>;
}
69 changes: 69 additions & 0 deletions types/validation.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,77 @@ type Types = {
};

declare module 'nodejs-vitals/validation' {
/**
* Checks whether or not all the items in the
* given array are type of the given type
* @param type Type to check the items in the array for
* @param arr The array to check
* @returns Whether or not all the items are
* type of the given type
* @example
* ```javascript
* const arr = [ 2, 4, 'Hello World!', 8 ];
* isArrayOf('number', arr); // false
* ```
* @example
* ```javascript
* const arr = [ 2, 4, 8, 12 ];
* isArrayOf('number', arr); // true
* ```
* @since `[email protected]`
*/
export function isArrayOf<T extends keyof Types>(
type: T,
arr: unknown
): arr is Types[T][];
/**
* @enum Primitives in NodeJS
* @since `[email protected]`
*/
export enum Primitives {
BIGINT = 'bigint',
BOOLEAN = 'boolean',
FUNCTION = 'function',
NUMBER = 'number',
OBJECT = 'object',
STRING = 'string',
SYMBOL = 'symbol',
UNDEFINED = 'undefined',
}
/**
* Checks if the given string is
* the name of a primitive
* @param value Value to check for
* @returns Whether or not the given string is the name of a primitive
* @example
* ```javascript
* isPrimitive('bigint'); // true
* isPrimitive('number'); // true
* isPrimitive('Hello World!'); // false
* ```
* @since `[email protected]`
*/
export function isPrimitive(value: unknown): value is keyof Types;
/**
* Throws an error if the type of given value does
* not match the name of the given primitive
* @param value Value to check
* @param type Name of the primitive
* @param argumentName Optional, the argument name, displayed in the error message
* @example
* ```javascript
* function add(a, b) {
* validatePrimitive(a, 'number', 'a'); // Throws an error if a
* validatePrimitive(b, 'number', 'b'); // or b is not a number
*
* return a + b;
* }
* ```
* @since `[email protected]`
*/
export function validatePrimitive(
value: unknown,
type: keyof Types,
argumentName?: string
): void;
}
8 changes: 8 additions & 0 deletions utilities/generateRandomNumber.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
const { validatePrimitive } = require('../validation');

function generateRandomNumberE(min, max) {
validatePrimitive(min, 'number', 'min');
validatePrimitive(max, 'number', 'max');

return Math.floor(Math.random() * (max - min) + min);
}

function generateRandomNumberI(min, max) {
validatePrimitive(min, 'number', 'min');
validatePrimitive(max, 'number', 'max');

return Math.floor(Math.random() * (max - min + 1) + min);
}

Expand Down
6 changes: 6 additions & 0 deletions utilities/readJSONFile.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
const { readFileSync } = require('fs');
const { readFile } = require('fs/promises');

const { validatePrimitive } = require('../validation');

function readJSONFileSync(path) {
validatePrimitive(path, 'string', 'path');

const raw = readFileSync(path, 'utf8');
return JSON.parse(raw);
}

async function readJSONFile(path) {
validatePrimitive(path, 'string', 'path');

const raw = await readFile(path, 'utf8');
return JSON.parse(raw);
}
Expand Down
4 changes: 4 additions & 0 deletions utilities/wait.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const { validatePrimitive } = require('../validation');

module.exports = async function wait(ms) {
validatePrimitive(ms, 'number', 'ms');

return new Promise((resolve) => setTimeout(resolve, ms));
};
2 changes: 2 additions & 0 deletions validation/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module.exports = {
isArrayOf: require('./isArrayOf'),
...require('./primitives'),
validatePrimitive: require('./validatePrimitive'),
};
18 changes: 18 additions & 0 deletions validation/primitives.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Primitives = Object.freeze({
BIGINT: 'bigint',
BOOLEAN: 'boolean',
FUNCTION: 'function',
NUMBER: 'number',
OBJECT: 'object',
STRING: 'string',
SYMBOL: 'symbol',
UNDEFINED: 'undefined',
});

const primitives = Object.values(Primitives);

function isPrimitive(value) {
return primitives.includes(value);
}

module.exports = { Primitives, isPrimitive };
30 changes: 30 additions & 0 deletions validation/validatePrimitive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { isPrimitive } = require('./primitives');

module.exports = function validatePrimitive(value, type, argumentName) {
if (!isPrimitive(type))
throw new TypeError(
`The "type" argument must be a string representing a primitive. Received ${type}`
);

if (typeof value === type) return;

argumentName = argumentName
? `The "${argumentName}" argument`
: 'The argument';

let actualType = typeof value;
let primitive = true;
if (actualType === 'object')
try {
actualType = Object.getPrototypeOf(value).constructor.name;
primitive = false;
} catch (error) {}

const error = new TypeError(
`${argumentName} must be of type ${type}. Received ${
primitive ? 'a type of' : 'an instance of'
} ${actualType}`
);
error.code = 'ERR_INVALID_ARG_TYPE';
throw error;
};

0 comments on commit 36feb3b

Please sign in to comment.