Socket
Socket
Sign inDemoInstall

@humanwhocodes/object-schema

Package Overview
Dependencies
0
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.0 to 1.0.0

2

package.json
{
"name": "@humanwhocodes/object-schema",
"version": "0.2.0",
"version": "1.0.0",
"description": "An object schema merger/validator",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -32,11 +32,6 @@ # JavaScript ObjectSchema Package

const schema = new ObjectSchema();
const schema = new ObjectSchema({
// there is also schema.defineStrategy() to just define one at a time
schema.defineStrategies([
// define a strategy for the "downloads" key
{
name: "downloads",
// define a definition for the "downloads" key
downloads: {
required: true,

@@ -54,4 +49,3 @@ merge(value1, value2) {

// define a strategy for the "versions" key
{
name: "versions",
version: {
required: true,

@@ -67,3 +61,3 @@ merge(value1, value2) {

}
]);
});

@@ -117,13 +111,12 @@ const record1 = {

```js
const schema = new ObjectSchema();
schema.defineStrategy({
name: "date",
merge() {
return undefined;
},
validate(value) {
Date.parse(value); // throws an error when invalid
const schema = new ObjectSchema({
date: {
merge() {
return undefined;
},
validate(value) {
Date.parse(value); // throws an error when invalid
}
}
})
});

@@ -145,5 +138,4 @@ const object1 = { date: "5/5/2005" };

schema.defineStrategies([
{
name: "date",
const schema = new ObjectSchema({
date: {
merge() {

@@ -156,6 +148,3 @@ return undefined;

},
// the key "time" requires that "date" be present
{
name: "time",
time: {
requires: ["date"],

@@ -169,5 +158,4 @@ merge(first, second) {

}
});
]);
// throws error: Key "time" requires keys "date"

@@ -174,0 +162,0 @@ schema.validate({

@@ -14,2 +14,30 @@ /**

/**
* Validates a schema strategy.
* @param {string} name The name of the key this strategy is for.
* @param {Object} strategy The strategy for the object key.
* @param {boolean} [strategy.required=true] Whether the key is required.
* @param {string[]} [strategy.requires] Other keys that are required when
* this key is present.
* @param {Function} strategy.merge A method to call when merging two objects
* with the same key.
* @param {Function} strategy.validate A method to call when validating an
* object with the key.
* @returns {void}
* @throws {Error} When the strategy is missing a name.
* @throws {Error} When the strategy is missing a merge() method.
* @throws {Error} When the strategy is missing a validate() method.
*/
function validateDefinition(name, strategy) {
if (typeof strategy.merge !== "function") {
throw new Error(`Definition for key "${name}" must have a merge() method.`);
}
if (typeof strategy.validate !== "function") {
throw new Error(`Definition for key "${name}" must have a validate() method.`);
}
}
//-----------------------------------------------------------------------------

@@ -27,4 +55,8 @@ // Class

*/
constructor() {
constructor(definitions) {
if (!definitions) {
throw new Error("Schema definitions missing.");
}
/**

@@ -39,62 +71,20 @@ * Track all strategies in the schema by key.

* Separately track any keys that are required for faster validation.
* @type {Array}
* @type {Map}
* @property requiredKeys
*/
this[requiredKeys] = [];
}
this[requiredKeys] = new Map();
/**
* Defines a new strategy for an object key.
* @param {Object} strategy The strategy for the object key.
* @param {string} strategy.name The name of the key the strategy applies to.
* @param {boolean} [strategy.required=true] Whether the key is required.
* @param {string[]} [strategy.requires] Other keys that are required when
* this key is present.
* @param {Function} strategy.merge A method to call when merging two objects
* with the same key.
* @param {Function} strategy.validate A method to call when validating an
* object with the key.
* @returns {void}
* @throws {Error} When the strategy is missing a name.
* @throws {Error} When the strategy is missing a merge() method.
* @throws {Error} When the strategy is missing a validate() method.
*/
defineStrategy(strategy) {
// add in all strategies
for (const key of Object.keys(definitions)) {
validateDefinition(key, definitions[key]);
if (typeof strategy.name !== "string") {
throw new Error("Strategy must have a \"name\" property.");
}
this[strategies].set(key, definitions[key]);
if (typeof strategy.merge !== "function") {
throw new Error(`Strategy for key "${strategy.name}" must have a merge() method.`);
if (definitions[key].required) {
this[requiredKeys].set(key, definitions[key]);
}
}
if (typeof strategy.validate !== "function") {
throw new Error(`Strategy for key "${strategy.name}" must have a validate() method.`);
}
this[strategies].set(strategy.name, strategy);
if (strategy.required) {
this[requiredKeys].push(strategy);
}
}
/**
* Defines multiple strategies at the same time. This is helpful for
* defining an array of strategies elsewhere and just passing the
* array directly instead of making individual method calls for each one.
* @param {Strategy[]} strategies An array of strategies to define.
* @returns {void}
* @throws {Error} When the strategy is missing a name.
* @throws {Error} When the strategy is missing a merge() method.
* @throws {Error} When the strategy is missing a validate() method.
*/
defineStrategies(strategies) {
for (const strategy of strategies) {
this.defineStrategy(strategy);
}
}
/**
* Determines if a strategy has been registered for the given object key.

@@ -104,3 +94,3 @@ * @param {string} key The object key to find a strategy for.

*/
hasStrategyFor(key) {
hasKey(key) {
return this[strategies].has(key);

@@ -152,3 +142,3 @@ }

// check to see if the key is defined
if (!this.hasStrategyFor(key)) {
if (!this.hasKey(key)) {
throw new Error(`Unexpected key "${key}" found.`);

@@ -168,9 +158,14 @@ }

// now apply remaining validation strategy
strategy.validate.call(strategy, object);
try {
strategy.validate.call(strategy, object);
} catch (ex) {
ex.message = `Key "${key}": ` + ex.message;
throw ex;
}
}
// ensure required keys aren't missing
for (const strategy of this[requiredKeys]) {
if (!(strategy.name in object)) {
throw new Error(`Missing required key "${strategy.name}".`);
for (const [key] of this[requiredKeys]) {
if (!(key in object)) {
throw new Error(`Missing required key "${key}".`);
}

@@ -177,0 +172,0 @@ }

@@ -23,25 +23,23 @@ /**

beforeEach(() => {
schema = new ObjectSchema();
});
describe("new ObjectSchema()", () => {
describe("defineStrategy()", () => {
it("should add a new key when a strategy is passed", () => {
schema.defineStrategy({
name: "foo",
merge() {},
validate() {}
schema = new ObjectSchema({
foo: {
merge() {},
validate() {}
}
});
assert.isTrue(schema.hasStrategyFor("foo"));
assert.isTrue(schema.hasKey("foo"));
});
it("should throw an error when a strategy is missing a name", () => {
it("should throw an error when a strategy is missing a merge() method", () => {
assert.throws(() => {
schema.defineStrategy({
merge() {},
validate() {}
schema = new ObjectSchema({
foo: {
validate() { }
}
});
}, /Strategy must have a "name" property/);
}, /Definition for key "foo" must have a merge\(\) method/);
});

@@ -51,7 +49,4 @@

assert.throws(() => {
schema.defineStrategy({
name: "foo",
validate() {}
});
}, /Strategy for key "foo" must have a merge\(\) method/);
schema = new ObjectSchema();
}, /Schema definitions missing/);
});

@@ -61,7 +56,8 @@

assert.throws(() => {
schema.defineStrategy({
name: "foo",
merge() {}
schema = new ObjectSchema({
foo: {
merge() { },
}
});
}, /Strategy for key "foo" must have a validate\(\) method/);
}, /Definition for key "foo" must have a validate\(\) method/);
});

@@ -71,47 +67,8 @@

describe("defineStrategies()", () => {
it("should add a new key when a strategy is passed", () => {
schema.defineStrategies([{
name: "foo",
merge() { },
validate() { }
}]);
assert.isTrue(schema.hasStrategyFor("foo"));
});
it("should throw an error when a strategy is missing a name", () => {
assert.throws(() => {
schema.defineStrategies([{
merge() { },
validate() { }
}]);
}, /Strategy must have a "name" property/);
});
it("should throw an error when a strategy is missing a merge() method", () => {
assert.throws(() => {
schema.defineStrategies([{
name: "foo",
validate() { }
}]);
}, /Strategy for key "foo" must have a merge\(\) method/);
});
it("should throw an error when a strategy is missing a validate() method", () => {
assert.throws(() => {
schema.defineStrategies([{
name: "foo",
merge() { }
}]);
}, /Strategy for key "foo" must have a validate\(\) method/);
});
});
describe("merge()", () => {
it("should throw an error when an unexpected key is found", () => {
let schema = new ObjectSchema({});
assert.throws(() => {

@@ -123,10 +80,12 @@ schema.merge({ foo: true }, { foo: true });

it("should call the merge() strategy for one key when called", () => {
schema.defineStrategy({
name: "foo",
merge() {
return "bar";
},
validate() {}
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() {}
}
});
const result = schema.merge({ foo: true }, { foo: false });

@@ -137,8 +96,9 @@ assert.propertyVal(result, "foo", "bar");

it("should omit returning the key when the merge() strategy returns undefined", () => {
schema.defineStrategy({
name: "foo",
merge() {
return undefined;
},
validate() {}
schema = new ObjectSchema({
foo: {
merge() {
return undefined;
},
validate() { }
}
});

@@ -151,16 +111,16 @@

it("should call the merge() strategy for two keys when called", () => {
schema.defineStrategies([{
name: "foo",
merge() {
return "bar";
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() { }
},
validate() {}
},
{
name: "bar",
merge() {
return "baz";
},
validate() {}
}]);
bar: {
merge() {
return "baz";
},
validate() {}
}
});

@@ -173,16 +133,16 @@ const result = schema.merge({ foo: true, bar: 1 }, { foo: true, bar: 2 });

it("should call the merge() strategy for two keys when called on three objects", () => {
schema.defineStrategies([{
name: "foo",
merge() {
return "bar";
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() { }
},
validate() {}
},
{
name: "bar",
merge() {
return "baz";
},
validate() {}
}]);
bar: {
merge() {
return "baz";
},
validate() { }
}
});

@@ -203,2 +163,3 @@ const result = schema.merge(

it("should throw an error when an unexpected key is found", () => {
let schema = new ObjectSchema({});
assert.throws(() => {

@@ -210,6 +171,9 @@ schema.validate({ foo: true });

it("should not throw an error when an expected key is found", () => {
schema.defineStrategy({
name: "foo",
merge() {},
validate() {}
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() {}
}
});

@@ -221,13 +185,16 @@

it("should not throw an error when expected keys are found", () => {
schema.defineStrategy({
name: "foo",
merge() {},
validate() {}
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() {}
},
bar: {
merge() {
return "baz";
},
validate() {}
}
});
schema.defineStrategy({
name: "bar",
merge() {},
validate() {}
});

@@ -238,14 +205,17 @@ schema.validate({ foo: true, bar: true });

it("should not throw an error when expected keys are found with required keys", () => {
schema.defineStrategy({
name: "foo",
merge() {},
validate() {}
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() { }
},
bar: {
requires: ["foo"],
merge() {
return "baz";
},
validate() { }
}
});
schema.defineStrategy({
name: "bar",
requires: ["foo"],
merge() {},
validate() {}
});

@@ -256,21 +226,23 @@ schema.validate({ foo: true, bar: true });

it("should throw an error when expected keys are found without required keys", () => {
schema.defineStrategy({
name: "foo",
merge() { },
validate() { }
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() { }
},
baz: {
merge() {
return "baz";
},
validate() { }
},
bar: {
name: "bar",
requires: ["foo", "baz"],
merge() { },
validate() { }
}
});
schema.defineStrategy({
name: "baz",
merge() { },
validate() { }
});
schema.defineStrategy({
name: "bar",
requires: ["foo", "baz"],
merge() { },
validate() { }
});
assert.throws(() => {

@@ -283,7 +255,11 @@ schema.validate({ bar: true });

it("should throw an error when an expected key is found but is invalid", () => {
schema.defineStrategy({
name: "foo",
merge() { },
validate() {
throw new Error("Invalid key: " + this.name);
schema = new ObjectSchema({
foo: {
merge() {
return "bar";
},
validate() {
throw new Error("Invalid key.");
}
}

@@ -294,12 +270,14 @@ });

schema.validate({ foo: true });
}, /Invalid key: foo/);
}, /Key "foo": Invalid key/);
});
it("should throw an error when a required key is missing", () => {
schema.defineStrategy({
name: "foo",
required: true,
merge() { },
validate() {
throw new Error("Invalid key: " + this.name);
schema = new ObjectSchema({
foo: {
required: true,
merge() {
return "bar";
},
validate() {}
}

@@ -306,0 +284,0 @@ });

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc