schemaglobin
Advanced tools
Comparing version 4.0.7 to 4.1.0
@@ -6,2 +6,3 @@ export * from "./Schema"; | ||
export * from "./schemas/DateSchema"; | ||
export * from "./schemas/DistanceSchema"; | ||
export * from "./schemas/EmailSchema"; | ||
@@ -15,3 +16,7 @@ export * from "./schemas/KeySchema"; | ||
export * from "./schemas/UrlSchema"; | ||
export * from "./Invalid"; | ||
export * from "./helpers"; | ||
export * from "./helpers/date"; | ||
export * from "./helpers/distance"; | ||
export * from "./helpers/invalid"; | ||
export * from "./helpers/number"; | ||
export * from "./helpers/schema"; | ||
export * from "./helpers/string"; |
@@ -20,2 +20,3 @@ "use strict"; | ||
__exportStar(require("./schemas/DateSchema"), exports); | ||
__exportStar(require("./schemas/DistanceSchema"), exports); | ||
__exportStar(require("./schemas/EmailSchema"), exports); | ||
@@ -29,5 +30,8 @@ __exportStar(require("./schemas/KeySchema"), exports); | ||
__exportStar(require("./schemas/UrlSchema"), exports); | ||
// Export Invalid. | ||
__exportStar(require("./Invalid"), exports); | ||
// Export helpers. | ||
__exportStar(require("./helpers"), exports); | ||
__exportStar(require("./helpers/date"), exports); | ||
__exportStar(require("./helpers/distance"), exports); | ||
__exportStar(require("./helpers/invalid"), exports); | ||
__exportStar(require("./helpers/number"), exports); | ||
__exportStar(require("./helpers/schema"), exports); | ||
__exportStar(require("./helpers/string"), exports); |
@@ -1,2 +0,2 @@ | ||
import type { Invalid } from "./Invalid"; | ||
import type { Invalid } from "./helpers/invalid"; | ||
/** | ||
@@ -3,0 +3,0 @@ * SchemaOptions enforces types on the options bag that is passed into SchemaClass to create Schema. |
import { Schema, SchemaOptions, SchemaType, ValidateFlags } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to an array (if possible). */ | ||
@@ -4,0 +4,0 @@ export declare function coerceArray(value: unknown): unknown[] | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.array = exports.ArraySchema = exports.coerceArray = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to an array (if possible). */ | ||
@@ -11,3 +11,3 @@ function coerceArray(value) { | ||
return value; | ||
return new Invalid_1.Invalid("Must be array"); | ||
return new invalid_1.Invalid("Must be array"); | ||
} | ||
@@ -59,3 +59,3 @@ exports.coerceArray = coerceArray; | ||
const unsafeArray = coerceArray(unsafeValue); | ||
if (unsafeArray instanceof Invalid_1.Invalid) | ||
if (unsafeArray instanceof invalid_1.Invalid) | ||
return unsafeArray; | ||
@@ -66,3 +66,3 @@ // Has contents? | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return empty array. | ||
@@ -74,6 +74,6 @@ // We know this assertion is okay because we know the array is empty. | ||
if (typeof this.min === "number" && unsafeArray.length < this.min) | ||
return new Invalid_1.Invalid(`Minimum ${this.min} items`); | ||
return new invalid_1.Invalid(`Minimum ${this.min} items`); | ||
// Array longer than max length returns Invalid. | ||
if (typeof this.max === "number" && unsafeArray.length > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max} items`); | ||
return new invalid_1.Invalid(`Maximum ${this.max} items`); | ||
// Check each item against `this.items` | ||
@@ -88,3 +88,3 @@ let changed = false; | ||
const value = items.validate(current, flags); | ||
if (value instanceof Invalid_1.Invalid) { | ||
if (value instanceof invalid_1.Invalid) { | ||
invalid = true; | ||
@@ -101,3 +101,3 @@ invalids[i.toString()] = value.message; | ||
if (invalid) | ||
return new Invalid_1.Invalid("Invalid items", invalids); | ||
return new invalid_1.Invalid("Invalid items", invalids); | ||
// Return the new array if it changed. | ||
@@ -104,0 +104,0 @@ // We know this assertion is okay because if it wasn't, we would've returned Invalid. |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { FalseIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a boolean. */ | ||
@@ -5,0 +5,0 @@ export declare function coerceBoolean(value: unknown): boolean; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.boolean = exports.BooleanSchema = exports.coerceBoolean = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to a boolean. */ | ||
@@ -26,3 +26,3 @@ function coerceBoolean(value) { | ||
if (this.required && !value) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return boolean. | ||
@@ -29,0 +29,0 @@ return value; |
import type { NullIfOptional } from "../types"; | ||
import type { Schema, SchemaOptions } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a Color (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coerceColor(value: unknown): string | null | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.color = exports.ColorSchema = exports.coerceColor = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
const R_COLOR = /^#[0-9A-F]{6}$/; | ||
@@ -20,3 +20,3 @@ /** | ||
return cleanColor(value) || null; | ||
return new Invalid_1.Invalid("Must be string or null"); | ||
return new invalid_1.Invalid("Must be string or null"); | ||
} | ||
@@ -45,3 +45,3 @@ exports.coerceColor = coerceColor; | ||
const value = coerceColor(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -52,3 +52,3 @@ // Null means 'no color' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return null. | ||
@@ -60,3 +60,3 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
if (!R_COLOR.test(value)) | ||
return new Invalid_1.Invalid("Invalid color (must be a hexadecimal color)"); | ||
return new invalid_1.Invalid("Invalid color (must be a hexadecimal color)"); | ||
// Return the normalised Color. | ||
@@ -63,0 +63,0 @@ return value; |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
export declare type DateSchemaType<R extends boolean = false> = string | NullIfOptional<R>; | ||
/** Convert an unknown value to a date string (or return Invalid) */ | ||
export declare function coerceDate<T extends unknown>(value: T): Date | null | Invalid; | ||
/** Convert a `Date()` instance to a YMD string like "2015-09-12" */ | ||
export declare function dateToString(value?: Date): string; | ||
export declare type DateInputs = Date | string | number | null | (() => DateInputs); | ||
@@ -10,0 +8,0 @@ export interface DateOptions<R extends boolean = false> extends SchemaOptions { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.date = exports.DateSchema = exports.dateToString = exports.coerceDate = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
exports.date = exports.DateSchema = exports.coerceDate = void 0; | ||
const invalid_1 = require("../helpers/invalid"); | ||
const date_1 = require("../helpers/date"); | ||
/** Convert an unknown value to a date string (or return Invalid) */ | ||
@@ -19,10 +20,5 @@ function coerceDate(value) { | ||
return new Date(value); // Strings are converted to dates (except empty string which should be ""). | ||
return new Invalid_1.Invalid("Must be date, number, string, or null"); | ||
return new invalid_1.Invalid("Must be date, number, string, or null"); | ||
} | ||
exports.coerceDate = coerceDate; | ||
/** Convert a `Date()` instance to a YMD string like "2015-09-12" */ | ||
function dateToString(value = new Date()) { | ||
return value.toISOString().substr(0, 10); | ||
} | ||
exports.dateToString = dateToString; | ||
/** | ||
@@ -46,3 +42,3 @@ * Schema that defines a valid date in string YMD format, e.g. `2019-10-04` | ||
const value = coerceDate(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -53,3 +49,3 @@ // Explicit null means 'no date'. | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return null. | ||
@@ -61,12 +57,12 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
if (Number.isNaN(value.getTime())) | ||
return new Invalid_1.Invalid("Invalid date"); | ||
return new invalid_1.Invalid("Invalid date"); | ||
// Enforce min/max. | ||
const minDate = coerceDate(this.min); | ||
if (minDate instanceof Date && value.getTime() < minDate.getTime()) | ||
return new Invalid_1.Invalid(`Minimum ${minDate.toLocaleDateString()}`); | ||
return new invalid_1.Invalid(`Minimum ${minDate.toLocaleDateString()}`); | ||
const maxDate = coerceDate(this.max); | ||
if (maxDate instanceof Date && value.getTime() > maxDate.getTime()) | ||
return new Invalid_1.Invalid(`Maximum ${maxDate.toLocaleDateString()}`); | ||
return new invalid_1.Invalid(`Maximum ${maxDate.toLocaleDateString()}`); | ||
// Return the valid date string. | ||
return dateToString(value); | ||
return date_1.getYmd(value); | ||
} | ||
@@ -73,0 +69,0 @@ } |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to an email (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coerceEmail(value: unknown): string | null | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.email = exports.EmailSchema = exports.coerceEmail = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to an email (if possible). */ | ||
@@ -11,3 +11,3 @@ function coerceEmail(value) { | ||
return value.trim().toLowerCase() || null; | ||
return new Invalid_1.Invalid("Must be string or null"); | ||
return new invalid_1.Invalid("Must be string or null"); | ||
} | ||
@@ -48,3 +48,3 @@ exports.coerceEmail = coerceEmail; | ||
const value = coerceEmail(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -55,3 +55,3 @@ // Null means 'no email' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return null. | ||
@@ -63,6 +63,6 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
if (value.length > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
return new invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
// Check format. | ||
if (!R_EMAIL.test(value)) | ||
return new Invalid_1.Invalid("Invalid email format"); | ||
return new invalid_1.Invalid("Invalid email format"); | ||
// Return email. | ||
@@ -69,0 +69,0 @@ return value; |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a key (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coerceKey(value: unknown): string | null | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.key = exports.KeySchema = exports.coerceKey = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to a key (if possible). */ | ||
@@ -13,3 +13,3 @@ function coerceKey(value) { | ||
return value.trim() || null; | ||
return new Invalid_1.Invalid("Must be string or null"); | ||
return new invalid_1.Invalid("Must be string or null"); | ||
} | ||
@@ -38,3 +38,3 @@ exports.coerceKey = coerceKey; | ||
const value = coerceKey(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -45,3 +45,3 @@ // Null means 'no key' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return. | ||
@@ -53,3 +53,3 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
if (!this.match.test(value)) | ||
return new Invalid_1.Invalid("Invalid key format"); | ||
return new invalid_1.Invalid("Invalid key format"); | ||
// Return key. | ||
@@ -56,0 +56,0 @@ return value; |
import type { Schema, SchemaOptions, SchemaType, ValidateFlags } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a map (if possible). */ | ||
@@ -4,0 +4,0 @@ export declare function coerceMap(value: unknown): Record<string, unknown> | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.map = exports.MapSchema = exports.coerceMap = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to a map (if possible). */ | ||
@@ -11,3 +11,3 @@ function coerceMap(value) { | ||
return value; | ||
return new Invalid_1.Invalid("Must be object"); | ||
return new invalid_1.Invalid("Must be object"); | ||
} | ||
@@ -39,3 +39,3 @@ exports.coerceMap = coerceMap; | ||
const unsafeObject = coerceMap(unsafeValue); | ||
if (unsafeObject instanceof Invalid_1.Invalid) | ||
if (unsafeObject instanceof invalid_1.Invalid) | ||
return unsafeObject; | ||
@@ -48,3 +48,3 @@ // Get number of properties. | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return empty object. | ||
@@ -56,5 +56,5 @@ // We know this type assertion is sound because we know value is empty. | ||
if (typeof this.min === "number" && length < this.min) | ||
return new Invalid_1.Invalid(`Minimum ${this.min} items`); | ||
return new invalid_1.Invalid(`Minimum ${this.min} items`); | ||
if (typeof this.max === "number" && length > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max} items`); | ||
return new invalid_1.Invalid(`Maximum ${this.max} items`); | ||
// Check value against against `this.items` | ||
@@ -68,3 +68,3 @@ let changed = false; | ||
const value = this.items.validate(current, flags); | ||
if (value instanceof Invalid_1.Invalid) { | ||
if (value instanceof invalid_1.Invalid) { | ||
invalid = true; | ||
@@ -81,3 +81,3 @@ invalids[key] = value.message; | ||
if (invalid) | ||
return new Invalid_1.Invalid("Invalid format", invalids); | ||
return new invalid_1.Invalid("Invalid format", invalids); | ||
// Return immuatably (return output if changes were made, or exact input otherwise). | ||
@@ -84,0 +84,0 @@ return (changed ? output : unsafeObject); |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
/** | ||
* Convert a stringy number into a number. | ||
* | ||
* @param str The string to convert. | ||
* @return The number, null (valid, meaning no number). | ||
*/ | ||
export declare function stringToNumber(str: string): number | null; | ||
/** | ||
* Round numbers to a given step. | ||
* | ||
* @param num The number to round. | ||
* @param step=0 The rounding to round to, e.g. `2` or `0.1` | ||
* @returns The number rounded to the step. | ||
*/ | ||
export declare function roundToStep(num: number, step: number): number; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a number (if possible). */ | ||
@@ -20,0 +5,0 @@ export declare function coerceNumber(value: unknown): number | null | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.number = exports.NumberSchema = exports.coerceNumber = exports.roundToStep = exports.stringToNumber = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
// RegExps. | ||
const R_NUMERIC = /[0-9]/; | ||
const R_MINUS = /-/g; | ||
/** | ||
* Convert a stringy number into a number. | ||
* | ||
* @param str The string to convert. | ||
* @return The number, null (valid, meaning no number). | ||
*/ | ||
function stringToNumber(str) { | ||
// Undefined if not a string. | ||
if (typeof str !== "string") | ||
return NaN; | ||
// Null if empty string. | ||
if (str.length === 0) | ||
return null; | ||
// Undefined if string has no numeric characters. | ||
const firstNumeric = str.search(R_NUMERIC); | ||
if (firstNumeric === -1) | ||
return null; | ||
// Negative if a minus sign appears before any numerics. | ||
// Double negatives are resolved (i.e. three "-" minus signs → one minus sign) | ||
const prefix = str.substr(0, firstNumeric); // Get starting portion of string without numbers. | ||
const signs = prefix ? prefix.split(R_MINUS).length - 1 : 0; // Number of minus signs. | ||
const negative = !!(signs % 2); // Negative (odd minus signs) or positive (even minus signs). | ||
// Get all the non-numeric and non-dot characters, then explode by dot. | ||
const nums = str.replace(/[^0-9.]+/g, "").split(/\.+/); | ||
// Combine back together the negative sign, main number, and decimal numbers | ||
const clean = (negative ? "-" : "") + nums[0] + (nums.length > 1 ? `.${nums.slice(1).join("")}` : ""); | ||
// Convert to number. | ||
const num = Number.parseFloat(clean); | ||
// Might be NaN (because e.g. 'abc' was passed'). | ||
return !Number.isNaN(num) ? num : null; | ||
} | ||
exports.stringToNumber = stringToNumber; | ||
/** | ||
* Round numbers to a given step. | ||
* | ||
* @param num The number to round. | ||
* @param step=0 The rounding to round to, e.g. `2` or `0.1` | ||
* @returns The number rounded to the step. | ||
*/ | ||
function roundToStep(num, step) { | ||
if (step < 0.00001) | ||
throw new Error("roundToStep() does not work accurtely with steps smaller than 0.00001"); | ||
return Math.round(num / step) * step; | ||
} | ||
exports.roundToStep = roundToStep; | ||
exports.number = exports.NumberSchema = exports.coerceNumber = void 0; | ||
const invalid_1 = require("../helpers/invalid"); | ||
const number_1 = require("../helpers/number"); | ||
/** Coerce an unknown value to a number (if possible). */ | ||
@@ -60,7 +13,8 @@ function coerceNumber(value) { | ||
if (typeof value === "string") { | ||
const numValue = stringToNumber(value); | ||
const numValue = number_1.stringToNumber(value); | ||
if (typeof numValue === "number") | ||
return numValue; | ||
return new invalid_1.Invalid("Must be numeric"); | ||
} | ||
return new Invalid_1.Invalid("Must be number or null"); | ||
return new invalid_1.Invalid("Must be number or null"); | ||
} | ||
@@ -87,3 +41,3 @@ exports.coerceNumber = coerceNumber; | ||
let value = coerceNumber(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -94,3 +48,3 @@ // Null means 'no number' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return null. | ||
@@ -102,11 +56,11 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
if (!Number.isFinite(value)) | ||
return new Invalid_1.Invalid("Must be finite number"); | ||
return new invalid_1.Invalid("Must be finite number"); | ||
// Check min and max. | ||
if (typeof this.max === "number" && value > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max}`); | ||
return new invalid_1.Invalid(`Maximum ${this.max}`); | ||
if (typeof this.min === "number" && value < this.min) | ||
return new Invalid_1.Invalid(`Minimum ${this.min}`); | ||
return new invalid_1.Invalid(`Minimum ${this.min}`); | ||
// Round to step. | ||
if (typeof this.step === "number") | ||
value = roundToStep(value, this.step); | ||
value = number_1.roundToStep(value, this.step); | ||
// Check options format. | ||
@@ -116,7 +70,7 @@ if (this.options) { | ||
if (!this.options.includes(value)) | ||
return new Invalid_1.Invalid("Unknown value"); | ||
return new invalid_1.Invalid("Unknown value"); | ||
} | ||
else { | ||
if (!(value.toString() in this.options)) | ||
return new Invalid_1.Invalid("Unknown value"); | ||
return new invalid_1.Invalid("Unknown value"); | ||
} | ||
@@ -123,0 +77,0 @@ } |
import type { Schema, Schemas, SchemasType, SchemaOptions, ValidateFlags } from "../Schema"; | ||
import type { NullIfOptional, DeepPartial, UnknownObject } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to an object (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coerceObject(value: unknown): UnknownObject | null | Invalid; |
@@ -5,3 +5,3 @@ "use strict"; | ||
exports.object = exports.ObjectSchema = exports.coerceObject = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to an object (if possible). */ | ||
@@ -13,3 +13,3 @@ function coerceObject(value) { | ||
return value; | ||
return new Invalid_1.Invalid("Must be object"); | ||
return new invalid_1.Invalid("Must be object"); | ||
} | ||
@@ -35,3 +35,3 @@ exports.coerceObject = coerceObject; | ||
const unsafeObj = coerceObject(unsafeValue); | ||
if (unsafeObj instanceof Invalid_1.Invalid) | ||
if (unsafeObj instanceof invalid_1.Invalid) | ||
return unsafeObj; | ||
@@ -42,3 +42,3 @@ // Null means 'no object' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return. | ||
@@ -59,3 +59,3 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
const safeProp = schema.validate(unsafeProp, flags); | ||
if (safeProp instanceof Invalid_1.Invalid) { | ||
if (safeProp instanceof invalid_1.Invalid) { | ||
invalid = true; | ||
@@ -81,3 +81,3 @@ invalids[key] = safeProp.message; | ||
const safeProp = schema.validate(unsafeProp, flags); | ||
if (safeProp instanceof Invalid_1.Invalid) { | ||
if (safeProp instanceof invalid_1.Invalid) { | ||
invalid = true; | ||
@@ -98,3 +98,3 @@ invalids[key] = safeProp.message; | ||
if (invalid) | ||
return new Invalid_1.Invalid("Invalid format", invalids); | ||
return new invalid_1.Invalid("Invalid format", invalids); | ||
// Return immuatably (return output if changes were made, or exact input otherwise). | ||
@@ -101,0 +101,0 @@ return (changed ? safeObj : unsafeObj); |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value into a phone number (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coercePhone(value: unknown): string | null | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.phone = exports.PhoneSchema = exports.coercePhone = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** | ||
@@ -23,3 +23,3 @@ * Clean a phone number string by removing characters that aren't digits. | ||
return cleanPhone(value) || null; // Return the clean phone number (also convert empty string to null). | ||
return new Invalid_1.Invalid("Must be string or null"); | ||
return new invalid_1.Invalid("Must be string or null"); | ||
} | ||
@@ -50,3 +50,3 @@ exports.coercePhone = coercePhone; | ||
const value = coercePhone(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -57,3 +57,3 @@ // Null means 'no phone' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return null. | ||
@@ -65,6 +65,6 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
if (value.length > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
return new invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
// Check format. | ||
if (!R_PHONE.test(value)) | ||
return new Invalid_1.Invalid("Invalid phone number (must be an international number starting with `+` plus)"); | ||
return new invalid_1.Invalid("Invalid phone number (must be an international number starting with `+` plus)"); | ||
// Return phone. | ||
@@ -71,0 +71,0 @@ return value; |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { EmptyIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a string (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coerceString(value: unknown): string | Invalid; |
@@ -5,3 +5,3 @@ "use strict"; | ||
exports.string = exports.StringSchema = exports.coerceString = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to a string (if possible). */ | ||
@@ -15,3 +15,3 @@ function coerceString(value) { | ||
return ""; // Convert falsy to empty string. | ||
return new Invalid_1.Invalid("Must be string"); | ||
return new invalid_1.Invalid("Must be string"); | ||
} | ||
@@ -58,3 +58,3 @@ exports.coerceString = coerceString; | ||
const uncleanValue = coerceString(unsafeValue); | ||
if (uncleanValue instanceof Invalid_1.Invalid) | ||
if (uncleanValue instanceof invalid_1.Invalid) | ||
return uncleanValue; | ||
@@ -67,3 +67,3 @@ // Strip control characters. | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return. | ||
@@ -74,6 +74,6 @@ return ""; | ||
if (typeof this.max === "number" && value.length > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
return new invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
// Check min. | ||
if (typeof this.min === "number" && value.length < this.min) | ||
return new Invalid_1.Invalid(`Minimum ${this.min} characters`); | ||
return new invalid_1.Invalid(`Minimum ${this.min} characters`); | ||
// Check enum format. | ||
@@ -83,7 +83,7 @@ if (this.options) { | ||
if (!this.options.includes(value)) | ||
return new Invalid_1.Invalid("Unknown value"); | ||
return new invalid_1.Invalid("Unknown value"); | ||
} | ||
else { | ||
if (!(value in this.options)) | ||
return new Invalid_1.Invalid("Unknown value"); | ||
return new invalid_1.Invalid("Unknown value"); | ||
} | ||
@@ -93,3 +93,3 @@ } | ||
if (this.match && !this.match.test(value)) | ||
return new Invalid_1.Invalid("Invalid format"); | ||
return new invalid_1.Invalid("Invalid format"); | ||
// Return string. | ||
@@ -96,0 +96,0 @@ // This type assertion is okay because the checks above make it so. |
import type { NullIfOptional } from "../types"; | ||
import type { Schema, SchemaOptions } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
/** Coerce an unknown value to a URL (if possible). */ | ||
@@ -5,0 +5,0 @@ export declare function coerceUrl(value: unknown): string | null | Invalid; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.url = exports.UrlSchema = exports.coerceUrl = void 0; | ||
const Invalid_1 = require("../Invalid"); | ||
const invalid_1 = require("../helpers/invalid"); | ||
/** Coerce an unknown value to a URL (if possible). */ | ||
@@ -11,3 +11,3 @@ function coerceUrl(value) { | ||
return value.trim() || null; | ||
return new Invalid_1.Invalid("Must be string or null"); | ||
return new invalid_1.Invalid("Must be string or null"); | ||
} | ||
@@ -39,3 +39,3 @@ exports.coerceUrl = coerceUrl; | ||
const value = coerceUrl(unsafeValue); | ||
if (value instanceof Invalid_1.Invalid) | ||
if (value instanceof invalid_1.Invalid) | ||
return value; | ||
@@ -46,3 +46,3 @@ // Null means 'no URL' | ||
if (this.required) | ||
return new Invalid_1.Invalid("Required"); | ||
return new invalid_1.Invalid("Required"); | ||
// Return null. | ||
@@ -60,12 +60,12 @@ // We know this type assertion is sound because `null` can never be returned if `this.required == true`. | ||
// Definitely not valid. | ||
return new Invalid_1.Invalid("Invalid URL format"); | ||
return new invalid_1.Invalid("Invalid URL format"); | ||
} | ||
// Check length again. | ||
if (u.href.length > this.max) | ||
return new Invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
return new invalid_1.Invalid(`Maximum ${this.max} characters`); | ||
// Check scheme and domain exist in whitelists. | ||
if (!this.schemes.includes(u.protocol)) | ||
return new Invalid_1.Invalid(`Scheme "${u.protocol}" is not allowed`); | ||
return new invalid_1.Invalid(`Scheme "${u.protocol}" is not allowed`); | ||
if (this.hosts && !this.hosts.includes(u.host)) | ||
return new Invalid_1.Invalid(`Domain "${u.host}" is not allowed`); | ||
return new invalid_1.Invalid(`Domain "${u.host}" is not allowed`); | ||
// Check domain. | ||
@@ -75,3 +75,3 @@ if (u.host.length) { | ||
if (u.host.length > 253) | ||
return new Invalid_1.Invalid(`Invalid URL format`); | ||
return new invalid_1.Invalid(`Invalid URL format`); | ||
// Each host segment is no more than 63 characters. | ||
@@ -81,3 +81,3 @@ const bits = u.host.split("."); | ||
if (bits[i].length > 63) | ||
return new Invalid_1.Invalid(`Invalid URL format`); | ||
return new invalid_1.Invalid(`Invalid URL format`); | ||
} | ||
@@ -84,0 +84,0 @@ // Return the normalised URL. |
{ | ||
"name": "schemaglobin", | ||
"description": "Validate user-entered data.", | ||
"version": "4.0.7", | ||
"version": "4.1.0", | ||
"repository": "https://github.com/dhoulb/schemaglobin", | ||
@@ -10,2 +10,3 @@ "author": "Dave Houlbrooke <dave@shax.com>", | ||
"main": "dist/index.js", | ||
"exports": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
@@ -12,0 +13,0 @@ "engineStrict": true, |
216
README.md
@@ -152,3 +152,3 @@ # Schemaglobin: Validate unknown user input against schemas | ||
```ts | ||
import { boolean, string, number, date, email, phone, url, key, array, object, map } from "schemaglobin"; | ||
import { boolean, string, number, date, distance, email, phone, url, key, array, object, map } from "schemaglobin"; | ||
@@ -161,2 +161,3 @@ // Create schemas. | ||
const dateSchema = date({ required: true, ...etc }); | ||
const distanceSchema = distance({ required: true, unit: "foot", ...etc }); | ||
const emailSchema = email({ required: true, ...etc }); | ||
@@ -166,5 +167,5 @@ const phoneSchema = phone({ required: true, ...etc }); | ||
const keySchema = key({ required: true, ...etc }); | ||
const arraySchema = array({ required: true, ...etc }); | ||
const objectSchema = object({ required: true, ...etc }); | ||
const mapSchema = object({ required: true, ...etc }); | ||
const arraySchema = array({ required: true, items: etc, ...etc }); | ||
const objectSchema = object({ required: true, props: etc, ...etc }); | ||
const mapSchema = object({ required: true, items: etc, ...etc }); | ||
@@ -174,5 +175,6 @@ // Successful validation. | ||
stringSchema.validate("abc"); // Returns "abc" | ||
numberSchema.validate(12345); // Returns "123" | ||
numberSchema.validate(12345); // Returns 12345 | ||
colorSchema.validate("#00CCFF"); // Returns "#00CCFF" | ||
dateSchema.validate("1995"); // Returns "1995-01-01" | ||
distanceSchema.validate("100 yd"); // Returns 300 (converted to feet). | ||
emailSchema.validate("me@x.com"); // Returns "me@x.com" | ||
@@ -190,2 +192,3 @@ phoneSchema.validate("+1234567890"); // Returns "+1234567890" | ||
dateSchema.validate("aaaaaaa"); // Returns Invalid("Invalid date") | ||
distanceSchema.validate("aaaaaaa"); // Returns Invalid("Invalid distance") | ||
colorSchema.validate(true); // Returns Invalid("Must be string or null") | ||
@@ -326,2 +329,19 @@ emailSchema.validate("111111"); // Returns Invalid("Invalid email format") | ||
### `array()` | ||
The `array()` creator function creates a `ArraySchema` instance that can validate arrays and their contents: | ||
- Arrays are valid, e.g. `[1,2,3]` | ||
- Contents of the array can be validated with `options.items` | ||
- Falsy values are converted to `[]` empty array. | ||
- `[]` empty array is an invalid value if `options.required` is truthy. | ||
`array()` also allows the following options: | ||
- `options.value: [] = []` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then empty arrays will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number of items allowed. | ||
- `options.max: number = null` - The maximum number of items allowed. | ||
- `options.items: Schema` (required) - Schema that will be used to validate the contents of the array. | ||
### `boolean()` | ||
@@ -342,46 +362,15 @@ | ||
### `string()` | ||
### `color()` | ||
The `string()` creator function creates a `StringSchema` instance: | ||
The `color()` creator function creates a `ColorSchema` instance that can validate hexadecimal color strings: | ||
- Strings are valid values. | ||
- Strings in hex color format are valid, e.g. `#00CCFF` | ||
- Whitespace is trimmed automatically. | ||
- Control characters are stripped automatically. | ||
- Newlines (and tabs) are stripped unless the `multiline` option is `true` | ||
- Numbers are converted to string automatically. | ||
- Falsy values are converted to `""` empty string | ||
- Default value is `""` empty string | ||
- `""` empty string is an invalid value if `options.required` is truthy. | ||
`string()` also allows the following options: | ||
- `options.value: string = ""` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then empty strings will return `Invalid("Required")` | ||
- `options.min: number = 0` - The minimum number of characters allowed. | ||
- `options.max: number = null` - The maximum number of characters allowed. | ||
- `options.options?: string[] | { string: string }` - Explicit list of allowed values as either: | ||
1. An array of strings where each string is an allowed value. | ||
2. An object where each string key is an allowed value, and the corresponding value is a user-facing title for the option. | ||
- `options.match: RegExp = null` - A regular expression that the string must match. | ||
- `options.multiline: boolean = false` - Whether the string allows newlines or not | ||
### `number()` | ||
The `number()` creator function creates a `NumberSchema` instance that can validate numbers: | ||
- Numbers are valid values. | ||
- `0` zero is a valid value. | ||
- Falsy values are converted to `null` | ||
- `null` is an invalid value if `options.required` is truthy. | ||
`number()` also allows the following options: | ||
`color()` also allows the following options: | ||
- `options.value: number | null = null` - The default value which will be used if the value is `undefined` | ||
- `options.value: Date = null` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then null values will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number allowed. | ||
- `options.max: number = null` - The maximum number allowed. | ||
- `options.options: number[] | { number: string } = null` - Explicit list of allowed values as either: | ||
1. An array of numbers where each number is an allowed value. | ||
2. An object where each number key is an allowed value and the corresponding value can be a user-facing title for the option. | ||
- `options.step: number = null` - The step size for the the number (the value will be rounded to the closest step). | ||
@@ -407,15 +396,21 @@ ### `date()` | ||
### `color()` | ||
### `distance()` | ||
The `color()` creator function creates a `ColorSchema` instance that can validate hexadecimal color strings: | ||
The `distance()` creator function creates a `DistanceSchema` instance that can validate distance numbers: | ||
- Strings in hex color format are valid, e.g. `#00CCFF` | ||
- Whitespace is trimmed automatically. | ||
- Numbers are valid values (and are assumed to be the base unit). | ||
- Numeric strings are valid values and are converted to numbers. | ||
- Numeric strings with unit suffixes (e.g. `10km` or `99 inches`) are valid values and are converted to a number and converted to the base unit. | ||
- `0` zero is a valid value. | ||
- Falsy values are converted to `null` | ||
- `null` is an invalid value if `options.required` is truthy. | ||
`color()` also allows the following options: | ||
`distance()` also allows the following options: | ||
- `options.value: Date = null` - The default value which will be used if the value is `undefined` | ||
- `options.value: number | null = null` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then null values will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number allowed. | ||
- `options.max: number = null` - The maximum number allowed. | ||
- `options.step: number = null` - The step size for the the number (the value will be rounded to the closest step). | ||
- `options.unit: DistanceUnit = "meter"` - The base unit for this schema. | ||
@@ -437,49 +432,70 @@ ### `email()` | ||
### `url()` | ||
### `key()` | ||
The `url()` creator function creates a `UrlSchema` instance that can validate URLs: | ||
The `key()` creator function creates a `KeySchema` instance that can validate database key strings: | ||
- Strings that are valid email addresses are valid, e.g. `https://x.com` or `data:anything` | ||
- Strings that are valid database keys are valid, e.g. `abc` or `AAAA1234` | ||
- By default key strings can only contain `a-zA-Z0-9` or `-` hyphen. | ||
- Whitespace is trimmed automatically. | ||
- Falsy values are converted to `null` | ||
- `null` is an invalid value if `options.required` is truthy | ||
- `null` is an invalid value if `options.required` is truthy. | ||
`url()` also allows the following options: | ||
`key()` also allows the following options: | ||
- `options.value: string = null` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then null values will return `Invalid("Required")` | ||
- `options.schemes: string[] = ["http:", "https:"]` - Whitelist of allowed URL schemes. | ||
- `options.hosts: string[] = null` - List of allowed hostnames, e.g. `["google.com"]` | ||
- `options.max: number = 512` - Maximum length of a URL. | ||
- `options.match: RegExp = /[a-zA-Z0-9-]{1,24}/` - Format the database key must match. | ||
### `phone()` | ||
### `map()` | ||
The `phone()` creator function creates a `PhoneSchema` instance that can validate URLs: | ||
The `map()` creator function creates a `MapSchema` instance that can validate an object containing a list of key: value entries: | ||
- Strings that are valid phone numbers are valid, e.g. `+441234567890` | ||
- Whitespace is trimmed automatically. | ||
- Non-digit characters are stripped automatically. | ||
- Objects are valid, e.g. `{ a: 1, b: 2, c: 3 }` | ||
- Contents of the object can be validated with `options.props` | ||
- Falsy values are converted to `{}` empty object. | ||
- `{}` empty object is an invalid value if `options.required` is truthy. | ||
`map()` also allows the following options: | ||
- `options.value: {} = {}` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then empty objects will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number of items allowed. | ||
- `options.max: number = null` - The maximum number of items allowed. | ||
- `options.items: Schema` (required) - Schema that will be used to validate all properties in the object. | ||
### `number()` | ||
The `number()` creator function creates a `NumberSchema` instance that can validate numbers: | ||
- Numbers are valid values. | ||
- Strings that can be converted to numbers are valid values. | ||
- `0` zero is a valid value. | ||
- Falsy values are converted to `null` | ||
- `null` is an invalid value if `options.required` is truthy. | ||
`phone()` also allows the following options: | ||
`number()` also allows the following options: | ||
- `options.value: string = null` - The default value which will be used if the value is `undefined` | ||
- `options.value: number | null = null` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then null values will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number allowed. | ||
- `options.max: number = null` - The maximum number allowed. | ||
- `options.options: number[] | { number: string } = null` - Explicit list of allowed values as either: | ||
1. An array of numbers where each number is an allowed value. | ||
2. An object where each number key is an allowed value and the corresponding value can be a user-facing title for the option. | ||
- `options.step: number = null` - The step size for the the number (the value will be rounded to the closest step). | ||
### `key()` | ||
### `phone()` | ||
The `key()` creator function creates a `KeySchema` instance that can validate database key strings: | ||
The `phone()` creator function creates a `PhoneSchema` instance that can validate URLs: | ||
- Strings that are valid database keys are valid, e.g. `abc` or `AAAA1234` | ||
- By default key strings can only contain `a-zA-Z0-9` or `-` hyphen. | ||
- Strings that are valid phone numbers are valid, e.g. `+441234567890` | ||
- Whitespace is trimmed automatically. | ||
- Non-digit characters are stripped automatically. | ||
- Falsy values are converted to `null` | ||
- `null` is an invalid value if `options.required` is truthy. | ||
`key()` also allows the following options: | ||
`phone()` also allows the following options: | ||
- `options.value: string = null` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then null values will return `Invalid("Required")` | ||
- `options.match: RegExp = /[a-zA-Z0-9-]{1,24}/` - Format the database key must match. | ||
@@ -506,35 +522,43 @@ ### `object()` | ||
### `array()` | ||
### `string()` | ||
The `array()` creator function creates a `ArraySchema` instance that can validate arrays and their contents: | ||
The `string()` creator function creates a `StringSchema` instance: | ||
- Arrays are valid, e.g. `[1,2,3]` | ||
- Contents of the array can be validated with `options.items` | ||
- Falsy values are converted to `[]` empty array. | ||
- `[]` empty array is an invalid value if `options.required` is truthy. | ||
- Strings are valid values. | ||
- Whitespace is trimmed automatically. | ||
- Control characters are stripped automatically. | ||
- Newlines (and tabs) are stripped unless the `multiline` option is `true` | ||
- Numbers are converted to string automatically. | ||
- Falsy values are converted to `""` empty string | ||
- Default value is `""` empty string | ||
- `""` empty string is an invalid value if `options.required` is truthy. | ||
`array()` also allows the following options: | ||
`string()` also allows the following options: | ||
- `options.value: [] = []` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then empty arrays will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number of items allowed. | ||
- `options.max: number = null` - The maximum number of items allowed. | ||
- `options.items: Schema` (required) - Schema that will be used to validate the contents of the array. | ||
- `options.value: string = ""` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then empty strings will return `Invalid("Required")` | ||
- `options.min: number = 0` - The minimum number of characters allowed. | ||
- `options.max: number = null` - The maximum number of characters allowed. | ||
- `options.options?: string[] | { string: string }` - Explicit list of allowed values as either: | ||
1. An array of strings where each string is an allowed value. | ||
2. An object where each string key is an allowed value, and the corresponding value is a user-facing title for the option. | ||
- `options.match: RegExp = null` - A regular expression that the string must match. | ||
- `options.multiline: boolean = false` - Whether the string allows newlines or not | ||
### `map()` | ||
### `url()` | ||
The `map()` creator function creates a `MapSchema` instance that can validate an object containing a list of key: value entries: | ||
The `url()` creator function creates a `UrlSchema` instance that can validate URLs: | ||
- Objects are valid, e.g. `{ a: 1, b: 2, c: 3 }` | ||
- Contents of the object can be validated with `options.props` | ||
- Falsy values are converted to `{}` empty object. | ||
- `{}` empty object is an invalid value if `options.required` is truthy. | ||
- Strings that are valid email addresses are valid, e.g. `https://x.com` or `data:anything` | ||
- Whitespace is trimmed automatically. | ||
- Falsy values are converted to `null` | ||
- `null` is an invalid value if `options.required` is truthy | ||
`map()` also allows the following options: | ||
`url()` also allows the following options: | ||
- `options.value: {} = {}` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then empty objects will return `Invalid("Required")` | ||
- `options.min: number = null` - The minimum number of items allowed. | ||
- `options.max: number = null` - The maximum number of items allowed. | ||
- `options.items: Schema` (required) - Schema that will be used to validate all properties in the object. | ||
- `options.value: string = null` - The default value which will be used if the value is `undefined` | ||
- `options.required: boolean = false` - If true, then null values will return `Invalid("Required")` | ||
- `options.schemes: string[] = ["http:", "https:"]` - Whitelist of allowed URL schemes. | ||
- `options.hosts: string[] = null` - List of allowed hostnames, e.g. `["google.com"]` | ||
- `options.max: number = 512` - Maximum length of a URL. | ||
@@ -546,3 +570,3 @@ ### Shortcuts | ||
```js | ||
import { boolean, date, email, key, number, phone, string, url } from "schemaglobin"; | ||
import { boolean, date, distance, email, key, number, phone, string, url } from "schemaglobin"; | ||
@@ -552,4 +576,8 @@ // The following is equivalent to e.g. boolean({ required: true }).validate() | ||
boolean.optional.validate(false); | ||
color.required.validate("#00CCFF"); | ||
color.optional.validate(null); | ||
date.required.validate(new Date()); | ||
date.optional.validate(null); | ||
distance.required.validate("123 km"); | ||
distance.optional.validate(null); | ||
email.required.validate("dave@x.com"); | ||
@@ -568,4 +596,2 @@ email.optional.validate(null); | ||
url.optional.validate(null); | ||
color.required.validate("#00CCFF"); | ||
color.optional.validate(null); | ||
``` | ||
@@ -572,0 +598,0 @@ |
@@ -9,2 +9,3 @@ // Export schema. | ||
export * from "./schemas/DateSchema"; | ||
export * from "./schemas/DistanceSchema"; | ||
export * from "./schemas/EmailSchema"; | ||
@@ -19,6 +20,8 @@ export * from "./schemas/KeySchema"; | ||
// Export Invalid. | ||
export * from "./Invalid"; | ||
// Export helpers. | ||
export * from "./helpers"; | ||
export * from "./helpers/date"; | ||
export * from "./helpers/distance"; | ||
export * from "./helpers/invalid"; | ||
export * from "./helpers/number"; | ||
export * from "./helpers/schema"; | ||
export * from "./helpers/string"; |
@@ -1,2 +0,2 @@ | ||
import type { Invalid } from "./Invalid"; | ||
import type { Invalid } from "./helpers/invalid"; | ||
@@ -3,0 +3,0 @@ /** |
import { Schema, SchemaOptions, SchemaType, ValidateFlags } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -4,0 +4,0 @@ /** Coerce an unknown value to an array (if possible). */ |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { FalseIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -5,0 +5,0 @@ /** Coerce an unknown value to a boolean. */ |
import type { NullIfOptional } from "../types"; | ||
import type { Schema, SchemaOptions } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -5,0 +5,0 @@ const R_COLOR = /^#[0-9A-F]{6}$/; |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
import { getYmd } from "../helpers/date"; | ||
@@ -19,7 +20,2 @@ // Types. | ||
/** Convert a `Date()` instance to a YMD string like "2015-09-12" */ | ||
export function dateToString(value: Date = new Date()): string { | ||
return value.toISOString().substr(0, 10); | ||
} | ||
// Options. | ||
@@ -83,3 +79,3 @@ export type DateInputs = Date | string | number | null | (() => DateInputs); | ||
// Return the valid date string. | ||
return dateToString(value); | ||
return getYmd(value); | ||
} | ||
@@ -86,0 +82,0 @@ } |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -5,0 +5,0 @@ /** Coerce an unknown value to an email (if possible). */ |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -5,0 +5,0 @@ /** Coerce an unknown value to a key (if possible). */ |
import type { Schema, SchemaOptions, SchemaType, ValidateFlags } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -4,0 +4,0 @@ /** Coerce an unknown value to a map (if possible). */ |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
import { stringToNumber, roundToStep } from "../helpers/number"; | ||
// RegExps. | ||
const R_NUMERIC = /[0-9]/; | ||
const R_MINUS = /-/g; | ||
/** | ||
* Convert a stringy number into a number. | ||
* | ||
* @param str The string to convert. | ||
* @return The number, null (valid, meaning no number). | ||
*/ | ||
export function stringToNumber(str: string): number | null { | ||
// Undefined if not a string. | ||
if (typeof str !== "string") return NaN; | ||
// Null if empty string. | ||
if (str.length === 0) return null; | ||
// Undefined if string has no numeric characters. | ||
const firstNumeric = str.search(R_NUMERIC); | ||
if (firstNumeric === -1) return null; | ||
// Negative if a minus sign appears before any numerics. | ||
// Double negatives are resolved (i.e. three "-" minus signs → one minus sign) | ||
const prefix = str.substr(0, firstNumeric); // Get starting portion of string without numbers. | ||
const signs = prefix ? prefix.split(R_MINUS).length - 1 : 0; // Number of minus signs. | ||
const negative = !!(signs % 2); // Negative (odd minus signs) or positive (even minus signs). | ||
// Get all the non-numeric and non-dot characters, then explode by dot. | ||
const nums = str.replace(/[^0-9.]+/g, "").split(/\.+/); | ||
// Combine back together the negative sign, main number, and decimal numbers | ||
const clean = (negative ? "-" : "") + nums[0] + (nums.length > 1 ? `.${nums.slice(1).join("")}` : ""); | ||
// Convert to number. | ||
const num = Number.parseFloat(clean); | ||
// Might be NaN (because e.g. 'abc' was passed'). | ||
return !Number.isNaN(num) ? num : null; | ||
} | ||
/** | ||
* Round numbers to a given step. | ||
* | ||
* @param num The number to round. | ||
* @param step=0 The rounding to round to, e.g. `2` or `0.1` | ||
* @returns The number rounded to the step. | ||
*/ | ||
export function roundToStep(num: number, step: number): number { | ||
if (step < 0.00001) throw new Error("roundToStep() does not work accurtely with steps smaller than 0.00001"); | ||
return Math.round(num / step) * step; | ||
} | ||
/** Coerce an unknown value to a number (if possible). */ | ||
@@ -64,2 +13,3 @@ export function coerceNumber(value: unknown): number | null | Invalid { | ||
if (typeof numValue === "number") return numValue; | ||
return new Invalid("Must be numeric"); | ||
} | ||
@@ -66,0 +16,0 @@ return new Invalid("Must be number or null"); |
@@ -5,3 +5,3 @@ /* eslint-disable no-dupe-class-members */ | ||
import type { NullIfOptional, DeepPartial, UnknownObject } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -8,0 +8,0 @@ /** Coerce an unknown value to an object (if possible). */ |
import type { Schema, SchemaOptions } from "../Schema"; | ||
import type { NullIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -5,0 +5,0 @@ /** |
@@ -5,3 +5,3 @@ /* eslint-disable no-control-regex */ | ||
import type { EmptyIfOptional } from "../types"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -8,0 +8,0 @@ /** Coerce an unknown value to a string (if possible). */ |
import type { NullIfOptional } from "../types"; | ||
import type { Schema, SchemaOptions } from "../Schema"; | ||
import { Invalid } from "../Invalid"; | ||
import { Invalid } from "../helpers/invalid"; | ||
@@ -5,0 +5,0 @@ /** Coerce an unknown value to a URL (if possible). */ |
@@ -149,4 +149,4 @@ import { Schema, array, string, number, object, StringSchema, NumberSchema, ArraySchema, Invalid } from "../../src"; | ||
if (invalid instanceof Invalid && invalid.messages) { | ||
expect(invalid.messages[0]).toBe("Must be number or null"); // arr[0] failed. | ||
expect(invalid.messages[2]).toBe("Must be number or null"); // arr[2] failed. | ||
expect(invalid.messages[0]).toBe("Must be numeric"); // arr[0] failed. | ||
expect(invalid.messages[2]).toBe("Must be numeric"); // arr[2] failed. | ||
expect(Object.keys(invalid.messages).length).toBe(2); // No additional errors (arr[1] passed). | ||
@@ -153,0 +153,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { date, DateSchema, dateToString, Invalid } from "../../src"; | ||
import { date, DateSchema, getYmd, Invalid } from "../../src"; | ||
@@ -83,3 +83,3 @@ // Tests. | ||
const schema = date({ value: Date.now }); | ||
expect(schema.validate(undefined)).toBe(dateToString(new Date())); | ||
expect(schema.validate(undefined)).toBe(getYmd(new Date())); | ||
}); | ||
@@ -125,8 +125,1 @@ }); | ||
}); | ||
describe("dateToString()", () => { | ||
test("Correctly converts date to string", () => { | ||
expect(dateToString(new Date("2019-11-27"))).toBe("2019-11-27"); | ||
expect(dateToString(new Date("0001-01-01"))).toBe("0001-01-01"); | ||
expect(dateToString(new Date("9999-12-31"))).toBe("9999-12-31"); | ||
}); | ||
}); |
@@ -136,3 +136,3 @@ import { map, string, number, boolean, NumberSchema, MapSchema, Invalid } from "../../src"; | ||
if (invalid instanceof Invalid && invalid.messages) { | ||
expect(invalid.messages.str).toEqual("Must be number or null"); | ||
expect(invalid.messages.str).toEqual("Must be numeric"); | ||
expect(Object.keys(invalid.messages).length).toBe(1); // No additional errors. | ||
@@ -139,0 +139,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { number, NumberSchema, Invalid, roundToStep, stringToNumber } from "../../src"; | ||
import { number, NumberSchema, Invalid } from "../../src"; | ||
@@ -67,3 +67,3 @@ // Tests. | ||
test("Non-number strings return Invalid", () => { | ||
expect(schema.validate("abc")).toEqual(new Invalid("Must be number or null")); | ||
expect(schema.validate("abc")).toEqual(new Invalid("Must be numeric")); | ||
}); | ||
@@ -172,60 +172,2 @@ test("Non-numbers return Invalid", () => { | ||
}); | ||
describe("roundToStep()", () => { | ||
test("Numbers are rounded correctly", () => { | ||
expect(roundToStep(51, 100)).toBe(100); | ||
expect(roundToStep(50, 100)).toBe(100); // Rounds up when exactly halfway. | ||
expect(roundToStep(149, 100)).toBe(100); | ||
expect(roundToStep(4, 2)).toBe(4); | ||
expect(roundToStep(3, 2)).toBe(4); // Rounds up when exactly halfway. | ||
expect(roundToStep(2, 2)).toBe(2); | ||
expect(roundToStep(1001, 1)).toBe(1001); | ||
expect(roundToStep(100.0, 1)).toBe(100); | ||
expect(roundToStep(100.001, 0.001)).toBe(100.001); | ||
expect(roundToStep(100.001, 0.001)).not.toBe(100); | ||
expect(roundToStep(100.00001, 0.00001)).toBe(100.00001); | ||
expect(roundToStep(100.00001, 0.0001)).toBe(100); | ||
}); | ||
}); | ||
describe("stringToNumber()", () => { | ||
test("Strings with numbers are converted to numbers", () => { | ||
expect(stringToNumber("0")).toBe(0); | ||
expect(stringToNumber("1")).toBe(1); | ||
}); | ||
test("Decimal places are converted correctly", () => { | ||
expect(stringToNumber("1.555")).toBe(1.555); | ||
expect(stringToNumber("123.456")).toBe(123.456); | ||
expect(stringToNumber("1.5.5.5")).toBe(1.555); | ||
expect(stringToNumber("99999.9.9.9.9.9")).toBe(99999.99999); | ||
}); | ||
test("Signs are converted correctly", () => { | ||
expect(stringToNumber("-1")).toBe(-1); | ||
expect(stringToNumber("--1")).toBe(1); | ||
expect(stringToNumber("---1")).toBe(-1); | ||
expect(stringToNumber("----1")).toBe(1); | ||
expect(stringToNumber("+1")).toBe(1); | ||
expect(stringToNumber("++1")).toBe(1); | ||
expect(stringToNumber("+++1")).toBe(1); | ||
expect(stringToNumber("++++1")).toBe(1); | ||
expect(stringToNumber("+-+1")).toBe(-1); | ||
expect(stringToNumber("-+-+1")).toBe(1); | ||
expect(stringToNumber("1.5")).toBe(1.5); | ||
expect(stringToNumber("-1.5")).toBe(-1.5); | ||
expect(stringToNumber("--1.5")).toBe(1.5); | ||
expect(stringToNumber("---1.5")).toBe(-1.5); | ||
expect(stringToNumber("----1.5")).toBe(1.5); | ||
}); | ||
test("Complicated numbers are fixed correctly", () => { | ||
expect(stringToNumber("99999.9.9.9.9.9")).toBe(99999.99999); | ||
expect(stringToNumber("-99999.9.9.9.9.9")).toBe(-99999.99999); | ||
expect(stringToNumber("--99999.9.9.9.9.9")).toBe(99999.99999); | ||
expect(stringToNumber("---99999.9.9.9.9.9")).toBe(-99999.99999); | ||
}); | ||
test("Empty string returns null", () => { | ||
expect(stringToNumber("")).toBe(null); | ||
}); | ||
test("Non-numbers return null", () => { | ||
expect(stringToNumber("a")).toBe(null); | ||
expect(stringToNumber("Willow perceptiveness purely sportsmanship namaste victoriously?")).toBe(null); | ||
}); | ||
}); | ||
}); |
@@ -226,3 +226,3 @@ import { object, number, StringSchema, NumberSchema, BooleanSchema, ObjectSchema, Invalid } from "../../src"; | ||
if (invalid instanceof Invalid && invalid.messages) { | ||
expect(invalid.messages.dogs).toEqual("Must be number or null"); | ||
expect(invalid.messages.dogs).toEqual("Must be numeric"); | ||
expect(invalid.messages.cats).toEqual("Required"); | ||
@@ -368,3 +368,3 @@ expect(Object.keys(invalid.messages).length).toBe(2); // No additional errors. | ||
if (invalid instanceof Invalid && invalid.messages) { | ||
expect(invalid.messages.dogs).toEqual("Must be number or null"); | ||
expect(invalid.messages.dogs).toEqual("Must be numeric"); | ||
expect(invalid.messages.cats).toEqual("Required"); | ||
@@ -371,0 +371,0 @@ expect(Object.keys(invalid.messages).length).toBe(2); // No additional errors (it doesn't matter that turtles is missing). |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
294843
93
6141
607