Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
value-object
Advanced tools
Value Object
- objects that matter only as the combination of their
properties. Two value objects with the same values for all their properties are
considered equal.
This library provides a convenient way to define strict, immutable value objects.
npm install value-object
Use subclasses to define value objects with type constraints:
const ValueObject = require('value-object')
class Currency extends ValueObject.define({
code: 'string',
name: 'string'
}) {}
class Money extends ValueObject.define({
currency: Currency,
amount: 'number'
}) {}
...or don't use classes, if you prefer:
const Money = ValueObject.define({
amount: 'number',
currency: { code: 'string' }
})
Use the new
keyword, passing values for each property:
const gbp = new Currency({ code: 'GBP', name: 'British Pounds' })
const price = new Money({ currency: gbp, amount: 12.34 })
const other = new Money({ currency: { code: 'USD', name: 'US Dollars' }, amount: 14.56 })
Constraints prevent value objects from being instantiated with invalid property values.
Property values with unexpected types are rejected:
> new Currency({ code: 'USD', name: 123 })
Error: Currency was constructed with invalid property values
Expected: { code:string, name:string }
Actual: { code:string, name:number }
name is invalid:
Expected string, was number
Value objects cannot be instantiated with unrecognised properties:
> new Currency({ code: 'NZD', name: 'New Zealand Dollars', colour: 'All black' })
Error: Currency was constructed with invalid property values
Expected: { code:string, name:string }
Actual: { code:string, name:string, colour:string }
colour is invalid:
Property is unexpected
Value objects cannot be instantiated with missing properties (unless they are optional):
> new Money({ amount: 123 })
Error: Money was constructed with invalid property values
Expected: { currency:Currency, amount:number }
Actual: { amount:number }
currency is invalid:
Property is missing
null
Properties can be set to null
:
> new Money({ currency: null, amount: null })
Money { currency: null, amount: null }
undefined
Properties cannot be set to undefined
(unless they are optional):
> new Money({ currency: null, amount: undefined })
Error: Money was constructed with invalid property values
Expected: { currency:Currency, amount:number }
Actual: { currency:null, amount:undefined }
amount is invalid:
Expected number, was undefined
Properties can be declared with built-in type constraints:
class Manager extends ValueObject.define({
firstName: 'string',
age: 'number',
trained: 'boolean',
subordinates: 'object',
preferences: 'any'
}) {}
string
: expects a value where typeof value === 'string'
number
: expects a value where typeof value === 'number'
boolean
: expects a value where typeof value === 'boolean'
object
: expects a value where typeof value === 'object'
any
: expects any non-null valueProperties declared with ?
can be set to null
or undefined
, or omitted
altogether:
class Options extends ValueObject.define({
age: 'number?',
aliases: 'object?',
colour: 'string?',
checked: 'boolean?'
}) {}
new Options({ age: null, aliases: {}, colour: undefined })
// => Options { age: null, aliases: {}, colour: undefined }
Optional properties can also be declared with ValueObject.optional()
:
class IceCream extends ValueObject.define({
flavours: ValueObject.optional(['string'])
}) {}
new IceCream({ flavours: ['mint', 'chocolate'] })
// => IceCream { flavours: [ 'mint', 'chocolate' ] }
new IceCream({})
// => IceCream {}
Arrays with arbitrary elements can be declared with the type Array
:
class Person extends ValueObject.define({
favouriteThings: Array
}) {}
new Person({ favouriteThings: ['cheese', 69, null] })
Arrays with value constraints are declared by wrapping the type definition (e.g.
'number'
, Date
) in []
:
class Point extends ValueObject.define({
x: 'number',
y: 'number'
}) {}
class Polygon extends ValueObject.define({
vertices: [Point] // instances of Point
}) {}
new Polygon({
vertices: [
new Point({ x: 1, y: 2 },
new Point({ x: 3, y: 4 }
)]
})
Custom property types can be defined with ValueObject.definePropertyType()
and
then used later by name in ValueObject.define()
:
ValueObject.definePropertyType('money', () => ({
coerce(value) {
if (typeof value === 'string') {
const parts = value.split(' ')
return { value: { amount: Number(parts[0]), currency: parts[1] } }
}
return { failure: 'Only string values allowed' }
},
areEqual(a, b) {
return a.currency == b.currency && a.amount == b.amount
},
describe() {
return '<money>'
}
}))
class Allowance extends ValueObject.define({ cash: 'money' }) {}
Property constraints are expressed as a function that returns a value with the following methods:
.coerce(value)
converts an arbitrary value to the final property value.
Expected to return { value }
when converting the property value is successful or { failure }
with a message when converting fails..areEqual(a, b)
returns true
if two instances of the type are considered equal, or false
otherwise..describe()
returns a string used in error messages mentioning the property.The constraint is used to convert property values from other types according to its
.coerce(value)
method:
> new Allowance({ cash: '123.00 GBP' })
Allowance { cash: { amount: 123, currency: 'GBP' } }
...and its .describe()
method is used in error messages:
> new Allowance({ cash: 666 })
Error: Allowance was constructed with invalid property values
Expected: { cash:<money> }
Actual: { cash:number }
cash is invalid:
Only string values allowed
Value objects are considered to be equal if their properties are equal. Equality
of two objects is tested by calling valueObject.isEqualTo(otherValueObject)
:
gbp.isEqualTo(new Currency({ code: 'GBP', name: 'British Pounds' }))
// => true
gbp.isEqualTo(new Currency({ code: 'EUR', name: 'Euros' }))
// => false
const gbpPrice = new Money({ amount: 123, currency: gbp })
const eurPrice = new Money({ amount: 123, currency: eur })
gbpPrice.isEqualTo(eurPrice)
// => false
eurPrice.isEqualTo(new Money({ amount: 123, currency: eur }))
// => true
ValueObject types have a schema
property that allows reflection over
properties and arbitrary metadata associated with each property:
class Product extends ValueObject.define({
name: 'string',
stockLevel: ValueObject.property('number', {
default: 0,
description: 'units in stock'
})
}) {}
> Product.schema.properties.stockLevel
Property {
constraint: Primitive { cast: [Function: Number], name: 'number' },
metadata: { default: 0, description: 'units in stock' },
optional: false }
Use with(newAttributes)
to create new value objects, with new values for a
specific set of properties:
const salePrice = price.with({ amount: 12.0 })
salePrice.currency.code
// => 'GBP'
Use toPlainObject()
to create a plain old mutable object from a value object's
property values:
> JSON.stringify(gbp.toPlainObject())
{ "code": "GBP", "name": "British Pounds" }
Any value-object instances will be converted using their schemas. Any objects
that are not value-object instances will be cloned using
JSON.parse(JSON.stringify(object))
by default. Pass in an optional clone
function to override this behaviour:
valueObject.toPlainObject(fancyDeepCloneFunction)
Use toJSON()
to create an object with __type__
properties for subsequent
deserialization:
> JSON.stringify(gbp.toJSON())
{ "__type__": "Currency", "code": "GBP", "name": "British Pounds" }
Use ValueObject.deserializeForNamespaces()
to create a deserialize function
that can turn the resulting JSON string back into objects
const deserialize = ValueObject.deserializeForNamespaces([{ Currency }])
const gbp2 = deserialize('{"__type__":"Currency","code":"GBP","name":"British Pounds"}')
gbp2.isEqualTo(gbp)
// => true
Value objects cannot be updated. Use strict mode to throw errors when attempts to set property values are made.
gbp.code = 'USD'
// TypeError:Cannot assign to read only property 'amount' of object '#<Currency>
FAQs
value-object.js - simple value objects
We found that value-object demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.