Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

schemaglobin

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

schemaglobin - npm Package Compare versions

Comparing version 3.2.0 to 3.3.0

1

dist/schemas/ArraySchema.d.ts

@@ -56,3 +56,2 @@ import { Schema, SchemaWithSchemas, SchemaOptions, SchemaType } from "../types";

validate(unsafeValue?: unknown): ReadonlyArray<SchemaType<S>> | Invalid;
private validateSchema;
}

@@ -59,0 +58,0 @@ /** Shortcuts for ArraySchema. */

33

dist/schemas/ArraySchema.js

@@ -60,7 +60,7 @@ "use strict";

// Coorce.
const value = this.coerce(unsafeValue);
if (value instanceof Invalid_1.Invalid)
return value;
const unsafeArray = this.coerce(unsafeValue);
if (unsafeArray instanceof Invalid_1.Invalid)
return unsafeArray;
// Has contents?
if (!value.length) {
if (!unsafeArray.length) {
// Check requiredness.

@@ -71,23 +71,18 @@ if (this.required)

// We know this assertion is okay because we know the array is empty.
return value;
return unsafeArray;
}
// Array shorter than min length returns Invalid.
if (typeof this.min === "number" && value.length < this.min)
if (typeof this.min === "number" && unsafeArray.length < this.min)
return new Invalid_1.Invalid(`Minimum ${this.min} items`);
// Array longer than max length returns Invalid.
if (typeof this.max === "number" && value.length > this.max)
if (typeof this.max === "number" && unsafeArray.length > this.max)
return new Invalid_1.Invalid(`Maximum ${this.max} items`);
// Check each item against `this.items`
return this.validateSchema(value);
}
// Validate an array against a subschema.
// Only returns a new instance of the object if it has changed (for immutability).
validateSchema(input) {
let changed = false;
let invalid = false;
const items = this.items;
const output = [];
const safeArray = [];
const invalids = {};
let changed = false;
let invalid = false;
for (let i = 0, l = input.length; i < l; i++) {
const current = input[i];
for (let i = 0, l = unsafeArray.length; i < l; i++) {
const current = unsafeArray[i];
const value = items.validate(current);

@@ -101,3 +96,3 @@ if (value instanceof Invalid_1.Invalid) {

changed = true;
output.push(value);
safeArray.push(value);
}

@@ -110,3 +105,3 @@ }

// We know this assertion is okay because if it wasn't, we would've returned Invalid.
return (changed ? output : input);
return (changed ? safeArray : unsafeArray);
}

@@ -113,0 +108,0 @@ }

@@ -35,7 +35,2 @@ import { Schema, SchemaWithSchemas, SchemaOptions, SchemaType } from "../types";

validate(unsafeValue?: unknown): Readonly<Record<string, SchemaType<S>>> | Invalid;
/**
* Validate an object's contents.
* Only returns a new instance of the object if it has changed (for immutability).
*/
private validateItems;
}

@@ -42,0 +37,0 @@ /** Shortcuts for MapSchema. */

@@ -38,7 +38,7 @@ "use strict";

// Coorce.
const value = this.coerce(unsafeValue);
if (value instanceof Invalid_1.Invalid)
return value;
const unsafeObject = this.coerce(unsafeValue);
if (unsafeObject instanceof Invalid_1.Invalid)
return unsafeObject;
// Get number of properties.
const length = Object.keys(value).length;
const length = Object.keys(unsafeObject).length;
// Has contents?

@@ -51,3 +51,3 @@ if (!length) {

// We know this type assertion is sound because we know value is empty.
return value;
return unsafeObject;
}

@@ -60,30 +60,19 @@ // Check min and max.

// Check value against against `this.items`
return this.validateItems(value);
}
/**
* Validate an object's contents.
* Only returns a new instance of the object if it has changed (for immutability).
*/
validateItems(input) {
let changed = false;
let invalid = false;
const output = {};
const invalids = {};
let changed = false;
let invalid = false;
// Validate the object against `this.items`
const items = this.items;
if (items) {
Object.entries(input).forEach(([key, current]) => {
// Validate the value.
const value = items.validate(current);
if (value instanceof Invalid_1.Invalid) {
invalid = true;
invalids[key] = value.message;
}
else {
if (value !== current)
changed = true;
output[key] = value;
}
});
}
Object.entries(unsafeObject).forEach(([key, current]) => {
// Validate the value.
const value = this.items.validate(current);
if (value instanceof Invalid_1.Invalid) {
invalid = true;
invalids[key] = value.message;
}
else {
if (value !== current)
changed = true;
output[key] = value;
}
});
// If any Schema threw Invalid, return an Invalids.

@@ -93,3 +82,3 @@ if (invalid)

// Return immuatably (return output if changes were made, or exact input otherwise).
return (changed ? output : input);
return (changed ? output : unsafeObject);
}

@@ -96,0 +85,0 @@ }

@@ -40,8 +40,14 @@ import { Schema, SchemaWithSchemas, SchemaOptions, SchemaType, NullIfOptional } from "../types";

}> | NullIfOptional<R> | Invalid;
schema<K extends keyof S & string>(key: K): S[K];
/**
* Validate an object's contents.
* Only returns a new instance of the object if it has changed (for immutability).
* Like `validate()` method but works on a Partial value.
* e.g. Missing properties in the object don't cause Invalid to be thrown.
*
* - Props that don't appear in `options.props` are silently removed.
*
* @returns The valid partial value, or an Invalid summarising the issues.
*/
private validateSchema;
partialValidate(unsafeValue: unknown): Readonly<Partial<{
[K in keyof S]: SchemaType<S[K]>;
}>> | NullIfOptional<R> | Invalid;
schema<K extends keyof S & string>(key: K): S[K];
}

@@ -48,0 +54,0 @@ /** Shortcuts for ObjectSchema. */

@@ -28,7 +28,7 @@ "use strict";

// Coorce.
const value = this.coerce(unsafeValue);
if (value instanceof Invalid_1.Invalid)
return value;
const unsafeObj = this.coerce(unsafeValue);
if (unsafeObj instanceof Invalid_1.Invalid)
return unsafeObj;
// Null means 'no object'
if (value === null) {
if (unsafeObj === null) {
// Check requiredness.

@@ -41,39 +41,77 @@ if (this.required)

}
// Check value against against `this.props`
return this.validateSchema(value);
// Validate the object against `this.props`
let changed = false;
let invalid = false;
const safeObj = {};
const invalids = {};
const entries = Object.entries(this.props);
entries.forEach(([key, schema]) => {
const unsafeProp = unsafeObj[key];
const safeProp = schema.validate(unsafeProp);
if (safeProp instanceof Invalid_1.Invalid) {
invalid = true;
invalids[key] = safeProp.message;
}
else {
if (safeProp !== unsafeProp)
changed = true;
safeObj[key] = safeProp;
}
});
// If input has keys that aren't in props, then these keys are _excess_ and we need to return output.
if (Object.keys(unsafeObj).length > entries.length)
changed = true;
// If any Schema threw Invalid, return an Invalids.
if (invalid)
return new Invalid_1.Invalid("Invalid format", invalids);
// Return immuatably (return output if changes were made, or exact input otherwise).
return (changed ? safeObj : unsafeObj);
}
// Implement SchemasSchema
schema(key) {
return this.props[key];
}
/**
* Validate an object's contents.
* Only returns a new instance of the object if it has changed (for immutability).
* Like `validate()` method but works on a Partial value.
* e.g. Missing properties in the object don't cause Invalid to be thrown.
*
* - Props that don't appear in `options.props` are silently removed.
*
* @returns The valid partial value, or an Invalid summarising the issues.
*/
validateSchema(input) {
const output = {};
const invalids = {};
partialValidate(unsafeValue) {
// Coorce.
const unsafeObj = this.coerce(unsafeValue);
if (unsafeObj instanceof Invalid_1.Invalid)
return unsafeObj;
// Null means 'no object'
if (unsafeObj === null) {
// Check requiredness.
if (this.required)
return new Invalid_1.Invalid("Required");
// Return.
// We know this type assertion is sound because `null` can never be returned if `this.required == true`.
return null;
}
// Check (partial) value against against `this.props`
let changed = false;
let invalid = false;
// Validate the object against `this.props`
const props = this.props;
if (props) {
const entries = Object.entries(props);
entries.forEach(([key, schema]) => {
const current = input[key];
const value = schema.validate(current);
if (value instanceof Invalid_1.Invalid) {
const safeObj = {};
const invalids = {};
Object.entries(unsafeObj).forEach(([key, unsafeProp]) => {
if (key in this.props) {
// Known props: validate against schema.
const schema = this.props[key];
const safeProp = schema.validate(unsafeProp);
if (safeProp instanceof Invalid_1.Invalid) {
invalid = true;
invalids[key] = value.message;
invalids[key] = safeProp.message;
}
else {
if (value !== current)
if (safeProp !== unsafeProp)
changed = true;
output[key] = value;
safeObj[key] = safeProp;
}
});
// If input has keys that aren't in props, then these keys are _excess_ and we need to return output.
if (Object.keys(input).length > entries.length)
}
else {
// Unknown prop: needs to be stripped from the return.
changed = true;
}
}
});
// If any Schema threw Invalid, return an Invalids.

@@ -83,4 +121,8 @@ if (invalid)

// Return immuatably (return output if changes were made, or exact input otherwise).
return (changed ? output : input);
return (changed ? safeObj : unsafeObj);
}
// Implement SchemasSchema
schema(key) {
return this.props[key];
}
}

@@ -87,0 +129,0 @@ exports.ObjectSchema = ObjectSchema;

{
"name": "schemaglobin",
"description": "Validate user-entered data.",
"version": "3.2.0",
"version": "3.3.0",
"repository": "https://github.com/dhoulb/schemaglobin",

@@ -6,0 +6,0 @@ "author": "Dave Houlbrooke <dave@shax.com>",

@@ -454,2 +454,7 @@ # Schemaglobin: Validate unknown user input against schemas

`ObjectSchema` instances also provide the following methods:
- `partialValidate(value: Partial<T>)` - Validate a _partial_ object where some properties are missing (normal `validate()` would return `Invalid` if required properties were missing).
- `schema(key: string)` - Get the subschema from `options.props` that corresponds to `key`
### `array()`

@@ -456,0 +461,0 @@

@@ -92,7 +92,7 @@ import { Schema, SchemaWithSchemas, SchemaOptions, SchemaType } from "../types";

// Coorce.
const value = this.coerce(unsafeValue);
if (value instanceof Invalid) return value;
const unsafeArray = this.coerce(unsafeValue);
if (unsafeArray instanceof Invalid) return unsafeArray;
// Has contents?
if (!value.length) {
if (!unsafeArray.length) {
// Check requiredness.

@@ -103,26 +103,21 @@ if (this.required) return new Invalid("Required");

// We know this assertion is okay because we know the array is empty.
return value as ReadonlyArray<SchemaType<S>>;
return unsafeArray as ReadonlyArray<SchemaType<S>>;
}
// Array shorter than min length returns Invalid.
if (typeof this.min === "number" && value.length < this.min) return new Invalid(`Minimum ${this.min} items`);
if (typeof this.min === "number" && unsafeArray.length < this.min)
return new Invalid(`Minimum ${this.min} items`);
// Array longer than max length returns Invalid.
if (typeof this.max === "number" && value.length > this.max) return new Invalid(`Maximum ${this.max} items`);
if (typeof this.max === "number" && unsafeArray.length > this.max)
return new Invalid(`Maximum ${this.max} items`);
// Check each item against `this.items`
return this.validateSchema(value);
}
// Validate an array against a subschema.
// Only returns a new instance of the object if it has changed (for immutability).
private validateSchema(input: unknown[]): ReadonlyArray<SchemaType<S>> | Invalid {
let changed = false;
let invalid = false;
const items = this.items;
const output: unknown[] = [];
const safeArray: unknown[] = [];
const invalids: { [name: string]: string } = {};
let changed = false;
let invalid = false;
for (let i = 0, l = input.length; i < l; i++) {
const current = input[i];
for (let i = 0, l = unsafeArray.length; i < l; i++) {
const current = unsafeArray[i];
const value = items.validate(current);

@@ -134,3 +129,3 @@ if (value instanceof Invalid) {

if (value !== current) changed = true;
output.push(value);
safeArray.push(value);
}

@@ -144,3 +139,3 @@ }

// We know this assertion is okay because if it wasn't, we would've returned Invalid.
return (changed ? output : input) as ReadonlyArray<SchemaType<S>>;
return (changed ? safeArray : unsafeArray) as ReadonlyArray<SchemaType<S>>;
}

@@ -147,0 +142,0 @@ }

@@ -71,7 +71,7 @@ import { Schema, SchemaWithSchemas, SchemaOptions, SchemaType } from "../types";

// Coorce.
const value = this.coerce(unsafeValue);
if (value instanceof Invalid) return value;
const unsafeObject = this.coerce(unsafeValue);
if (unsafeObject instanceof Invalid) return unsafeObject;
// Get number of properties.
const length = Object.keys(value).length;
const length = Object.keys(unsafeObject).length;

@@ -85,3 +85,3 @@ // Has contents?

// We know this type assertion is sound because we know value is empty.
return value as Record<string, SchemaType<S>>;
return unsafeObject as Record<string, SchemaType<S>>;
}

@@ -94,31 +94,18 @@

// Check value against against `this.items`
return this.validateItems(value);
}
/**
* Validate an object's contents.
* Only returns a new instance of the object if it has changed (for immutability).
*/
private validateItems(input: Record<string, unknown>): Record<string, SchemaType<S>> | Invalid {
let changed = false;
let invalid = false;
const output: Record<string, unknown> = {};
const invalids: Record<string, string> = {};
let changed = false;
let invalid = false;
Object.entries(unsafeObject).forEach(([key, current]) => {
// Validate the value.
const value = this.items.validate(current);
if (value instanceof Invalid) {
invalid = true;
invalids[key] = value.message;
} else {
if (value !== current) changed = true;
output[key] = value;
}
});
// Validate the object against `this.items`
const items = this.items;
if (items) {
Object.entries(input).forEach(([key, current]) => {
// Validate the value.
const value = items.validate(current);
if (value instanceof Invalid) {
invalid = true;
invalids[key] = value.message;
} else {
if (value !== current) changed = true;
output[key] = value;
}
});
}
// If any Schema threw Invalid, return an Invalids.

@@ -128,3 +115,3 @@ if (invalid) return new Invalid("Invalid format", invalids);

// Return immuatably (return output if changes were made, or exact input otherwise).
return (changed ? output : input) as Record<string, SchemaType<S>>;
return (changed ? output : unsafeObject) as Record<string, SchemaType<S>>;
}

@@ -131,0 +118,0 @@ }

@@ -60,7 +60,7 @@ import { Schema, SchemaWithSchemas, SchemaOptions, SchemaType, NullIfOptional } from "../types";

// Coorce.
const value = this.coerce(unsafeValue);
if (value instanceof Invalid) return value;
const unsafeObj = this.coerce(unsafeValue);
if (unsafeObj instanceof Invalid) return unsafeObj;
// Null means 'no object'
if (value === null) {
if (unsafeObj === null) {
// Check requiredness.

@@ -74,41 +74,78 @@ if (this.required) return new Invalid("Required");

// Check value against against `this.props`
return this.validateSchema(value);
}
// Validate the object against `this.props`
let changed = false;
let invalid = false;
const safeObj: { [key: string]: unknown } = {};
const invalids: { [key: string]: string } = {};
const entries = Object.entries(this.props);
entries.forEach(([key, schema]) => {
const unsafeProp = unsafeObj[key];
const safeProp = schema.validate(unsafeProp);
if (safeProp instanceof Invalid) {
invalid = true;
invalids[key] = safeProp.message;
} else {
if (safeProp !== unsafeProp) changed = true;
safeObj[key] = safeProp;
}
});
// Implement SchemasSchema
schema<K extends keyof S & string>(key: K): S[K] {
return this.props[key];
// If input has keys that aren't in props, then these keys are _excess_ and we need to return output.
if (Object.keys(unsafeObj).length > entries.length) changed = true;
// If any Schema threw Invalid, return an Invalids.
if (invalid) return new Invalid("Invalid format", invalids);
// Return immuatably (return output if changes were made, or exact input otherwise).
return (changed ? safeObj : unsafeObj) as { [K in keyof S]: SchemaType<S[K]> };
}
/**
* Validate an object's contents.
* Only returns a new instance of the object if it has changed (for immutability).
* Like `validate()` method but works on a Partial value.
* e.g. Missing properties in the object don't cause Invalid to be thrown.
*
* - Props that don't appear in `options.props` are silently removed.
*
* @returns The valid partial value, or an Invalid summarising the issues.
*/
private validateSchema(input: UnknownObject): { [K in keyof S]: SchemaType<S[K]> } | Invalid {
const output: { [key: string]: unknown } = {};
const invalids: { [key: string]: string } = {};
partialValidate(
unsafeValue: unknown,
): Readonly<Partial<{ [K in keyof S]: SchemaType<S[K]> }>> | NullIfOptional<R> | Invalid {
// Coorce.
const unsafeObj = this.coerce(unsafeValue);
if (unsafeObj instanceof Invalid) return unsafeObj;
// Null means 'no object'
if (unsafeObj === null) {
// Check requiredness.
if (this.required) return new Invalid("Required");
// Return.
// We know this type assertion is sound because `null` can never be returned if `this.required == true`.
return null as NullIfOptional<R>;
}
// Check (partial) value against against `this.props`
let changed = false;
let invalid = false;
// Validate the object against `this.props`
const props = this.props;
if (props) {
const entries = Object.entries(props);
entries.forEach(([key, schema]) => {
const current = input[key];
const value = (schema as Schema).validate(current);
if (value instanceof Invalid) {
const safeObj: { [key: string]: unknown } = {};
const invalids: { [key: string]: string } = {};
Object.entries(unsafeObj).forEach(([key, unsafeProp]) => {
if (key in this.props) {
// Known props: validate against schema.
const schema = this.props[key];
const safeProp = schema.validate(unsafeProp);
if (safeProp instanceof Invalid) {
invalid = true;
invalids[key] = value.message;
invalids[key] = safeProp.message;
} else {
if (value !== current) changed = true;
output[key] = value;
if (safeProp !== unsafeProp) changed = true;
safeObj[key] = safeProp;
}
});
} else {
// Unknown prop: needs to be stripped from the return.
changed = true;
}
});
// If input has keys that aren't in props, then these keys are _excess_ and we need to return output.
if (Object.keys(input).length > entries.length) changed = true;
}
// If any Schema threw Invalid, return an Invalids.

@@ -118,4 +155,9 @@ if (invalid) return new Invalid("Invalid format", invalids);

// Return immuatably (return output if changes were made, or exact input otherwise).
return (changed ? output : input) as { [K in keyof S]: SchemaType<S[K]> };
return (changed ? safeObj : unsafeObj) as { [K in keyof S]: SchemaType<S[K]> };
}
// Implement SchemasSchema
schema<K extends keyof S & string>(key: K): S[K] {
return this.props[key];
}
}

@@ -122,0 +164,0 @@

@@ -177,2 +177,99 @@ import {

});
describe("partialValidate()", () => {
test("Non-objects throw error", () => {
const schema = object({ props: {} });
expect(schema.partialValidate("abc")).toEqual(new Invalid("Must be object"));
expect(schema.partialValidate(123)).toEqual(new Invalid("Must be object"));
expect(schema.partialValidate(true)).toEqual(new Invalid("Must be object"));
});
test("Falsy values return null", () => {
const schema = object({ props: {} });
expect(schema.partialValidate(0)).toBe(null);
expect(schema.partialValidate(null)).toBe(null);
expect(schema.partialValidate(false)).toBe(null);
});
describe("options.required", () => {
test("Required null objects return Required", () => {
const schema = object({ props: {}, required: true });
expect(schema.partialValidate(null)).toEqual(new Invalid("Required"));
});
test("Required non-null objects are not invalid", () => {
const schema = object({ props: {}, required: true });
const obj = {};
expect(schema.partialValidate(obj)).toBe(obj);
});
test("Non-required empty objects do not return Required", () => {
const schema = object({ props: {}, required: false });
const obj = {};
expect(schema.partialValidate(obj)).toBe(obj);
});
});
describe("options.props", () => {
test("Object with no missing props that validates is returned unchanged", () => {
const a = { num: 123, str: "abc", bool: true };
const schema = object({
props: {
num: new NumberSchema(),
str: new StringSchema(),
bool: new BooleanSchema(),
},
});
expect(schema.partialValidate(a)).toBe(a);
});
test("Object with missing props that validates is returned unchanged", () => {
const a = { num: 123 };
const schema = object({
props: {
num: new NumberSchema(),
str: new StringSchema(),
bool: new BooleanSchema(),
},
});
expect(schema.partialValidate(a)).toBe(a);
});
test("Object with props and fixable schema is fixed", () => {
const schema = object({
props: {
num: new NumberSchema(),
str: new StringSchema(),
bool: new BooleanSchema(),
},
});
expect(schema.partialValidate({ num: "123", str: 123 })).toEqual({ num: 123, str: "123" });
});
test("Object with props has unknown fields stripped", () => {
const schema = object({
props: {
num: new NumberSchema(),
str: new StringSchema({ value: "abcdef" }),
bool: new BooleanSchema(),
},
});
expect(
schema.partialValidate({
num: 123,
str: "abcdef",
excess: "should be removed",
}),
).toEqual({ num: 123, str: "abcdef" }); // Excess is removed.
});
test("Objects with unfixable errors in subschemas returns Invalids", () => {
const schema = object({
props: {
dogs: new NumberSchema(),
turtles: new NumberSchema(),
cats: new NumberSchema({ required: true }),
},
});
const invalid = schema.partialValidate({ dogs: "abc", cats: false });
expect(invalid).toBeInstanceOf(Object);
if (invalid instanceof Invalid && invalid.messages) {
expect(invalid.messages.dogs).toEqual("Must be number or null");
expect(invalid.messages.cats).toEqual("Required");
expect(Object.keys(invalid.messages).length).toBe(2); // No additional errors (it doesn't matter that turtles is missing).
}
});
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc