Add validation utilities as a separate package
This commit is contained in:
parent
96d22f09d1
commit
8d1528de23
@ -1,6 +1,6 @@
|
|||||||
import { UnexpectedError } from "../error/unexpected-error.ts";
|
import { UnexpectedError } from "../error/unexpected-error.ts";
|
||||||
|
|
||||||
export function ensureValue<T>(value?: T): T {
|
export function ensure<T>(value?: T): T {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw new UnexpectedError("Value is nullish.");
|
throw new UnexpectedError("Value is nullish.");
|
||||||
}
|
}
|
||||||
@ -1,4 +1 @@
|
|||||||
export * from "./ensure-value.ts";
|
export * from "./ensure.ts";
|
||||||
export * from "./is-not-a-number.ts";
|
|
||||||
export * from "./is-nullish.ts";
|
|
||||||
export * from "./sanitize-string.ts";
|
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
import { describe, expect, test } from "@fabric/testing";
|
|
||||||
import { isNotANumber } from "./is-not-a-number.ts";
|
|
||||||
|
|
||||||
describe("Is not a number", () => {
|
|
||||||
test("Given a number it should return false", () => {
|
|
||||||
expect(isNotANumber(1)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given a string it should return true", () => {
|
|
||||||
expect(isNotANumber("a")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given a string number it should return false", () => {
|
|
||||||
expect(isNotANumber("5")).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given an empty string it should return true", () => {
|
|
||||||
expect(isNotANumber("")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given a boolean it should return true", () => {
|
|
||||||
expect(isNotANumber(true)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given an object it should return true", () => {
|
|
||||||
expect(isNotANumber({})).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given an array it should return true", () => {
|
|
||||||
expect(isNotANumber([])).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given a null it should return true", () => {
|
|
||||||
expect(isNotANumber(null)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Given an undefined it should return true", () => {
|
|
||||||
expect(isNotANumber(undefined)).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import { isNullish } from "./is-nullish.ts";
|
|
||||||
import { parseAndSanitizeString } from "./sanitize-string.ts";
|
|
||||||
|
|
||||||
export function isNotANumber(value: unknown): boolean {
|
|
||||||
if (isNullish(value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === "string") {
|
|
||||||
const sanitized = parseAndSanitizeString(value);
|
|
||||||
if (sanitized === "") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof value === "boolean" ||
|
|
||||||
typeof value === "object" ||
|
|
||||||
Array.isArray(value)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isNaN(Number(value));
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
export function isNullish(value: unknown): value is null | undefined {
|
|
||||||
return isNull(value) || isUndefined(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isUndefined(value: unknown): value is undefined {
|
|
||||||
return value === undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNull(value: unknown): value is null {
|
|
||||||
return value === null;
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import validator from "validator";
|
|
||||||
import { isUndefined } from "./is-nullish.ts";
|
|
||||||
|
|
||||||
const { stripLow, trim } = validator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses and sanitizes an unknown value into a string
|
|
||||||
* The string is trimmed and all low characters are removed
|
|
||||||
*/
|
|
||||||
export function parseAndSanitizeString(value: unknown): string | undefined {
|
|
||||||
if (isUndefined(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return stripLow(trim(String(value)));
|
|
||||||
}
|
|
||||||
9
packages/fabric/validations/deno.json
Normal file
9
packages/fabric/validations/deno.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "@fabric/validations",
|
||||||
|
"exports": {
|
||||||
|
".": "./index.ts"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"@fabric/core": "jsr:@fabric/core"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
packages/fabric/validations/index.ts
Normal file
3
packages/fabric/validations/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./nullish/index.ts";
|
||||||
|
export * from "./number/index.ts";
|
||||||
|
export * from "./string/index.ts";
|
||||||
3
packages/fabric/validations/nullish/index.ts
Normal file
3
packages/fabric/validations/nullish/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./is-null.ts";
|
||||||
|
export * from "./is-nullish.ts";
|
||||||
|
export * from "./is-undefined.ts";
|
||||||
3
packages/fabric/validations/nullish/is-null.ts
Normal file
3
packages/fabric/validations/nullish/is-null.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isNull(value: unknown): value is null {
|
||||||
|
return value === null;
|
||||||
|
}
|
||||||
6
packages/fabric/validations/nullish/is-nullish.ts
Normal file
6
packages/fabric/validations/nullish/is-nullish.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { isNull } from "./is-null.ts";
|
||||||
|
import { isUndefined } from "./is-undefined.ts";
|
||||||
|
|
||||||
|
export function isNullish(value: unknown): value is null | undefined {
|
||||||
|
return isNull(value) || isUndefined(value);
|
||||||
|
}
|
||||||
3
packages/fabric/validations/nullish/is-undefined.ts
Normal file
3
packages/fabric/validations/nullish/is-undefined.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isUndefined(value: unknown): value is undefined {
|
||||||
|
return value === undefined;
|
||||||
|
}
|
||||||
1
packages/fabric/validations/number/index.ts
Normal file
1
packages/fabric/validations/number/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./is-number.ts";
|
||||||
36
packages/fabric/validations/number/is-number.test.ts
Normal file
36
packages/fabric/validations/number/is-number.test.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
|
import { isNumber } from "./is-number.ts";
|
||||||
|
|
||||||
|
describe("Is a number", () => {
|
||||||
|
test("Given a number it should return true", () => {
|
||||||
|
expect(isNumber(1)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given a string it should return false", () => {
|
||||||
|
expect(isNumber("a")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given an empty string it should return false", () => {
|
||||||
|
expect(isNumber("")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given a boolean it should return false", () => {
|
||||||
|
expect(isNumber(false)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given an object it should return false", () => {
|
||||||
|
expect(isNumber({})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given an array it should return false", () => {
|
||||||
|
expect(isNumber([])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given a null it should return false", () => {
|
||||||
|
expect(isNumber(null)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given an undefined it should return false", () => {
|
||||||
|
expect(isNumber(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
9
packages/fabric/validations/number/is-number.ts
Normal file
9
packages/fabric/validations/number/is-number.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Checks if a value is a number even if it is a string that can be converted to a number
|
||||||
|
*/
|
||||||
|
export function isNumber(value: unknown): value is number {
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return !isNaN(value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
3
packages/fabric/validations/string/index.ts
Normal file
3
packages/fabric/validations/string/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./is-string.ts";
|
||||||
|
export * from "./is-uuid.ts";
|
||||||
|
export * from "./sanitize-string.ts";
|
||||||
3
packages/fabric/validations/string/is-string.ts
Normal file
3
packages/fabric/validations/string/is-string.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isString(value: unknown): value is string {
|
||||||
|
return typeof value === "string";
|
||||||
|
}
|
||||||
54
packages/fabric/validations/string/is-uuid.test.ts
Normal file
54
packages/fabric/validations/string/is-uuid.test.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
|
import { isUUID } from "./is-uuid.ts";
|
||||||
|
|
||||||
|
describe("isUUID", () => {
|
||||||
|
test("should return true for a valid UUID", () => {
|
||||||
|
const validUUID = "123e4567-e89b-12d3-a456-426614174000";
|
||||||
|
expect(isUUID(validUUID)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return true for a valid UUID with uppercase letters", () => {
|
||||||
|
const validUUID = "123E4567-E89B-12D3-A456-426614174000";
|
||||||
|
expect(isUUID(validUUID)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return true for a nil UUID", () => {
|
||||||
|
const nilUUID = "00000000-0000-0000-0000-000000000000";
|
||||||
|
expect(isUUID(nilUUID)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return true for a max UUID", () => {
|
||||||
|
const maxUUID = "ffffffff-ffff-ffff-ffff-ffffffffffff";
|
||||||
|
expect(isUUID(maxUUID)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for an invalid UUID", () => {
|
||||||
|
const invalidUUID = "123e4567-e89b-12d3-a456-42661417400";
|
||||||
|
expect(isUUID(invalidUUID)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for a string that is not a UUID", () => {
|
||||||
|
const notUUID = "not-a-uuid";
|
||||||
|
expect(isUUID(notUUID)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for a number", () => {
|
||||||
|
const number = 1234567890;
|
||||||
|
expect(isUUID(number)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for a boolean", () => {
|
||||||
|
const boolean = true;
|
||||||
|
expect(isUUID(boolean)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for null", () => {
|
||||||
|
const nullValue = null;
|
||||||
|
expect(isUUID(nullValue)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for undefined", () => {
|
||||||
|
const undefinedValue = undefined;
|
||||||
|
expect(isUUID(undefinedValue)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
10
packages/fabric/validations/string/is-uuid.ts
Normal file
10
packages/fabric/validations/string/is-uuid.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { UUID } from "@fabric/core";
|
||||||
|
import { isString } from "./is-string.ts";
|
||||||
|
|
||||||
|
// From https://github.com/uuidjs/uuid/blob/main/src/regex.ts
|
||||||
|
const uuidRegex =
|
||||||
|
/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
|
||||||
|
|
||||||
|
export function isUUID(value: unknown): value is UUID {
|
||||||
|
return isString(value) && uuidRegex.test(value);
|
||||||
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { describe, expect, test } from "@fabric/testing";
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
import { parseAndSanitizeString } from "./sanitize-string.ts";
|
import { parseAndSanitizeString } from "../string/sanitize-string.ts";
|
||||||
|
|
||||||
describe("Sanitize String", () => {
|
describe("Sanitize String", () => {
|
||||||
test("Given a string with low characters it should sanitize it", () => {
|
test("Given a string with low (control) characters it should sanitize it", () => {
|
||||||
const sanitized = parseAndSanitizeString("John\x00");
|
const sanitized = parseAndSanitizeString("John\x00");
|
||||||
|
|
||||||
expect(sanitized).toBe("John");
|
expect(sanitized).toBe("John");
|
||||||
@ -26,13 +26,13 @@ describe("Sanitize String", () => {
|
|||||||
expect(sanitized).toBe("true");
|
expect(sanitized).toBe("true");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Given a null value it should convert it to an empty string", () => {
|
test("Given a null value it should return null", () => {
|
||||||
const sanitized = parseAndSanitizeString(null);
|
const sanitized = parseAndSanitizeString(null);
|
||||||
|
|
||||||
expect(sanitized).toBe("null");
|
expect(sanitized).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Given an undefined value it should convert it to an empty string", () => {
|
test("Given an undefined value it should return undefined", () => {
|
||||||
const sanitized = parseAndSanitizeString(undefined);
|
const sanitized = parseAndSanitizeString(undefined);
|
||||||
|
|
||||||
expect(sanitized).toBe(undefined);
|
expect(sanitized).toBe(undefined);
|
||||||
17
packages/fabric/validations/string/sanitize-string.ts
Normal file
17
packages/fabric/validations/string/sanitize-string.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { isNullish } from "../nullish/is-nullish.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses and sanitizes an unknown value into a string
|
||||||
|
* The string is trimmed and all low characters are removed
|
||||||
|
*/
|
||||||
|
export function parseAndSanitizeString(
|
||||||
|
value: unknown,
|
||||||
|
): string | undefined {
|
||||||
|
if (isNullish(value)) return undefined;
|
||||||
|
return stripLow((String(value)).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// deno-lint-ignore no-control-regex
|
||||||
|
const lowCharsRegex = /[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/g;
|
||||||
|
|
||||||
|
const stripLow = (str: string) => str.replace(lowCharsRegex, "");
|
||||||
Loading…
Reference in New Issue
Block a user