superstruct
Advanced tools
Comparing version 0.12.2 to 0.13.0
@@ -5,2 +5,20 @@ # Changelog | ||
### `0.13.0` — December 11, 2020 | ||
###### NEW | ||
**Structs can now define an `entries` iterator for nested values.** Previously iterating through nested values was defined in a one-off manner inside certain structs, but this led to non-uniform support. Now, any struct can define an `entries` iterator that will cause nested values to be automatically coerced and validated. | ||
**Coercion receives `context` objects and supports nested values.** Previously context objects were only passed to the validations and refinements. But now the same context is passed to coercions too so you can implement more complex logic. And coercions are automatically applied to nested values thanks to the addition of `entries`. | ||
**Iteration logic has gotten simpler, and more performant.** The addition of the `entries` logic has enabled us to only ever iterate through a tree of values one time for coercion and validation, instead of once each. This should speed up most standard use cases. | ||
###### BREAKING | ||
**The `ctx.fail()` function has been removed.** Previously you'd use it to return more information about a failure inside a struct. Now you can simply return a partial failure object. | ||
**The `ctx.check()` function has been removed.** Previously you'd use it to validate nested objects in more complex struct shapes. Now you can use the new `entries` property for this instead. | ||
**The `context.struct` and `context.value` properties have been removed.** These properties were previously available, but unnecessary since anywhere you have the context object you will also know the `value` and the specific struct that is being validated. Keeping them around required extra unnecessary plumbing in the library that made composing structs much more difficult so they were removed. | ||
### `0.12.0` — November 24, 2020 | ||
@@ -7,0 +25,0 @@ |
@@ -6,3 +6,3 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
@@ -12,3 +12,3 @@ refinement: string | undefined; | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
path: Array<any>; | ||
}; | ||
@@ -25,11 +25,11 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
refinement: string | undefined; | ||
path: Array<number | string>; | ||
path: Array<any>; | ||
branch: Array<any>; | ||
failures: () => Array<Failure>; | ||
[x: string]: any; | ||
constructor(failure: Failure, moreFailures: IterableIterator<Failure>); | ||
constructor(failure: Failure, failures: () => Generator<Failure>); | ||
} | ||
//# sourceMappingURL=error.d.ts.map |
@@ -6,3 +6,3 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
@@ -12,3 +12,3 @@ refinement: string | undefined; | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
path: Array<any>; | ||
}; | ||
@@ -25,10 +25,10 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
refinement: string | undefined; | ||
path: Array<number | string>; | ||
path: Array<any>; | ||
branch: Array<any>; | ||
failures: () => Array<Failure>; | ||
[x: string]: any; | ||
constructor(failure: Failure, moreFailures: IterableIterator<Failure>); | ||
constructor(failure: Failure, failures: () => Generator<Failure>); | ||
} | ||
@@ -144,11 +144,17 @@ /** | ||
schema: S; | ||
coercer: Coercer; | ||
validator: Validator<T, S>; | ||
refiner: Refiner<T, S>; | ||
coercer: (value: unknown, context: Context) => unknown; | ||
validator: (value: unknown, context: Context) => Iterable<Failure>; | ||
refiner: (value: T, context: Context) => Iterable<Failure>; | ||
entries: (value: unknown, context: Context) => Iterable<[ | ||
string | number, | ||
unknown, | ||
Struct<any> | Struct<never> | ||
]>; | ||
constructor(props: { | ||
type: Struct<T, S>["type"]; | ||
schema: Struct<T, S>["schema"]; | ||
coercer?: Struct<T, S>["coercer"]; | ||
validator?: Struct<T, S>["validator"]; | ||
refiner?: Struct<T, S>["refiner"]; | ||
type: string; | ||
schema: S; | ||
coercer?: Coercer; | ||
validator?: Validator; | ||
refiner?: Refiner<T>; | ||
entries?: Struct<T, S>["entries"]; | ||
}); | ||
@@ -191,12 +197,37 @@ /** | ||
/** | ||
* A `StructContext` contains information about the current value being | ||
* validated as well as helper functions for failures and recursive validating. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
type Context<T, S> = { | ||
value: any; | ||
struct: Struct<T, S>; | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a struct. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* A `Context` contains information about the current location of the | ||
* validation inside the initial input value. | ||
*/ | ||
type Context = { | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
fail: (props?: string | Partial<Failure>) => Failure; | ||
check: <Y, Z>(value: any, struct: Struct<Y, Z>, parent?: any, key?: string | number) => IterableIterator<Failure>; | ||
path: Array<any>; | ||
}; | ||
@@ -214,11 +245,11 @@ /** | ||
*/ | ||
type Result = boolean | string | Iterable<Failure>; | ||
type Result = boolean | string | Partial<Failure> | Iterable<boolean | string | Partial<Failure>>; | ||
/** | ||
* A `Coercer` takes an unknown value and optionally coerces it. | ||
*/ | ||
type Coercer = (value: unknown) => unknown; | ||
type Coercer<T = unknown> = (value: T, context: Context) => unknown; | ||
/** | ||
* A `Validate` takes an unknown value and validates it. | ||
* A `Validator` takes an unknown value and validates it. | ||
*/ | ||
type Validator<T, S> = (value: unknown, context: Context<T, S>) => Result; | ||
type Validator = (value: unknown, context: Context) => Result; | ||
/** | ||
@@ -228,32 +259,4 @@ * A `Refiner` takes a value of a known type and validates it against a further | ||
*/ | ||
type Refiner<T, S> = (value: T, context: Context<T, S>) => Result; | ||
type Refiner<T> = (value: T, context: Context) => Result; | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
*/ | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a `Struct`. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* Augment a `Struct` to add an additional coercion step to its input. | ||
@@ -268,3 +271,3 @@ * | ||
*/ | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: (value: C) => any): Struct<T, S>; | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: Coercer<C>): Struct<T, S>; | ||
/** | ||
@@ -276,3 +279,3 @@ * Augment a struct to replace `undefined` values with a default. | ||
*/ | ||
declare function defaulted<T, S>(S: Struct<T, S>, fallback: any, options?: { | ||
declare function defaulted<T, S>(struct: Struct<T, S>, fallback: any, options?: { | ||
strict?: boolean; | ||
@@ -325,3 +328,3 @@ }): Struct<T, S>; | ||
*/ | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T, S>): Struct<T, S>; | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T>): Struct<T, S>; | ||
/** | ||
@@ -1203,5 +1206,5 @@ * Ensure that any value passes validation. | ||
*/ | ||
declare function define<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
declare function define<T>(name: string, validator: Validator): Struct<T, null>; | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -1212,5 +1215,5 @@ * The callback will receive the value currently being validated, and must | ||
*/ | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context<T, null>) => Struct<T, any>): Struct<T, null>; | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context) => Struct<T, any>): Struct<T, null>; | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -1249,3 +1252,3 @@ * The first time validation is run with the struct, the callback will be called | ||
*/ | ||
declare function struct<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
export { Failure, StructError, Struct, Context, Infer, Describe, Result, Coercer, Validator, Refiner, assert, create, mask, is, validate, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; | ||
declare function struct<T>(name: string, validator: Validator): Struct<T, null>; | ||
export { Failure, StructError, Struct, assert, create, mask, is, validate, Context, Infer, Describe, Result, Coercer, Validator, Refiner, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; |
@@ -6,3 +6,3 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
@@ -12,3 +12,3 @@ refinement: string | undefined; | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
path: Array<any>; | ||
}; | ||
@@ -25,10 +25,10 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
refinement: string | undefined; | ||
path: Array<number | string>; | ||
path: Array<any>; | ||
branch: Array<any>; | ||
failures: () => Array<Failure>; | ||
[x: string]: any; | ||
constructor(failure: Failure, moreFailures: IterableIterator<Failure>); | ||
constructor(failure: Failure, failures: () => Generator<Failure>); | ||
} | ||
@@ -144,11 +144,17 @@ /** | ||
schema: S; | ||
coercer: Coercer; | ||
validator: Validator<T, S>; | ||
refiner: Refiner<T, S>; | ||
coercer: (value: unknown, context: Context) => unknown; | ||
validator: (value: unknown, context: Context) => Iterable<Failure>; | ||
refiner: (value: T, context: Context) => Iterable<Failure>; | ||
entries: (value: unknown, context: Context) => Iterable<[ | ||
string | number, | ||
unknown, | ||
Struct<any> | Struct<never> | ||
]>; | ||
constructor(props: { | ||
type: Struct<T, S>["type"]; | ||
schema: Struct<T, S>["schema"]; | ||
coercer?: Struct<T, S>["coercer"]; | ||
validator?: Struct<T, S>["validator"]; | ||
refiner?: Struct<T, S>["refiner"]; | ||
type: string; | ||
schema: S; | ||
coercer?: Coercer; | ||
validator?: Validator; | ||
refiner?: Refiner<T>; | ||
entries?: Struct<T, S>["entries"]; | ||
}); | ||
@@ -191,12 +197,37 @@ /** | ||
/** | ||
* A `StructContext` contains information about the current value being | ||
* validated as well as helper functions for failures and recursive validating. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
type Context<T, S> = { | ||
value: any; | ||
struct: Struct<T, S>; | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a struct. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* A `Context` contains information about the current location of the | ||
* validation inside the initial input value. | ||
*/ | ||
type Context = { | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
fail: (props?: string | Partial<Failure>) => Failure; | ||
check: <Y, Z>(value: any, struct: Struct<Y, Z>, parent?: any, key?: string | number) => IterableIterator<Failure>; | ||
path: Array<any>; | ||
}; | ||
@@ -214,11 +245,11 @@ /** | ||
*/ | ||
type Result = boolean | string | Iterable<Failure>; | ||
type Result = boolean | string | Partial<Failure> | Iterable<boolean | string | Partial<Failure>>; | ||
/** | ||
* A `Coercer` takes an unknown value and optionally coerces it. | ||
*/ | ||
type Coercer = (value: unknown) => unknown; | ||
type Coercer<T = unknown> = (value: T, context: Context) => unknown; | ||
/** | ||
* A `Validate` takes an unknown value and validates it. | ||
* A `Validator` takes an unknown value and validates it. | ||
*/ | ||
type Validator<T, S> = (value: unknown, context: Context<T, S>) => Result; | ||
type Validator = (value: unknown, context: Context) => Result; | ||
/** | ||
@@ -228,32 +259,4 @@ * A `Refiner` takes a value of a known type and validates it against a further | ||
*/ | ||
type Refiner<T, S> = (value: T, context: Context<T, S>) => Result; | ||
type Refiner<T> = (value: T, context: Context) => Result; | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
*/ | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a `Struct`. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* Augment a `Struct` to add an additional coercion step to its input. | ||
@@ -268,3 +271,3 @@ * | ||
*/ | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: (value: C) => any): Struct<T, S>; | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: Coercer<C>): Struct<T, S>; | ||
/** | ||
@@ -276,3 +279,3 @@ * Augment a struct to replace `undefined` values with a default. | ||
*/ | ||
declare function defaulted<T, S>(S: Struct<T, S>, fallback: any, options?: { | ||
declare function defaulted<T, S>(struct: Struct<T, S>, fallback: any, options?: { | ||
strict?: boolean; | ||
@@ -325,3 +328,3 @@ }): Struct<T, S>; | ||
*/ | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T, S>): Struct<T, S>; | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T>): Struct<T, S>; | ||
/** | ||
@@ -1203,5 +1206,5 @@ * Ensure that any value passes validation. | ||
*/ | ||
declare function define<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
declare function define<T>(name: string, validator: Validator): Struct<T, null>; | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -1212,5 +1215,5 @@ * The callback will receive the value currently being validated, and must | ||
*/ | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context<T, null>) => Struct<T, any>): Struct<T, null>; | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context) => Struct<T, any>): Struct<T, null>; | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -1249,3 +1252,3 @@ * The first time validation is run with the struct, the callback will be called | ||
*/ | ||
declare function struct<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
export { Failure, StructError, Struct, Context, Infer, Describe, Result, Coercer, Validator, Refiner, assert, create, mask, is, validate, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; | ||
declare function struct<T>(name: string, validator: Validator): Struct<T, null>; | ||
export { Failure, StructError, Struct, assert, create, mask, is, validate, Context, Infer, Describe, Result, Coercer, Validator, Refiner, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; |
@@ -14,3 +14,4 @@ /** | ||
class StructError extends TypeError { | ||
constructor(failure, moreFailures) { | ||
constructor(failure, failures) { | ||
let cached; | ||
const { | ||
@@ -23,3 +24,2 @@ message, | ||
} = failure; | ||
let failures; | ||
const msg = path.length === 0 ? message : "At path: " + path.join('.') + " -- " + message; | ||
@@ -31,7 +31,5 @@ super(msg); | ||
this.failures = () => { | ||
if (!failures) { | ||
failures = [failure, ...moreFailures]; | ||
} | ||
var _cached; | ||
return failures; | ||
return (_cached = cached) != null ? _cached : cached = [failure, ...failures()]; | ||
}; | ||
@@ -43,10 +41,25 @@ } | ||
/** | ||
* Check if a value is an iterator. | ||
*/ | ||
function isIterable(x) { | ||
return isObject(x) && typeof x[Symbol.iterator] === 'function'; | ||
} | ||
/** | ||
* Check if a value is a plain object. | ||
*/ | ||
function isPlainObject(value) { | ||
if (Object.prototype.toString.call(value) !== '[object Object]') { | ||
function isObject(x) { | ||
return typeof x === 'object' && x != null; | ||
} | ||
/** | ||
* Check if a value is a plain object. | ||
*/ | ||
function isPlainObject(x) { | ||
if (Object.prototype.toString.call(x) !== '[object Object]') { | ||
return false; | ||
} | ||
const prototype = Object.getPrototypeOf(value); | ||
const prototype = Object.getPrototypeOf(x); | ||
return prototype === null || prototype === Object.prototype; | ||
@@ -74,19 +87,121 @@ } | ||
/** | ||
* Convert a validation result to an iterable of failures. | ||
* Convert a single validation result to a failure. | ||
*/ | ||
function* toFailures(result, context) { | ||
if (typeof result === 'string') { | ||
yield context.fail({ | ||
message: result | ||
}); | ||
} else if (result === true) { | ||
function toFailure(result, context, struct, value) { | ||
if (result === true) { | ||
return; | ||
} else if (result === false) { | ||
yield context.fail(); | ||
} else { | ||
yield* result; | ||
result = {}; | ||
} else if (typeof result === 'string') { | ||
result = { | ||
message: result | ||
}; | ||
} | ||
const { | ||
path, | ||
branch | ||
} = context; | ||
const { | ||
type | ||
} = struct; | ||
const { | ||
refinement, | ||
message = "Expected a value of type `" + type + "`" + (refinement ? " with refinement `" + refinement + "`" : '') + ", but received: `" + print(value) + "`" | ||
} = result; | ||
return { | ||
value, | ||
type, | ||
refinement, | ||
key: path[path.length - 1], | ||
path, | ||
branch, | ||
...result, | ||
message | ||
}; | ||
} | ||
/** | ||
* Convert a validation result to an iterable of failures. | ||
*/ | ||
function* toFailures(result, context, struct, value) { | ||
if (!isIterable(result)) { | ||
result = [result]; | ||
} | ||
for (const r of result) { | ||
const failure = toFailure(r, context, struct, value); | ||
if (failure) { | ||
yield failure; | ||
} | ||
} | ||
} | ||
/** | ||
* Check a value against a struct, traversing deeply into nested values, and | ||
* returning an iterator of failures or success. | ||
*/ | ||
function* run(value, struct, options = {}) { | ||
const { | ||
path = [], | ||
branch = [value], | ||
coerce = false | ||
} = options; | ||
const ctx = { | ||
path, | ||
branch | ||
}; | ||
if (coerce) { | ||
value = struct.coercer(value, ctx); | ||
} | ||
let valid = true; | ||
for (const failure of struct.validator(value, ctx)) { | ||
valid = false; | ||
yield [failure, undefined]; | ||
} | ||
if (valid) { | ||
for (const failure of struct.refiner(value, ctx)) { | ||
valid = false; | ||
yield [failure, undefined]; | ||
} | ||
} | ||
for (let [k, v, s] of struct.entries(value, ctx)) { | ||
const ts = run(v, s, { | ||
path: k === undefined ? path : [...path, k], | ||
branch: k === undefined ? branch : [...branch, v], | ||
coerce | ||
}); | ||
for (const t of ts) { | ||
if (t[0]) { | ||
valid = false; | ||
yield [t[0], undefined]; | ||
} else if (coerce) { | ||
v = t[1]; | ||
if (k === undefined) { | ||
value = v; | ||
} else if (value instanceof Map) { | ||
value.set(k, v); | ||
} else if (value instanceof Set) { | ||
value.add(v); | ||
} else if (isObject(value)) { | ||
value[k] = v; | ||
} | ||
} | ||
} | ||
} | ||
if (valid) { | ||
yield [undefined, value]; | ||
} | ||
} | ||
function assign(...Structs) { | ||
@@ -109,3 +224,3 @@ const schemas = Structs.map(s => s.schema); | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -118,8 +233,25 @@ * The callback will receive the value currently being validated, and must | ||
function dynamic(fn) { | ||
return define('dynamic', (value, ctx) => { | ||
return ctx.check(value, fn(value, ctx)); | ||
return new Struct({ | ||
type: 'dynamic', | ||
schema: null, | ||
*entries(value, ctx) { | ||
const struct = fn(value, ctx); | ||
yield* struct.entries(value, ctx); | ||
}, | ||
validator(value, ctx) { | ||
const struct = fn(value, ctx); | ||
return struct.validator(value, ctx); | ||
}, | ||
coercer(value, ctx) { | ||
const struct = fn(value, ctx); | ||
return struct.coercer(value, ctx); | ||
} | ||
}); | ||
} | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -133,9 +265,28 @@ * The first time validation is run with the struct, the callback will be called | ||
function lazy(fn) { | ||
let s; | ||
return define('lazy', (value, ctx) => { | ||
if (!s) { | ||
s = fn(); | ||
let struct; | ||
return new Struct({ | ||
type: 'lazy', | ||
schema: null, | ||
*entries(value, ctx) { | ||
var _struct; | ||
(_struct = struct) != null ? _struct : struct = fn(); | ||
yield* struct.entries(value, ctx); | ||
}, | ||
validator(value, ctx) { | ||
var _struct2; | ||
(_struct2 = struct) != null ? _struct2 : struct = fn(); | ||
return struct.validator(value, ctx); | ||
}, | ||
coercer(value, ctx) { | ||
var _struct3; | ||
(_struct3 = struct) != null ? _struct3 : struct = fn(); | ||
return struct.coercer(value, ctx); | ||
} | ||
return ctx.check(value, s); | ||
}); | ||
@@ -222,14 +373,17 @@ } | ||
schema: Element, | ||
coercer: value => { | ||
return Element && Array.isArray(value) ? value.map(v => Element.coercer(v)) : value; | ||
}, | ||
*validator(value, ctx) { | ||
if (!Array.isArray(value)) { | ||
yield ctx.fail("Expected an array value, but received: " + print(value)); | ||
} else if (Element) { | ||
*entries(value) { | ||
if (Element && Array.isArray(value)) { | ||
for (const [i, v] of value.entries()) { | ||
yield* ctx.check(v, Element, value, i); | ||
yield [i, v, Element]; | ||
} | ||
} | ||
}, | ||
coercer(value) { | ||
return Array.isArray(value) ? value.slice() : value; | ||
}, | ||
validator(value) { | ||
return Array.isArray(value) || "Expected an array value, but received: " + print(value); | ||
} | ||
@@ -271,5 +425,7 @@ | ||
schema, | ||
validator: value => { | ||
validator(value) { | ||
return values.includes(value) || "Expected one of `" + description + "`, but received: " + print(value); | ||
} | ||
}); | ||
@@ -305,6 +461,18 @@ } | ||
function intersection(Structs) { | ||
return define('intersection', function* (value, ctx) { | ||
for (const S of Structs) { | ||
yield* ctx.check(value, S); | ||
return new Struct({ | ||
type: 'intersection', | ||
schema: null, | ||
*entries(value, ctx) { | ||
for (const S of Structs) { | ||
yield* S.entries(value, ctx); | ||
} | ||
}, | ||
*validator(value, ctx) { | ||
for (const S of Structs) { | ||
yield* S.validator(value, ctx); | ||
} | ||
} | ||
}); | ||
@@ -319,11 +487,23 @@ } | ||
function map(Key, Value) { | ||
return define('map', function* (value, ctx) { | ||
if (!(value instanceof Map)) { | ||
yield ctx.fail("Expected a `Map` object, but received: " + print(value)); | ||
} else if (Key && Value) { | ||
for (const [k, v] of value.entries()) { | ||
yield* ctx.check(k, Key, value, k); | ||
yield* ctx.check(v, Value, value, k); | ||
return new Struct({ | ||
type: 'map', | ||
schema: null, | ||
*entries(value) { | ||
if (Key && Value && value instanceof Map) { | ||
for (const [k, v] of value.entries()) { | ||
yield [k, k, Key]; | ||
yield [k, v, Value]; | ||
} | ||
} | ||
}, | ||
coercer(value) { | ||
return value instanceof Map ? new Map(value) : value; | ||
}, | ||
validator(value) { | ||
return value instanceof Map || "Expected a `Map` object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -343,17 +523,5 @@ } | ||
function nullable(struct) { | ||
const { | ||
refiner | ||
} = struct; | ||
return new Struct({ ...struct, | ||
validator: (value, ctx) => { | ||
return value === null || ctx.check(value, struct); | ||
}, | ||
refiner: function* (value, ctx) { | ||
if (value != null) { | ||
const c = { ...ctx, | ||
struct | ||
}; | ||
yield* toFailures(refiner(value, c), c); | ||
} | ||
} | ||
validator: (value, ctx) => value === null || struct.validator(value, ctx), | ||
refiner: (value, ctx) => value === null || struct.refiner(value, ctx) | ||
}); | ||
@@ -377,6 +545,4 @@ } | ||
*validator(value, ctx) { | ||
if (typeof value !== 'object' || value == null) { | ||
yield ctx.fail("Expected an object, but received: " + print(value)); | ||
} else if (schema) { | ||
*entries(value) { | ||
if (schema && isObject(value)) { | ||
const unknowns = new Set(Object.keys(value)); | ||
@@ -386,10 +552,7 @@ | ||
unknowns.delete(key); | ||
const Value = schema[key]; | ||
const v = value[key]; | ||
yield* ctx.check(v, Value, value, key); | ||
yield [key, value[key], schema[key]]; | ||
} | ||
for (const key of unknowns) { | ||
const v = value[key]; | ||
yield* ctx.check(v, Never, value, key); | ||
yield [key, value[key], Never]; | ||
} | ||
@@ -399,23 +562,11 @@ } | ||
coercer: value => { | ||
if (!schema || typeof value !== 'object' || value == null) { | ||
return value; | ||
} | ||
validator(value) { | ||
return isObject(value) || "Expected an object, but received: " + print(value); | ||
}, | ||
const ret = {}; | ||
const unknowns = new Set(Object.keys(value)); | ||
coercer(value) { | ||
return isObject(value) ? { ...value | ||
} : value; | ||
} | ||
for (const key of knowns) { | ||
unknowns.delete(key); | ||
const Value = schema[key]; | ||
const v = value[key]; | ||
ret[key] = Value.coercer(v); | ||
} | ||
for (const key of unknowns) { | ||
ret[key] = value[key]; | ||
} | ||
return ret; | ||
} | ||
}); | ||
@@ -428,17 +579,5 @@ } | ||
function optional(struct) { | ||
const { | ||
refiner | ||
} = struct; | ||
return new Struct({ ...struct, | ||
validator: (value, ctx) => { | ||
return value === undefined || ctx.check(value, struct); | ||
}, | ||
refiner: function* (value, ctx) { | ||
if (value != null) { | ||
const c = { ...ctx, | ||
struct | ||
}; | ||
yield* toFailures(refiner(value, c), c); | ||
} | ||
} | ||
validator: (value, ctx) => value === undefined || struct.validator(value, ctx), | ||
refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx) | ||
}); | ||
@@ -454,12 +593,20 @@ } | ||
function record(Key, Value) { | ||
return define('record', function* (value, ctx) { | ||
if (typeof value !== 'object' || value == null) { | ||
yield ctx.fail("Expected an object, but received: " + print(value)); | ||
} else { | ||
for (const k in value) { | ||
const v = value[k]; | ||
yield* ctx.check(k, Key, value, k); | ||
yield* ctx.check(v, Value, value, k); | ||
return new Struct({ | ||
type: 'record', | ||
schema: null, | ||
*entries(value) { | ||
if (isObject(value)) { | ||
for (const k in value) { | ||
const v = value[k]; | ||
yield [k, k, Key]; | ||
yield [k, v, Value]; | ||
} | ||
} | ||
}, | ||
validator(value) { | ||
return isObject(value) || "Expected an object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -480,10 +627,22 @@ } | ||
function set(Element) { | ||
return define('set', function* (value, ctx) { | ||
if (!(value instanceof Set)) { | ||
yield ctx.fail("Expected a `Set` object, but received: " + print(value)); | ||
} else if (Element) { | ||
for (const val of value) { | ||
yield* ctx.check(val, Element, value, val); | ||
return new Struct({ | ||
type: 'set', | ||
schema: null, | ||
*entries(value) { | ||
if (Element && value instanceof Set) { | ||
for (const v of value) { | ||
yield [v, v, Element]; | ||
} | ||
} | ||
}, | ||
coercer(value) { | ||
return value instanceof Set ? new Set(value) : value; | ||
}, | ||
validator(value) { | ||
return value instanceof Set || "Expected a `Set` object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -502,17 +661,20 @@ } | ||
const Never = never(); | ||
return define('tuple', function* (value, ctx) { | ||
if (!Array.isArray(value)) { | ||
yield ctx.fail("Expected an array, but received: " + print(value)); | ||
} else { | ||
for (const [index, Element] of Elements.entries()) { | ||
const v = value[index]; | ||
yield* ctx.check(v, Element, value, index); | ||
return new Struct({ | ||
type: 'tuple', | ||
schema: null, | ||
*entries(value) { | ||
if (Array.isArray(value)) { | ||
const length = Math.max(Elements.length, value.length); | ||
for (let i = 0; i < length; i++) { | ||
yield [i, value[i], Elements[i] || Never]; | ||
} | ||
} | ||
}, | ||
if (value.length > Elements.length) { | ||
const index = Elements.length; | ||
const v = value[index]; | ||
yield* ctx.check(v, Never, value, index); | ||
} | ||
validator(value) { | ||
return Array.isArray(value) || "Expected an array, but received: " + print(value); | ||
} | ||
}); | ||
@@ -532,13 +694,15 @@ } | ||
schema, | ||
validator: function* (value, ctx) { | ||
if (typeof value !== 'object' || value == null) { | ||
yield ctx.fail("Expected an object, but received: " + print(value)); | ||
} else { | ||
for (const key of keys) { | ||
const Value = schema[key]; | ||
const v = value[key]; | ||
yield* ctx.check(v, Value, value, key); | ||
*entries(value) { | ||
if (isObject(value)) { | ||
for (const k of keys) { | ||
yield [k, value[k], schema[k]]; | ||
} | ||
} | ||
}, | ||
validator(value) { | ||
return isObject(value) || "Expected an object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -548,17 +712,27 @@ } | ||
const description = Structs.map(s => s.type).join(' | '); | ||
return define('union', function* (value, ctx) { | ||
const failures = []; | ||
return new Struct({ | ||
type: 'union', | ||
schema: null, | ||
for (const S of Structs) { | ||
const [...array] = ctx.check(value, S); | ||
validator(value, ctx) { | ||
const failures = []; | ||
if (array.length === 0) { | ||
return; | ||
} else { | ||
failures.push(...array); | ||
for (const S of Structs) { | ||
const [...tuples] = run(value, S, ctx); | ||
const [first] = tuples; | ||
if (!first[0]) { | ||
return []; | ||
} else { | ||
for (const [failure] of tuples) { | ||
if (failure) { | ||
failures.push(failure); | ||
} | ||
} | ||
} | ||
} | ||
return ["Expected the value to satisfy a union of `" + description + "`, but received: " + print(value), ...failures]; | ||
} | ||
yield ctx.fail("Expected the value to satisfy a union of `" + description + "`, but received: " + print(value)); | ||
yield* failures; | ||
}); | ||
@@ -586,10 +760,5 @@ } | ||
function coerce(struct, condition, coercer) { | ||
const fn = struct.coercer; | ||
return new Struct({ ...struct, | ||
coercer: value => { | ||
if (is(value, condition)) { | ||
return fn(coercer(value)); | ||
} else { | ||
return fn(value); | ||
} | ||
coercer: (value, ctx) => { | ||
return is(value, condition) ? struct.coercer(coercer(value, ctx), ctx) : struct.coercer(value, ctx); | ||
} | ||
@@ -605,7 +774,4 @@ }); | ||
function defaulted(S, fallback, options = {}) { | ||
const { | ||
strict | ||
} = options; | ||
return coerce(S, unknown(), x => { | ||
function defaulted(struct, fallback, options = {}) { | ||
return coerce(struct, unknown(), x => { | ||
const f = typeof fallback === 'function' ? fallback() : fallback; | ||
@@ -617,3 +783,3 @@ | ||
if (!strict && isPlainObject(x) && isPlainObject(f)) { | ||
if (!options.strict && isPlainObject(x) && isPlainObject(f)) { | ||
const ret = { ...x | ||
@@ -681,10 +847,32 @@ }; | ||
constructor(props) { | ||
this.type = props.type; | ||
this.schema = props.schema; | ||
const { | ||
type, | ||
schema, | ||
validator, | ||
refiner, | ||
coercer = value => value, | ||
entries = function* () {} | ||
} = props; | ||
this.type = type; | ||
this.schema = schema; | ||
this.entries = entries; | ||
this.coercer = coercer; | ||
this.coercer = props.coercer || (value => value); | ||
if (validator) { | ||
this.validator = (value, context) => { | ||
const result = validator(value, context); | ||
return toFailures(result, context, this, value); | ||
}; | ||
} else { | ||
this.validator = () => []; | ||
} | ||
this.validator = props.validator || (() => []); | ||
this.refiner = props.refiner || (() => []); | ||
if (refiner) { | ||
this.refiner = (value, context) => { | ||
const result = refiner(value, context); | ||
return toFailures(result, context, this, value); | ||
}; | ||
} else { | ||
this.refiner = () => []; | ||
} | ||
} | ||
@@ -740,3 +928,3 @@ /** | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
@@ -752,12 +940,18 @@ | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
function create(value, struct) { | ||
const ret = struct.coercer(value); | ||
assert(ret, struct); | ||
return ret; | ||
const result = validate(value, struct, { | ||
coerce: true | ||
}); | ||
if (result[0]) { | ||
throw result[0]; | ||
} else { | ||
return result[1]; | ||
} | ||
} | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
@@ -771,3 +965,3 @@ | ||
/** | ||
* Check if a value passes a `Struct`. | ||
* Check if a value passes a struct. | ||
*/ | ||
@@ -780,79 +974,25 @@ | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
function validate(value, struct, options = {}) { | ||
if (options.coerce) { | ||
value = struct.coercer(value); | ||
} | ||
const tuples = run(value, struct, options); | ||
const tuple = shiftIterator(tuples); | ||
const failures = check(value, struct); | ||
const failure = shiftIterator(failures); | ||
if (failure) { | ||
const error = new StructError(failure, failures); | ||
if (tuple[0]) { | ||
const error = new StructError(tuple[0], function* () { | ||
for (const t of tuples) { | ||
if (t[0]) { | ||
yield t[0]; | ||
} | ||
} | ||
}); | ||
return [error, undefined]; | ||
} else { | ||
return [undefined, value]; | ||
const v = tuple[1]; | ||
return [undefined, v]; | ||
} | ||
} | ||
/** | ||
* Check a value against a `Struct`, returning an iterable of failures. | ||
*/ | ||
function* check(value, struct, path = [], branch = []) { | ||
const ctx = { | ||
value, | ||
struct, | ||
branch, | ||
path, | ||
check(v, s, parent, key) { | ||
const p = parent !== undefined ? [...path, key] : path; | ||
const b = parent !== undefined ? [...branch, parent] : branch; | ||
return check(v, s, p, b); | ||
}, | ||
fail(props = {}) { | ||
if (typeof props === 'string') { | ||
props = { | ||
message: props | ||
}; | ||
} | ||
const { | ||
type | ||
} = struct; | ||
let { | ||
message, | ||
refinement | ||
} = props; | ||
if (!message) { | ||
message = "Expected a value of type `" + type + "`" + (refinement ? " with refinement `" + refinement + "`" : '') + (path.length ? " for `" + path.join('.') + "`" : '') + ", but received: `" + print(value) + "`"; | ||
} | ||
return { ...props, | ||
value, | ||
type, | ||
refinement, | ||
message, | ||
key: path[path.length - 1], | ||
path, | ||
branch: [...branch, value] | ||
}; | ||
} | ||
}; | ||
const failures = toFailures(struct.validator(value, ctx), ctx); | ||
const failure = shiftIterator(failures); | ||
if (failure) { | ||
yield failure; | ||
yield* failures; | ||
} else { | ||
yield* toFailures(struct.refiner(value, ctx), ctx); | ||
} | ||
} | ||
/** | ||
@@ -943,9 +1083,10 @@ * Ensure that a string, array, map, or set is empty. | ||
function refine(struct, name, refiner) { | ||
const fn = struct.refiner; | ||
return new Struct({ ...struct, | ||
*refiner(value, ctx) { | ||
yield* toFailures(fn(value, ctx), ctx); | ||
yield* struct.refiner(value, ctx); | ||
const result = refiner(value, ctx); | ||
const failures = toFailures(result, ctx, struct, value); | ||
for (const failure of toFailures(refiner(value, ctx), ctx)) { | ||
for (const failure of failures) { | ||
yield { ...failure, | ||
@@ -952,0 +1093,0 @@ refinement: name |
@@ -12,11 +12,13 @@ import { StructSchema } from './utils'; | ||
schema: S; | ||
coercer: Coercer; | ||
validator: Validator<T, S>; | ||
refiner: Refiner<T, S>; | ||
coercer: (value: unknown, context: Context) => unknown; | ||
validator: (value: unknown, context: Context) => Iterable<Failure>; | ||
refiner: (value: T, context: Context) => Iterable<Failure>; | ||
entries: (value: unknown, context: Context) => Iterable<[string | number, unknown, Struct<any> | Struct<never>]>; | ||
constructor(props: { | ||
type: Struct<T, S>['type']; | ||
schema: Struct<T, S>['schema']; | ||
coercer?: Struct<T, S>['coercer']; | ||
validator?: Struct<T, S>['validator']; | ||
refiner?: Struct<T, S>['refiner']; | ||
type: string; | ||
schema: S; | ||
coercer?: Coercer; | ||
validator?: Validator; | ||
refiner?: Refiner<T>; | ||
entries?: Struct<T, S>['entries']; | ||
}); | ||
@@ -53,12 +55,31 @@ /** | ||
/** | ||
* A `StructContext` contains information about the current value being | ||
* validated as well as helper functions for failures and recursive validating. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
export declare type Context<T, S> = { | ||
value: any; | ||
struct: Struct<T, S>; | ||
export declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
export declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
export declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a struct. | ||
*/ | ||
export declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
export declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [StructError, undefined] | [undefined, T]; | ||
/** | ||
* A `Context` contains information about the current location of the | ||
* validation inside the initial input value. | ||
*/ | ||
export declare type Context = { | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
fail: (props?: string | Partial<Failure>) => Failure; | ||
check: <Y, Z>(value: any, struct: Struct<Y, Z>, parent?: any, key?: string | number) => IterableIterator<Failure>; | ||
path: Array<any>; | ||
}; | ||
@@ -76,11 +97,11 @@ /** | ||
*/ | ||
export declare type Result = boolean | string | Iterable<Failure>; | ||
export declare type Result = boolean | string | Partial<Failure> | Iterable<boolean | string | Partial<Failure>>; | ||
/** | ||
* A `Coercer` takes an unknown value and optionally coerces it. | ||
*/ | ||
export declare type Coercer = (value: unknown) => unknown; | ||
export declare type Coercer<T = unknown> = (value: T, context: Context) => unknown; | ||
/** | ||
* A `Validate` takes an unknown value and validates it. | ||
* A `Validator` takes an unknown value and validates it. | ||
*/ | ||
export declare type Validator<T, S> = (value: unknown, context: Context<T, S>) => Result; | ||
export declare type Validator = (value: unknown, context: Context) => Result; | ||
/** | ||
@@ -90,25 +111,3 @@ * A `Refiner` takes a value of a known type and validates it against a further | ||
*/ | ||
export declare type Refiner<T, S> = (value: T, context: Context<T, S>) => Result; | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
*/ | ||
export declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
*/ | ||
export declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
*/ | ||
export declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a `Struct`. | ||
*/ | ||
export declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
*/ | ||
export declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [StructError, undefined] | [undefined, T]; | ||
export declare type Refiner<T> = (value: T, context: Context) => Result; | ||
//# sourceMappingURL=struct.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { Struct } from '../struct'; | ||
import { Struct, Coercer } from '../struct'; | ||
/** | ||
@@ -12,3 +12,3 @@ * Augment a `Struct` to add an additional coercion step to its input. | ||
*/ | ||
export declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: (value: C) => any): Struct<T, S>; | ||
export declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: Coercer<C>): Struct<T, S>; | ||
/** | ||
@@ -20,3 +20,3 @@ * Augment a struct to replace `undefined` values with a default. | ||
*/ | ||
export declare function defaulted<T, S>(S: Struct<T, S>, fallback: any, options?: { | ||
export declare function defaulted<T, S>(struct: Struct<T, S>, fallback: any, options?: { | ||
strict?: boolean; | ||
@@ -23,0 +23,0 @@ }): Struct<T, S>; |
@@ -33,3 +33,3 @@ import { Struct, Refiner } from '../struct'; | ||
*/ | ||
export declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T, S>): Struct<T, S>; | ||
export declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T>): Struct<T, S>; | ||
//# sourceMappingURL=refinements.d.ts.map |
@@ -16,5 +16,5 @@ import { Struct, Context, Validator } from '../struct'; | ||
*/ | ||
export declare function define<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
export declare function define<T>(name: string, validator: Validator): Struct<T, null>; | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -25,5 +25,5 @@ * The callback will receive the value currently being validated, and must | ||
*/ | ||
export declare function dynamic<T>(fn: (value: unknown, ctx: Context<T, null>) => Struct<T, any>): Struct<T, null>; | ||
export declare function dynamic<T>(fn: (value: unknown, ctx: Context) => Struct<T, any>): Struct<T, null>; | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -62,3 +62,3 @@ * The first time validation is run with the struct, the callback will be called | ||
*/ | ||
export declare function struct<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
export declare function struct<T>(name: string, validator: Validator): Struct<T, null>; | ||
//# sourceMappingURL=utilities.d.ts.map |
@@ -6,3 +6,7 @@ import { Struct, Infer, Result, Context, Describe } from './struct'; | ||
*/ | ||
export declare function isPlainObject(value: unknown): value is { | ||
export declare function isObject(x: unknown): x is object; | ||
/** | ||
* Check if a value is a plain object. | ||
*/ | ||
export declare function isPlainObject(x: unknown): x is { | ||
[key: string]: any; | ||
@@ -20,6 +24,19 @@ }; | ||
/** | ||
* Convert a single validation result to a failure. | ||
*/ | ||
export declare function toFailure<T, S>(result: string | boolean | Partial<Failure>, context: Context, struct: Struct<T, S>, value: any): Failure | undefined; | ||
/** | ||
* Convert a validation result to an iterable of failures. | ||
*/ | ||
export declare function toFailures<T, S>(result: Result, context: Context<T, S>): IterableIterator<Failure>; | ||
export declare function toFailures<T, S>(result: Result, context: Context, struct: Struct<T, S>, value: any): IterableIterator<Failure>; | ||
/** | ||
* Check a value against a struct, traversing deeply into nested values, and | ||
* returning an iterator of failures or success. | ||
*/ | ||
export declare function run<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
path?: any[]; | ||
branch?: any[]; | ||
coerce?: boolean; | ||
}): IterableIterator<[Failure, undefined] | [undefined, T]>; | ||
/** | ||
* Check if a type is a tuple. | ||
@@ -26,0 +43,0 @@ */ |
@@ -5,3 +5,3 @@ { | ||
"description": "A simple and composable way to validate data in JavaScript (and TypeScript).", | ||
"version": "0.12.2", | ||
"version": "0.13.0", | ||
"license": "MIT", | ||
@@ -8,0 +8,0 @@ "repository": "git://github.com/ianstormtaylor/superstruct.git", |
@@ -6,3 +6,3 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
@@ -12,3 +12,3 @@ refinement: string | undefined; | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
path: Array<any>; | ||
}; | ||
@@ -25,10 +25,10 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
refinement: string | undefined; | ||
path: Array<number | string>; | ||
path: Array<any>; | ||
branch: Array<any>; | ||
failures: () => Array<Failure>; | ||
[x: string]: any; | ||
constructor(failure: Failure, moreFailures: IterableIterator<Failure>); | ||
constructor(failure: Failure, failures: () => Generator<Failure>); | ||
} | ||
@@ -144,11 +144,17 @@ /** | ||
schema: S; | ||
coercer: Coercer; | ||
validator: Validator<T, S>; | ||
refiner: Refiner<T, S>; | ||
coercer: (value: unknown, context: Context) => unknown; | ||
validator: (value: unknown, context: Context) => Iterable<Failure>; | ||
refiner: (value: T, context: Context) => Iterable<Failure>; | ||
entries: (value: unknown, context: Context) => Iterable<[ | ||
string | number, | ||
unknown, | ||
Struct<any> | Struct<never> | ||
]>; | ||
constructor(props: { | ||
type: Struct<T, S>["type"]; | ||
schema: Struct<T, S>["schema"]; | ||
coercer?: Struct<T, S>["coercer"]; | ||
validator?: Struct<T, S>["validator"]; | ||
refiner?: Struct<T, S>["refiner"]; | ||
type: string; | ||
schema: S; | ||
coercer?: Coercer; | ||
validator?: Validator; | ||
refiner?: Refiner<T>; | ||
entries?: Struct<T, S>["entries"]; | ||
}); | ||
@@ -191,12 +197,37 @@ /** | ||
/** | ||
* A `StructContext` contains information about the current value being | ||
* validated as well as helper functions for failures and recursive validating. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
type Context<T, S> = { | ||
value: any; | ||
struct: Struct<T, S>; | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a struct. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* A `Context` contains information about the current location of the | ||
* validation inside the initial input value. | ||
*/ | ||
type Context = { | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
fail: (props?: string | Partial<Failure>) => Failure; | ||
check: <Y, Z>(value: any, struct: Struct<Y, Z>, parent?: any, key?: string | number) => IterableIterator<Failure>; | ||
path: Array<any>; | ||
}; | ||
@@ -214,11 +245,11 @@ /** | ||
*/ | ||
type Result = boolean | string | Iterable<Failure>; | ||
type Result = boolean | string | Partial<Failure> | Iterable<boolean | string | Partial<Failure>>; | ||
/** | ||
* A `Coercer` takes an unknown value and optionally coerces it. | ||
*/ | ||
type Coercer = (value: unknown) => unknown; | ||
type Coercer<T = unknown> = (value: T, context: Context) => unknown; | ||
/** | ||
* A `Validate` takes an unknown value and validates it. | ||
* A `Validator` takes an unknown value and validates it. | ||
*/ | ||
type Validator<T, S> = (value: unknown, context: Context<T, S>) => Result; | ||
type Validator = (value: unknown, context: Context) => Result; | ||
/** | ||
@@ -228,32 +259,4 @@ * A `Refiner` takes a value of a known type and validates it against a further | ||
*/ | ||
type Refiner<T, S> = (value: T, context: Context<T, S>) => Result; | ||
type Refiner<T> = (value: T, context: Context) => Result; | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
*/ | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a `Struct`. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* Augment a `Struct` to add an additional coercion step to its input. | ||
@@ -268,3 +271,3 @@ * | ||
*/ | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: (value: C) => any): Struct<T, S>; | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: Coercer<C>): Struct<T, S>; | ||
/** | ||
@@ -276,3 +279,3 @@ * Augment a struct to replace `undefined` values with a default. | ||
*/ | ||
declare function defaulted<T, S>(S: Struct<T, S>, fallback: any, options?: { | ||
declare function defaulted<T, S>(struct: Struct<T, S>, fallback: any, options?: { | ||
strict?: boolean; | ||
@@ -325,3 +328,3 @@ }): Struct<T, S>; | ||
*/ | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T, S>): Struct<T, S>; | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T>): Struct<T, S>; | ||
/** | ||
@@ -1203,5 +1206,5 @@ * Ensure that any value passes validation. | ||
*/ | ||
declare function define<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
declare function define<T>(name: string, validator: Validator): Struct<T, null>; | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -1212,5 +1215,5 @@ * The callback will receive the value currently being validated, and must | ||
*/ | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context<T, null>) => Struct<T, any>): Struct<T, null>; | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context) => Struct<T, any>): Struct<T, null>; | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -1249,3 +1252,3 @@ * The first time validation is run with the struct, the callback will be called | ||
*/ | ||
declare function struct<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
export { Failure, StructError, Struct, Context, Infer, Describe, Result, Coercer, Validator, Refiner, assert, create, mask, is, validate, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; | ||
declare function struct<T>(name: string, validator: Validator): Struct<T, null>; | ||
export { Failure, StructError, Struct, assert, create, mask, is, validate, Context, Infer, Describe, Result, Coercer, Validator, Refiner, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; |
@@ -20,3 +20,4 @@ (function (global, factory) { | ||
class StructError extends TypeError { | ||
constructor(failure, moreFailures) { | ||
constructor(failure, failures) { | ||
let cached; | ||
const { | ||
@@ -29,3 +30,2 @@ message, | ||
} = failure; | ||
let failures; | ||
const msg = path.length === 0 ? message : "At path: " + path.join('.') + " -- " + message; | ||
@@ -37,7 +37,5 @@ super(msg); | ||
this.failures = () => { | ||
if (!failures) { | ||
failures = [failure, ...moreFailures]; | ||
} | ||
var _cached; | ||
return failures; | ||
return (_cached = cached) != null ? _cached : cached = [failure, ...failures()]; | ||
}; | ||
@@ -49,10 +47,25 @@ } | ||
/** | ||
* Check if a value is an iterator. | ||
*/ | ||
function isIterable(x) { | ||
return isObject(x) && typeof x[Symbol.iterator] === 'function'; | ||
} | ||
/** | ||
* Check if a value is a plain object. | ||
*/ | ||
function isPlainObject(value) { | ||
if (Object.prototype.toString.call(value) !== '[object Object]') { | ||
function isObject(x) { | ||
return typeof x === 'object' && x != null; | ||
} | ||
/** | ||
* Check if a value is a plain object. | ||
*/ | ||
function isPlainObject(x) { | ||
if (Object.prototype.toString.call(x) !== '[object Object]') { | ||
return false; | ||
} | ||
const prototype = Object.getPrototypeOf(value); | ||
const prototype = Object.getPrototypeOf(x); | ||
return prototype === null || prototype === Object.prototype; | ||
@@ -80,19 +93,121 @@ } | ||
/** | ||
* Convert a validation result to an iterable of failures. | ||
* Convert a single validation result to a failure. | ||
*/ | ||
function* toFailures(result, context) { | ||
if (typeof result === 'string') { | ||
yield context.fail({ | ||
message: result | ||
}); | ||
} else if (result === true) { | ||
function toFailure(result, context, struct, value) { | ||
if (result === true) { | ||
return; | ||
} else if (result === false) { | ||
yield context.fail(); | ||
} else { | ||
yield* result; | ||
result = {}; | ||
} else if (typeof result === 'string') { | ||
result = { | ||
message: result | ||
}; | ||
} | ||
const { | ||
path, | ||
branch | ||
} = context; | ||
const { | ||
type | ||
} = struct; | ||
const { | ||
refinement, | ||
message = "Expected a value of type `" + type + "`" + (refinement ? " with refinement `" + refinement + "`" : '') + ", but received: `" + print(value) + "`" | ||
} = result; | ||
return { | ||
value, | ||
type, | ||
refinement, | ||
key: path[path.length - 1], | ||
path, | ||
branch, | ||
...result, | ||
message | ||
}; | ||
} | ||
/** | ||
* Convert a validation result to an iterable of failures. | ||
*/ | ||
function* toFailures(result, context, struct, value) { | ||
if (!isIterable(result)) { | ||
result = [result]; | ||
} | ||
for (const r of result) { | ||
const failure = toFailure(r, context, struct, value); | ||
if (failure) { | ||
yield failure; | ||
} | ||
} | ||
} | ||
/** | ||
* Check a value against a struct, traversing deeply into nested values, and | ||
* returning an iterator of failures or success. | ||
*/ | ||
function* run(value, struct, options = {}) { | ||
const { | ||
path = [], | ||
branch = [value], | ||
coerce = false | ||
} = options; | ||
const ctx = { | ||
path, | ||
branch | ||
}; | ||
if (coerce) { | ||
value = struct.coercer(value, ctx); | ||
} | ||
let valid = true; | ||
for (const failure of struct.validator(value, ctx)) { | ||
valid = false; | ||
yield [failure, undefined]; | ||
} | ||
if (valid) { | ||
for (const failure of struct.refiner(value, ctx)) { | ||
valid = false; | ||
yield [failure, undefined]; | ||
} | ||
} | ||
for (let [k, v, s] of struct.entries(value, ctx)) { | ||
const ts = run(v, s, { | ||
path: k === undefined ? path : [...path, k], | ||
branch: k === undefined ? branch : [...branch, v], | ||
coerce | ||
}); | ||
for (const t of ts) { | ||
if (t[0]) { | ||
valid = false; | ||
yield [t[0], undefined]; | ||
} else if (coerce) { | ||
v = t[1]; | ||
if (k === undefined) { | ||
value = v; | ||
} else if (value instanceof Map) { | ||
value.set(k, v); | ||
} else if (value instanceof Set) { | ||
value.add(v); | ||
} else if (isObject(value)) { | ||
value[k] = v; | ||
} | ||
} | ||
} | ||
} | ||
if (valid) { | ||
yield [undefined, value]; | ||
} | ||
} | ||
function assign(...Structs) { | ||
@@ -115,3 +230,3 @@ const schemas = Structs.map(s => s.schema); | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -124,8 +239,25 @@ * The callback will receive the value currently being validated, and must | ||
function dynamic(fn) { | ||
return define('dynamic', (value, ctx) => { | ||
return ctx.check(value, fn(value, ctx)); | ||
return new Struct({ | ||
type: 'dynamic', | ||
schema: null, | ||
*entries(value, ctx) { | ||
const struct = fn(value, ctx); | ||
yield* struct.entries(value, ctx); | ||
}, | ||
validator(value, ctx) { | ||
const struct = fn(value, ctx); | ||
return struct.validator(value, ctx); | ||
}, | ||
coercer(value, ctx) { | ||
const struct = fn(value, ctx); | ||
return struct.coercer(value, ctx); | ||
} | ||
}); | ||
} | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -139,9 +271,28 @@ * The first time validation is run with the struct, the callback will be called | ||
function lazy(fn) { | ||
let s; | ||
return define('lazy', (value, ctx) => { | ||
if (!s) { | ||
s = fn(); | ||
let struct; | ||
return new Struct({ | ||
type: 'lazy', | ||
schema: null, | ||
*entries(value, ctx) { | ||
var _struct; | ||
(_struct = struct) != null ? _struct : struct = fn(); | ||
yield* struct.entries(value, ctx); | ||
}, | ||
validator(value, ctx) { | ||
var _struct2; | ||
(_struct2 = struct) != null ? _struct2 : struct = fn(); | ||
return struct.validator(value, ctx); | ||
}, | ||
coercer(value, ctx) { | ||
var _struct3; | ||
(_struct3 = struct) != null ? _struct3 : struct = fn(); | ||
return struct.coercer(value, ctx); | ||
} | ||
return ctx.check(value, s); | ||
}); | ||
@@ -228,14 +379,17 @@ } | ||
schema: Element, | ||
coercer: value => { | ||
return Element && Array.isArray(value) ? value.map(v => Element.coercer(v)) : value; | ||
}, | ||
*validator(value, ctx) { | ||
if (!Array.isArray(value)) { | ||
yield ctx.fail("Expected an array value, but received: " + print(value)); | ||
} else if (Element) { | ||
*entries(value) { | ||
if (Element && Array.isArray(value)) { | ||
for (const [i, v] of value.entries()) { | ||
yield* ctx.check(v, Element, value, i); | ||
yield [i, v, Element]; | ||
} | ||
} | ||
}, | ||
coercer(value) { | ||
return Array.isArray(value) ? value.slice() : value; | ||
}, | ||
validator(value) { | ||
return Array.isArray(value) || "Expected an array value, but received: " + print(value); | ||
} | ||
@@ -277,5 +431,7 @@ | ||
schema, | ||
validator: value => { | ||
validator(value) { | ||
return values.includes(value) || "Expected one of `" + description + "`, but received: " + print(value); | ||
} | ||
}); | ||
@@ -311,6 +467,18 @@ } | ||
function intersection(Structs) { | ||
return define('intersection', function* (value, ctx) { | ||
for (const S of Structs) { | ||
yield* ctx.check(value, S); | ||
return new Struct({ | ||
type: 'intersection', | ||
schema: null, | ||
*entries(value, ctx) { | ||
for (const S of Structs) { | ||
yield* S.entries(value, ctx); | ||
} | ||
}, | ||
*validator(value, ctx) { | ||
for (const S of Structs) { | ||
yield* S.validator(value, ctx); | ||
} | ||
} | ||
}); | ||
@@ -325,11 +493,23 @@ } | ||
function map(Key, Value) { | ||
return define('map', function* (value, ctx) { | ||
if (!(value instanceof Map)) { | ||
yield ctx.fail("Expected a `Map` object, but received: " + print(value)); | ||
} else if (Key && Value) { | ||
for (const [k, v] of value.entries()) { | ||
yield* ctx.check(k, Key, value, k); | ||
yield* ctx.check(v, Value, value, k); | ||
return new Struct({ | ||
type: 'map', | ||
schema: null, | ||
*entries(value) { | ||
if (Key && Value && value instanceof Map) { | ||
for (const [k, v] of value.entries()) { | ||
yield [k, k, Key]; | ||
yield [k, v, Value]; | ||
} | ||
} | ||
}, | ||
coercer(value) { | ||
return value instanceof Map ? new Map(value) : value; | ||
}, | ||
validator(value) { | ||
return value instanceof Map || "Expected a `Map` object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -349,17 +529,5 @@ } | ||
function nullable(struct) { | ||
const { | ||
refiner | ||
} = struct; | ||
return new Struct({ ...struct, | ||
validator: (value, ctx) => { | ||
return value === null || ctx.check(value, struct); | ||
}, | ||
refiner: function* (value, ctx) { | ||
if (value != null) { | ||
const c = { ...ctx, | ||
struct | ||
}; | ||
yield* toFailures(refiner(value, c), c); | ||
} | ||
} | ||
validator: (value, ctx) => value === null || struct.validator(value, ctx), | ||
refiner: (value, ctx) => value === null || struct.refiner(value, ctx) | ||
}); | ||
@@ -383,6 +551,4 @@ } | ||
*validator(value, ctx) { | ||
if (typeof value !== 'object' || value == null) { | ||
yield ctx.fail("Expected an object, but received: " + print(value)); | ||
} else if (schema) { | ||
*entries(value) { | ||
if (schema && isObject(value)) { | ||
const unknowns = new Set(Object.keys(value)); | ||
@@ -392,10 +558,7 @@ | ||
unknowns.delete(key); | ||
const Value = schema[key]; | ||
const v = value[key]; | ||
yield* ctx.check(v, Value, value, key); | ||
yield [key, value[key], schema[key]]; | ||
} | ||
for (const key of unknowns) { | ||
const v = value[key]; | ||
yield* ctx.check(v, Never, value, key); | ||
yield [key, value[key], Never]; | ||
} | ||
@@ -405,23 +568,11 @@ } | ||
coercer: value => { | ||
if (!schema || typeof value !== 'object' || value == null) { | ||
return value; | ||
} | ||
validator(value) { | ||
return isObject(value) || "Expected an object, but received: " + print(value); | ||
}, | ||
const ret = {}; | ||
const unknowns = new Set(Object.keys(value)); | ||
coercer(value) { | ||
return isObject(value) ? { ...value | ||
} : value; | ||
} | ||
for (const key of knowns) { | ||
unknowns.delete(key); | ||
const Value = schema[key]; | ||
const v = value[key]; | ||
ret[key] = Value.coercer(v); | ||
} | ||
for (const key of unknowns) { | ||
ret[key] = value[key]; | ||
} | ||
return ret; | ||
} | ||
}); | ||
@@ -434,17 +585,5 @@ } | ||
function optional(struct) { | ||
const { | ||
refiner | ||
} = struct; | ||
return new Struct({ ...struct, | ||
validator: (value, ctx) => { | ||
return value === undefined || ctx.check(value, struct); | ||
}, | ||
refiner: function* (value, ctx) { | ||
if (value != null) { | ||
const c = { ...ctx, | ||
struct | ||
}; | ||
yield* toFailures(refiner(value, c), c); | ||
} | ||
} | ||
validator: (value, ctx) => value === undefined || struct.validator(value, ctx), | ||
refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx) | ||
}); | ||
@@ -460,12 +599,20 @@ } | ||
function record(Key, Value) { | ||
return define('record', function* (value, ctx) { | ||
if (typeof value !== 'object' || value == null) { | ||
yield ctx.fail("Expected an object, but received: " + print(value)); | ||
} else { | ||
for (const k in value) { | ||
const v = value[k]; | ||
yield* ctx.check(k, Key, value, k); | ||
yield* ctx.check(v, Value, value, k); | ||
return new Struct({ | ||
type: 'record', | ||
schema: null, | ||
*entries(value) { | ||
if (isObject(value)) { | ||
for (const k in value) { | ||
const v = value[k]; | ||
yield [k, k, Key]; | ||
yield [k, v, Value]; | ||
} | ||
} | ||
}, | ||
validator(value) { | ||
return isObject(value) || "Expected an object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -486,10 +633,22 @@ } | ||
function set(Element) { | ||
return define('set', function* (value, ctx) { | ||
if (!(value instanceof Set)) { | ||
yield ctx.fail("Expected a `Set` object, but received: " + print(value)); | ||
} else if (Element) { | ||
for (const val of value) { | ||
yield* ctx.check(val, Element, value, val); | ||
return new Struct({ | ||
type: 'set', | ||
schema: null, | ||
*entries(value) { | ||
if (Element && value instanceof Set) { | ||
for (const v of value) { | ||
yield [v, v, Element]; | ||
} | ||
} | ||
}, | ||
coercer(value) { | ||
return value instanceof Set ? new Set(value) : value; | ||
}, | ||
validator(value) { | ||
return value instanceof Set || "Expected a `Set` object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -508,17 +667,20 @@ } | ||
const Never = never(); | ||
return define('tuple', function* (value, ctx) { | ||
if (!Array.isArray(value)) { | ||
yield ctx.fail("Expected an array, but received: " + print(value)); | ||
} else { | ||
for (const [index, Element] of Elements.entries()) { | ||
const v = value[index]; | ||
yield* ctx.check(v, Element, value, index); | ||
return new Struct({ | ||
type: 'tuple', | ||
schema: null, | ||
*entries(value) { | ||
if (Array.isArray(value)) { | ||
const length = Math.max(Elements.length, value.length); | ||
for (let i = 0; i < length; i++) { | ||
yield [i, value[i], Elements[i] || Never]; | ||
} | ||
} | ||
}, | ||
if (value.length > Elements.length) { | ||
const index = Elements.length; | ||
const v = value[index]; | ||
yield* ctx.check(v, Never, value, index); | ||
} | ||
validator(value) { | ||
return Array.isArray(value) || "Expected an array, but received: " + print(value); | ||
} | ||
}); | ||
@@ -538,13 +700,15 @@ } | ||
schema, | ||
validator: function* (value, ctx) { | ||
if (typeof value !== 'object' || value == null) { | ||
yield ctx.fail("Expected an object, but received: " + print(value)); | ||
} else { | ||
for (const key of keys) { | ||
const Value = schema[key]; | ||
const v = value[key]; | ||
yield* ctx.check(v, Value, value, key); | ||
*entries(value) { | ||
if (isObject(value)) { | ||
for (const k of keys) { | ||
yield [k, value[k], schema[k]]; | ||
} | ||
} | ||
}, | ||
validator(value) { | ||
return isObject(value) || "Expected an object, but received: " + print(value); | ||
} | ||
}); | ||
@@ -554,17 +718,27 @@ } | ||
const description = Structs.map(s => s.type).join(' | '); | ||
return define('union', function* (value, ctx) { | ||
const failures = []; | ||
return new Struct({ | ||
type: 'union', | ||
schema: null, | ||
for (const S of Structs) { | ||
const [...array] = ctx.check(value, S); | ||
validator(value, ctx) { | ||
const failures = []; | ||
if (array.length === 0) { | ||
return; | ||
} else { | ||
failures.push(...array); | ||
for (const S of Structs) { | ||
const [...tuples] = run(value, S, ctx); | ||
const [first] = tuples; | ||
if (!first[0]) { | ||
return []; | ||
} else { | ||
for (const [failure] of tuples) { | ||
if (failure) { | ||
failures.push(failure); | ||
} | ||
} | ||
} | ||
} | ||
return ["Expected the value to satisfy a union of `" + description + "`, but received: " + print(value), ...failures]; | ||
} | ||
yield ctx.fail("Expected the value to satisfy a union of `" + description + "`, but received: " + print(value)); | ||
yield* failures; | ||
}); | ||
@@ -592,10 +766,5 @@ } | ||
function coerce(struct, condition, coercer) { | ||
const fn = struct.coercer; | ||
return new Struct({ ...struct, | ||
coercer: value => { | ||
if (is(value, condition)) { | ||
return fn(coercer(value)); | ||
} else { | ||
return fn(value); | ||
} | ||
coercer: (value, ctx) => { | ||
return is(value, condition) ? struct.coercer(coercer(value, ctx), ctx) : struct.coercer(value, ctx); | ||
} | ||
@@ -611,7 +780,4 @@ }); | ||
function defaulted(S, fallback, options = {}) { | ||
const { | ||
strict | ||
} = options; | ||
return coerce(S, unknown(), x => { | ||
function defaulted(struct, fallback, options = {}) { | ||
return coerce(struct, unknown(), x => { | ||
const f = typeof fallback === 'function' ? fallback() : fallback; | ||
@@ -623,3 +789,3 @@ | ||
if (!strict && isPlainObject(x) && isPlainObject(f)) { | ||
if (!options.strict && isPlainObject(x) && isPlainObject(f)) { | ||
const ret = { ...x | ||
@@ -687,10 +853,32 @@ }; | ||
constructor(props) { | ||
this.type = props.type; | ||
this.schema = props.schema; | ||
const { | ||
type, | ||
schema, | ||
validator, | ||
refiner, | ||
coercer = value => value, | ||
entries = function* () {} | ||
} = props; | ||
this.type = type; | ||
this.schema = schema; | ||
this.entries = entries; | ||
this.coercer = coercer; | ||
this.coercer = props.coercer || (value => value); | ||
if (validator) { | ||
this.validator = (value, context) => { | ||
const result = validator(value, context); | ||
return toFailures(result, context, this, value); | ||
}; | ||
} else { | ||
this.validator = () => []; | ||
} | ||
this.validator = props.validator || (() => []); | ||
this.refiner = props.refiner || (() => []); | ||
if (refiner) { | ||
this.refiner = (value, context) => { | ||
const result = refiner(value, context); | ||
return toFailures(result, context, this, value); | ||
}; | ||
} else { | ||
this.refiner = () => []; | ||
} | ||
} | ||
@@ -746,3 +934,3 @@ /** | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
@@ -758,12 +946,18 @@ | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
function create(value, struct) { | ||
const ret = struct.coercer(value); | ||
assert(ret, struct); | ||
return ret; | ||
const result = validate(value, struct, { | ||
coerce: true | ||
}); | ||
if (result[0]) { | ||
throw result[0]; | ||
} else { | ||
return result[1]; | ||
} | ||
} | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
@@ -777,3 +971,3 @@ | ||
/** | ||
* Check if a value passes a `Struct`. | ||
* Check if a value passes a struct. | ||
*/ | ||
@@ -786,79 +980,25 @@ | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
function validate(value, struct, options = {}) { | ||
if (options.coerce) { | ||
value = struct.coercer(value); | ||
} | ||
const tuples = run(value, struct, options); | ||
const tuple = shiftIterator(tuples); | ||
const failures = check(value, struct); | ||
const failure = shiftIterator(failures); | ||
if (failure) { | ||
const error = new StructError(failure, failures); | ||
if (tuple[0]) { | ||
const error = new StructError(tuple[0], function* () { | ||
for (const t of tuples) { | ||
if (t[0]) { | ||
yield t[0]; | ||
} | ||
} | ||
}); | ||
return [error, undefined]; | ||
} else { | ||
return [undefined, value]; | ||
const v = tuple[1]; | ||
return [undefined, v]; | ||
} | ||
} | ||
/** | ||
* Check a value against a `Struct`, returning an iterable of failures. | ||
*/ | ||
function* check(value, struct, path = [], branch = []) { | ||
const ctx = { | ||
value, | ||
struct, | ||
branch, | ||
path, | ||
check(v, s, parent, key) { | ||
const p = parent !== undefined ? [...path, key] : path; | ||
const b = parent !== undefined ? [...branch, parent] : branch; | ||
return check(v, s, p, b); | ||
}, | ||
fail(props = {}) { | ||
if (typeof props === 'string') { | ||
props = { | ||
message: props | ||
}; | ||
} | ||
const { | ||
type | ||
} = struct; | ||
let { | ||
message, | ||
refinement | ||
} = props; | ||
if (!message) { | ||
message = "Expected a value of type `" + type + "`" + (refinement ? " with refinement `" + refinement + "`" : '') + (path.length ? " for `" + path.join('.') + "`" : '') + ", but received: `" + print(value) + "`"; | ||
} | ||
return { ...props, | ||
value, | ||
type, | ||
refinement, | ||
message, | ||
key: path[path.length - 1], | ||
path, | ||
branch: [...branch, value] | ||
}; | ||
} | ||
}; | ||
const failures = toFailures(struct.validator(value, ctx), ctx); | ||
const failure = shiftIterator(failures); | ||
if (failure) { | ||
yield failure; | ||
yield* failures; | ||
} else { | ||
yield* toFailures(struct.refiner(value, ctx), ctx); | ||
} | ||
} | ||
/** | ||
@@ -949,9 +1089,10 @@ * Ensure that a string, array, map, or set is empty. | ||
function refine(struct, name, refiner) { | ||
const fn = struct.refiner; | ||
return new Struct({ ...struct, | ||
*refiner(value, ctx) { | ||
yield* toFailures(fn(value, ctx), ctx); | ||
yield* struct.refiner(value, ctx); | ||
const result = refiner(value, ctx); | ||
const failures = toFailures(result, ctx, struct, value); | ||
for (const failure of toFailures(refiner(value, ctx), ctx)) { | ||
for (const failure of failures) { | ||
yield { ...failure, | ||
@@ -958,0 +1099,0 @@ refinement: name |
@@ -6,3 +6,3 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
@@ -12,3 +12,3 @@ refinement: string | undefined; | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
path: Array<any>; | ||
}; | ||
@@ -25,10 +25,10 @@ /** | ||
value: any; | ||
key: string | number | undefined; | ||
key: any; | ||
type: string; | ||
refinement: string | undefined; | ||
path: Array<number | string>; | ||
path: Array<any>; | ||
branch: Array<any>; | ||
failures: () => Array<Failure>; | ||
[x: string]: any; | ||
constructor(failure: Failure, moreFailures: IterableIterator<Failure>); | ||
constructor(failure: Failure, failures: () => Generator<Failure>); | ||
} | ||
@@ -144,11 +144,17 @@ /** | ||
schema: S; | ||
coercer: Coercer; | ||
validator: Validator<T, S>; | ||
refiner: Refiner<T, S>; | ||
coercer: (value: unknown, context: Context) => unknown; | ||
validator: (value: unknown, context: Context) => Iterable<Failure>; | ||
refiner: (value: T, context: Context) => Iterable<Failure>; | ||
entries: (value: unknown, context: Context) => Iterable<[ | ||
string | number, | ||
unknown, | ||
Struct<any> | Struct<never> | ||
]>; | ||
constructor(props: { | ||
type: Struct<T, S>["type"]; | ||
schema: Struct<T, S>["schema"]; | ||
coercer?: Struct<T, S>["coercer"]; | ||
validator?: Struct<T, S>["validator"]; | ||
refiner?: Struct<T, S>["refiner"]; | ||
type: string; | ||
schema: S; | ||
coercer?: Coercer; | ||
validator?: Validator; | ||
refiner?: Refiner<T>; | ||
entries?: Struct<T, S>["entries"]; | ||
}); | ||
@@ -191,12 +197,37 @@ /** | ||
/** | ||
* A `StructContext` contains information about the current value being | ||
* validated as well as helper functions for failures and recursive validating. | ||
* Assert that a value passes a struct, throwing if it doesn't. | ||
*/ | ||
type Context<T, S> = { | ||
value: any; | ||
struct: Struct<T, S>; | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of struct and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a struct. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a struct, returning an error if invalid, or the | ||
* value (with potential coercion) if valid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* A `Context` contains information about the current location of the | ||
* validation inside the initial input value. | ||
*/ | ||
type Context = { | ||
branch: Array<any>; | ||
path: Array<string | number>; | ||
fail: (props?: string | Partial<Failure>) => Failure; | ||
check: <Y, Z>(value: any, struct: Struct<Y, Z>, parent?: any, key?: string | number) => IterableIterator<Failure>; | ||
path: Array<any>; | ||
}; | ||
@@ -214,11 +245,11 @@ /** | ||
*/ | ||
type Result = boolean | string | Iterable<Failure>; | ||
type Result = boolean | string | Partial<Failure> | Iterable<boolean | string | Partial<Failure>>; | ||
/** | ||
* A `Coercer` takes an unknown value and optionally coerces it. | ||
*/ | ||
type Coercer = (value: unknown) => unknown; | ||
type Coercer<T = unknown> = (value: T, context: Context) => unknown; | ||
/** | ||
* A `Validate` takes an unknown value and validates it. | ||
* A `Validator` takes an unknown value and validates it. | ||
*/ | ||
type Validator<T, S> = (value: unknown, context: Context<T, S>) => Result; | ||
type Validator = (value: unknown, context: Context) => Result; | ||
/** | ||
@@ -228,32 +259,4 @@ * A `Refiner` takes a value of a known type and validates it against a further | ||
*/ | ||
type Refiner<T, S> = (value: T, context: Context<T, S>) => Result; | ||
type Refiner<T> = (value: T, context: Context) => Result; | ||
/** | ||
* Assert that a value passes a `Struct`, throwing if it doesn't. | ||
*/ | ||
declare function assert<T, S>(value: unknown, struct: Struct<T, S>): asserts value is T; | ||
/** | ||
* Create a value with the coercion logic of `Struct` and validate it. | ||
*/ | ||
declare function create<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Mask a value, returning only the subset of properties defined by a Struct. | ||
*/ | ||
declare function mask<T, S>(value: unknown, struct: Struct<T, S>): T; | ||
/** | ||
* Check if a value passes a `Struct`. | ||
*/ | ||
declare function is<T, S>(value: unknown, struct: Struct<T, S>): value is T; | ||
/** | ||
* Validate a value against a `Struct`, returning an error if invalid. | ||
*/ | ||
declare function validate<T, S>(value: unknown, struct: Struct<T, S>, options?: { | ||
coerce?: boolean; | ||
}): [ | ||
StructError, | ||
undefined | ||
] | [ | ||
undefined, | ||
T | ||
]; | ||
/** | ||
* Augment a `Struct` to add an additional coercion step to its input. | ||
@@ -268,3 +271,3 @@ * | ||
*/ | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: (value: C) => any): Struct<T, S>; | ||
declare function coerce<T, S, C>(struct: Struct<T, S>, condition: Struct<C, any>, coercer: Coercer<C>): Struct<T, S>; | ||
/** | ||
@@ -276,3 +279,3 @@ * Augment a struct to replace `undefined` values with a default. | ||
*/ | ||
declare function defaulted<T, S>(S: Struct<T, S>, fallback: any, options?: { | ||
declare function defaulted<T, S>(struct: Struct<T, S>, fallback: any, options?: { | ||
strict?: boolean; | ||
@@ -325,3 +328,3 @@ }): Struct<T, S>; | ||
*/ | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T, S>): Struct<T, S>; | ||
declare function refine<T, S>(struct: Struct<T, S>, name: string, refiner: Refiner<T>): Struct<T, S>; | ||
/** | ||
@@ -1203,5 +1206,5 @@ * Ensure that any value passes validation. | ||
*/ | ||
declare function define<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
declare function define<T>(name: string, validator: Validator): Struct<T, null>; | ||
/** | ||
* Create a struct with dynamic, runtime validation. | ||
* Create a struct with dynamic validation logic. | ||
* | ||
@@ -1212,5 +1215,5 @@ * The callback will receive the value currently being validated, and must | ||
*/ | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context<T, null>) => Struct<T, any>): Struct<T, null>; | ||
declare function dynamic<T>(fn: (value: unknown, ctx: Context) => Struct<T, any>): Struct<T, null>; | ||
/** | ||
* Create a struct with lazily evaluated validation. | ||
* Create a struct with lazily evaluated validation logic. | ||
* | ||
@@ -1249,3 +1252,3 @@ * The first time validation is run with the struct, the callback will be called | ||
*/ | ||
declare function struct<T>(name: string, validator: Validator<T, null>): Struct<T, null>; | ||
export { Failure, StructError, Struct, Context, Infer, Describe, Result, Coercer, Validator, Refiner, assert, create, mask, is, validate, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; | ||
declare function struct<T>(name: string, validator: Validator): Struct<T, null>; | ||
export { Failure, StructError, Struct, assert, create, mask, is, validate, Context, Infer, Describe, Result, Coercer, Validator, Refiner, coerce, defaulted, masked, trimmed, empty, max, min, pattern, size, refine, any, array, boolean, date, enums, func, instance, integer, intersection, literal, map, never, nullable, number, object, optional, record, regexp, set, string, tuple, type, union, unknown, assign, define, dynamic, lazy, omit, partial, pick, struct }; |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Superstruct={})}(this,(function(e){"use strict";class t extends TypeError{constructor(e,t){const{message:n,...r}=e,{path:c}=e;let i;super(0===c.length?n:"At path: "+c.join(".")+" -- "+n),Object.assign(this,r),this.name=this.constructor.name,this.failures=()=>(i||(i=[e,...t]),i)}}function n(e){if("[object Object]"!==Object.prototype.toString.call(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function r(e){return"string"==typeof e?JSON.stringify(e):""+e}function c(e){const{done:t,value:n}=e.next();return t?void 0:n}function*i(e,t){if("string"==typeof e)yield t.fail({message:e});else{if(!0===e)return;!1===e?yield t.fail():yield*e}}function o(e,t){return new y({type:e,schema:null,validator:t})}function u(){return o("never",(()=>!1))}function s(e){const t=e?Object.keys(e):[],n=u();return new y({type:"object",schema:e||null,*validator(c,i){if("object"!=typeof c||null==c)yield i.fail("Expected an object, but received: "+r(c));else if(e){const r=new Set(Object.keys(c));for(const n of t){r.delete(n);const t=e[n],o=c[n];yield*i.check(o,t,c,n)}for(const e of r){const t=c[e];yield*i.check(t,n,c,e)}}},coercer:n=>{if(!e||"object"!=typeof n||null==n)return n;const r={},c=new Set(Object.keys(n));for(const i of t){c.delete(i);const t=e[i],o=n[i];r[i]=t.coercer(o)}for(const e of c)r[e]=n[e];return r}})}function f(e){const{refiner:t}=e;return new y({...e,validator:(t,n)=>void 0===t||n.check(t,e),refiner:function*(n,r){if(null!=n){const c={...r,struct:e};yield*i(t(n,c),c)}}})}function a(){return o("string",(e=>"string"==typeof e||"Expected a string, but received: "+r(e)))}function l(){return o("unknown",(()=>!0))}function d(e,t,n){const r=e.coercer;return new y({...e,coercer:e=>v(e,t)?r(n(e)):r(e)})}function p(e){return d(e,l(),(t=>{if("object"!=typeof e.schema||null==e.schema||"object"!=typeof t||null==t)return t;{const n={};for(const r in e.schema)r in t&&(n[r]=t[r]);return n}}))}class y{constructor(e){this.type=e.type,this.schema=e.schema,this.coercer=e.coercer||(e=>e),this.validator=e.validator||(()=>[]),this.refiner=e.refiner||(()=>[])}assert(e){return h(e,this)}create(e){return b(e,this)}is(e){return v(e,this)}mask(e){return m(e,this)}validate(e,t={}){return g(e,this,t)}}function h(e,t){const n=g(e,t);if(n[0])throw n[0]}function b(e,t){const n=t.coercer(e);return h(n,t),n}function m(e,t){return b(e,p(t))}function v(e,t){return!g(e,t)[0]}function g(e,n,r={}){r.coerce&&(e=n.coercer(e));const i=x(e,n),o=c(i);if(o){return[new t(o,i),void 0]}return[void 0,e]}function*x(e,t,n=[],o=[]){const u={value:e,struct:t,branch:o,path:n,check:(e,t,r,c)=>x(e,t,void 0!==r?[...n,c]:n,void 0!==r?[...o,r]:o),fail(c={}){"string"==typeof c&&(c={message:c});const{type:i}=t;let{message:u,refinement:s}=c;return u||(u="Expected a value of type `"+i+"`"+(s?" with refinement `"+s+"`":"")+(n.length?" for `"+n.join(".")+"`":"")+", but received: `"+r(e)+"`"),{...c,value:e,type:i,refinement:s,message:u,key:n[n.length-1],path:n,branch:[...o,e]}}},s=i(t.validator(e,u),u),f=c(s);f?(yield f,yield*s):yield*i(t.refiner(e,u),u)}function j(e,t,n){const r=e.refiner;return new y({...e,*refiner(e,c){yield*i(r(e,c),c);for(const r of i(n(e,c),c))yield{...r,refinement:t}}})}e.Struct=y,e.StructError=t,e.any=function(){return o("any",(()=>!0))},e.array=function(e){return new y({type:"array",schema:e,coercer:t=>e&&Array.isArray(t)?t.map((t=>e.coercer(t))):t,*validator(t,n){if(Array.isArray(t)){if(e)for(const[r,c]of t.entries())yield*n.check(c,e,t,r)}else yield n.fail("Expected an array value, but received: "+r(t))}})},e.assert=h,e.assign=function(...e){const t=e.map((e=>e.schema));return s(Object.assign({},...t))},e.boolean=function(){return o("boolean",(e=>"boolean"==typeof e))},e.coerce=d,e.create=b,e.date=function(){return o("date",(e=>e instanceof Date&&!isNaN(e.getTime())||"Expected a valid `Date` object, but received: "+r(e)))},e.defaulted=function(e,t,r={}){const{strict:c}=r;return d(e,l(),(e=>{const r="function"==typeof t?t():t;if(void 0===e)return r;if(!c&&n(e)&&n(r)){const t={...e};let n=!1;for(const e in r)void 0===t[e]&&(t[e]=r[e],n=!0);if(n)return t}return e}))},e.define=o,e.dynamic=function(e){return o("dynamic",((t,n)=>n.check(t,e(t,n))))},e.empty=function(e){const t="Expected an empty "+e.type;return j(e,"empty",(e=>{if(e instanceof Map||e instanceof Set){const{size:n}=e;return 0===n||t+" but received one with a size of `"+n+"`"}{const{length:n}=e;return 0===n||t+" but received one with a length of `"+n+"`"}}))},e.enums=function(e){const t={},n=e.map((e=>r(e))).join();for(const n of e)t[n]=n;return new y({type:"enums",schema:t,validator:t=>e.includes(t)||"Expected one of `"+n+"`, but received: "+r(t)})},e.func=function(){return o("func",(e=>"function"==typeof e||"Expected a function, but received: "+r(e)))},e.instance=function(e){return o("instance",(t=>t instanceof e||"Expected a `"+e.name+"` instance, but received: "+r(t)))},e.integer=function(){return o("integer",(e=>"number"==typeof e&&!isNaN(e)&&Number.isInteger(e)||"Expected an integer, but received: "+r(e)))},e.intersection=function(e){return o("intersection",(function*(t,n){for(const r of e)yield*n.check(t,r)}))},e.is=v,e.lazy=function(e){let t;return o("lazy",((n,r)=>(t||(t=e()),r.check(n,t))))},e.literal=function(e){const t=r(e);return o("literal",(n=>n===e||"Expected the literal `"+t+"`, but received: "+r(n)))},e.map=function(e,t){return o("map",(function*(n,c){if(n instanceof Map){if(e&&t)for(const[r,i]of n.entries())yield*c.check(r,e,n,r),yield*c.check(i,t,n,r)}else yield c.fail("Expected a `Map` object, but received: "+r(n))}))},e.mask=m,e.masked=p,e.max=function(e,t,n={}){const{exclusive:r}=n;return j(e,"max",(n=>r?n<t:n<=t||"Expected a "+e.type+" greater than "+(r?"":"or equal to ")+t+" but received `"+n+"`"))},e.min=function(e,t,n={}){const{exclusive:r}=n;return j(e,"min",(n=>r?n>t:n>=t||"Expected a "+e.type+" greater than "+(r?"":"or equal to ")+t+" but received `"+n+"`"))},e.never=u,e.nullable=function(e){const{refiner:t}=e;return new y({...e,validator:(t,n)=>null===t||n.check(t,e),refiner:function*(n,r){if(null!=n){const c={...r,struct:e};yield*i(t(n,c),c)}}})},e.number=function(){return o("number",(e=>"number"==typeof e&&!isNaN(e)||"Expected a number, but received: "+r(e)))},e.object=s,e.omit=function(e,t){const{schema:n}=e,r={...n};for(const e of t)delete r[e];return s(r)},e.optional=f,e.partial=function(e){const t=e instanceof y?{...e.schema}:{...e};for(const e in t)t[e]=f(t[e]);return s(t)},e.pattern=function(e,t){return j(e,"pattern",(n=>t.test(n)||"Expected a "+e.type+" matching `/"+t.source+'/` but received "'+n+'"'))},e.pick=function(e,t){const{schema:n}=e,r={};for(const e of t)r[e]=n[e];return s(r)},e.record=function(e,t){return o("record",(function*(n,c){if("object"!=typeof n||null==n)yield c.fail("Expected an object, but received: "+r(n));else for(const r in n){const i=n[r];yield*c.check(r,e,n,r),yield*c.check(i,t,n,r)}}))},e.refine=j,e.regexp=function(){return o("regexp",(e=>e instanceof RegExp))},e.set=function(e){return o("set",(function*(t,n){if(t instanceof Set){if(e)for(const r of t)yield*n.check(r,e,t,r)}else yield n.fail("Expected a `Set` object, but received: "+r(t))}))},e.size=function(e,t,n=t){const r="Expected a "+e.type,c=t===n?"of `"+t+"`":"between `"+t+"` and `"+n+"`";return j(e,"size",(e=>{if("number"==typeof e||e instanceof Date)return t<=e&&e<=n||r+" "+c+" but received `"+e+"`";if(e instanceof Map||e instanceof Set){const{size:i}=e;return t<=i&&i<=n||r+" with a size "+c+" but received one with a size of `"+i+"`"}{const{length:i}=e;return t<=i&&i<=n||r+" with a length "+c+" but received one with a length of `"+i+"`"}}))},e.string=a,e.struct=function(e,t){return console.warn("superstruct@0.11 - The `struct` helper has been renamed to `define`."),o(e,t)},e.trimmed=function(e){return d(e,a(),(e=>e.trim()))},e.tuple=function(e){const t=u();return o("tuple",(function*(n,c){if(Array.isArray(n)){for(const[t,r]of e.entries()){const e=n[t];yield*c.check(e,r,n,t)}if(n.length>e.length){const r=e.length,i=n[r];yield*c.check(i,t,n,r)}}else yield c.fail("Expected an array, but received: "+r(n))}))},e.type=function(e){const t=Object.keys(e);return new y({type:"type",schema:e,validator:function*(n,c){if("object"!=typeof n||null==n)yield c.fail("Expected an object, but received: "+r(n));else for(const r of t){const t=e[r],i=n[r];yield*c.check(i,t,n,r)}}})},e.union=function(e){const t=e.map((e=>e.type)).join(" | ");return o("union",(function*(n,c){const i=[];for(const t of e){const[...e]=c.check(n,t);if(0===e.length)return;i.push(...e)}yield c.fail("Expected the value to satisfy a union of `"+t+"`, but received: "+r(n)),yield*i}))},e.unknown=l,e.validate=g,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Superstruct={})}(this,(function(e){"use strict";class t extends TypeError{constructor(e,t){let n;const{message:r,...o}=e,{path:i}=e;super(0===i.length?r:"At path: "+i.join(".")+" -- "+r),Object.assign(this,o),this.name=this.constructor.name,this.failures=()=>{var r;return null!=(r=n)?r:n=[e,...t()]}}}function n(e){return"object"==typeof e&&null!=e}function r(e){if("[object Object]"!==Object.prototype.toString.call(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function o(e){return"string"==typeof e?JSON.stringify(e):""+e}function i(e,t,n,r){if(!0===e)return;!1===e?e={}:"string"==typeof e&&(e={message:e});const{path:i,branch:c}=t,{type:a}=n,{refinement:s,message:u="Expected a value of type `"+a+"`"+(s?" with refinement `"+s+"`":"")+", but received: `"+o(r)+"`"}=e;return{value:r,type:a,refinement:s,key:i[i.length-1],path:i,branch:c,...e,message:u}}function*c(e,t,r,o){var c;n(c=e)&&"function"==typeof c[Symbol.iterator]||(e=[e]);for(const n of e){const e=i(n,t,r,o);e&&(yield e)}}function*a(e,t,r={}){const{path:o=[],branch:i=[e],coerce:c=!1}=r,s={path:o,branch:i};c&&(e=t.coercer(e,s));let u=!0;for(const n of t.validator(e,s))u=!1,yield[n,void 0];if(u)for(const n of t.refiner(e,s))u=!1,yield[n,void 0];for(let[r,f,l]of t.entries(e,s)){const t=a(f,l,{path:void 0===r?o:[...o,r],branch:void 0===r?i:[...i,f],coerce:c});for(const o of t)o[0]?(u=!1,yield[o[0],void 0]):c&&(f=o[1],void 0===r?e=f:e instanceof Map?e.set(r,f):e instanceof Set?e.add(f):n(e)&&(e[r]=f))}u&&(yield[void 0,e])}function s(e,t){return new v({type:e,schema:null,validator:t})}function u(){return s("never",(()=>!1))}function f(e){const t=e?Object.keys(e):[],r=u();return new v({type:"object",schema:e||null,*entries(o){if(e&&n(o)){const n=new Set(Object.keys(o));for(const r of t)n.delete(r),yield[r,o[r],e[r]];for(const e of n)yield[e,o[e],r]}},validator:e=>n(e)||"Expected an object, but received: "+o(e),coercer:e=>n(e)?{...e}:e})}function l(e){return new v({...e,validator:(t,n)=>void 0===t||e.validator(t,n),refiner:(t,n)=>void 0===t||e.refiner(t,n)})}function d(){return s("string",(e=>"string"==typeof e||"Expected a string, but received: "+o(e)))}function p(){return s("unknown",(()=>!0))}function y(e,t,n){return new v({...e,coercer:(r,o)=>x(r,t)?e.coercer(n(r,o),o):e.coercer(r,o)})}function h(e){return y(e,p(),(t=>{if("object"!=typeof e.schema||null==e.schema||"object"!=typeof t||null==t)return t;{const n={};for(const r in e.schema)r in t&&(n[r]=t[r]);return n}}))}class v{constructor(e){const{type:t,schema:n,validator:r,refiner:o,coercer:i=(e=>e),entries:a=function*(){}}=e;this.type=t,this.schema=n,this.entries=a,this.coercer=i,this.validator=r?(e,t)=>c(r(e,t),t,this,e):()=>[],this.refiner=o?(e,t)=>c(o(e,t),t,this,e):()=>[]}assert(e){return m(e,this)}create(e){return b(e,this)}is(e){return x(e,this)}mask(e){return g(e,this)}validate(e,t={}){return w(e,this,t)}}function m(e,t){const n=w(e,t);if(n[0])throw n[0]}function b(e,t){const n=w(e,t,{coerce:!0});if(n[0])throw n[0];return n[1]}function g(e,t){return b(e,h(t))}function x(e,t){return!w(e,t)[0]}function w(e,n,r={}){const o=a(e,n,r),i=function(e){const{done:t,value:n}=e.next();return t?void 0:n}(o);if(i[0]){return[new t(i[0],(function*(){for(const e of o)e[0]&&(yield e[0])})),void 0]}return[void 0,i[1]]}function j(e,t,n){return new v({...e,*refiner(r,o){yield*e.refiner(r,o);const i=c(n(r,o),o,e,r);for(const e of i)yield{...e,refinement:t}}})}e.Struct=v,e.StructError=t,e.any=function(){return s("any",(()=>!0))},e.array=function(e){return new v({type:"array",schema:e,*entries(t){if(e&&Array.isArray(t))for(const[n,r]of t.entries())yield[n,r,e]},coercer:e=>Array.isArray(e)?e.slice():e,validator:e=>Array.isArray(e)||"Expected an array value, but received: "+o(e)})},e.assert=m,e.assign=function(...e){const t=e.map((e=>e.schema));return f(Object.assign({},...t))},e.boolean=function(){return s("boolean",(e=>"boolean"==typeof e))},e.coerce=y,e.create=b,e.date=function(){return s("date",(e=>e instanceof Date&&!isNaN(e.getTime())||"Expected a valid `Date` object, but received: "+o(e)))},e.defaulted=function(e,t,n={}){return y(e,p(),(e=>{const o="function"==typeof t?t():t;if(void 0===e)return o;if(!n.strict&&r(e)&&r(o)){const t={...e};let n=!1;for(const e in o)void 0===t[e]&&(t[e]=o[e],n=!0);if(n)return t}return e}))},e.define=s,e.dynamic=function(e){return new v({type:"dynamic",schema:null,*entries(t,n){const r=e(t,n);yield*r.entries(t,n)},validator:(t,n)=>e(t,n).validator(t,n),coercer:(t,n)=>e(t,n).coercer(t,n)})},e.empty=function(e){const t="Expected an empty "+e.type;return j(e,"empty",(e=>{if(e instanceof Map||e instanceof Set){const{size:n}=e;return 0===n||t+" but received one with a size of `"+n+"`"}{const{length:n}=e;return 0===n||t+" but received one with a length of `"+n+"`"}}))},e.enums=function(e){const t={},n=e.map((e=>o(e))).join();for(const n of e)t[n]=n;return new v({type:"enums",schema:t,validator:t=>e.includes(t)||"Expected one of `"+n+"`, but received: "+o(t)})},e.func=function(){return s("func",(e=>"function"==typeof e||"Expected a function, but received: "+o(e)))},e.instance=function(e){return s("instance",(t=>t instanceof e||"Expected a `"+e.name+"` instance, but received: "+o(t)))},e.integer=function(){return s("integer",(e=>"number"==typeof e&&!isNaN(e)&&Number.isInteger(e)||"Expected an integer, but received: "+o(e)))},e.intersection=function(e){return new v({type:"intersection",schema:null,*entries(t,n){for(const r of e)yield*r.entries(t,n)},*validator(t,n){for(const r of e)yield*r.validator(t,n)}})},e.is=x,e.lazy=function(e){let t;return new v({type:"lazy",schema:null,*entries(n,r){null!=t||(t=e()),yield*t.entries(n,r)},validator:(n,r)=>(null!=t||(t=e()),t.validator(n,r)),coercer:(n,r)=>(null!=t||(t=e()),t.coercer(n,r))})},e.literal=function(e){const t=o(e);return s("literal",(n=>n===e||"Expected the literal `"+t+"`, but received: "+o(n)))},e.map=function(e,t){return new v({type:"map",schema:null,*entries(n){if(e&&t&&n instanceof Map)for(const[r,o]of n.entries())yield[r,r,e],yield[r,o,t]},coercer:e=>e instanceof Map?new Map(e):e,validator:e=>e instanceof Map||"Expected a `Map` object, but received: "+o(e)})},e.mask=g,e.masked=h,e.max=function(e,t,n={}){const{exclusive:r}=n;return j(e,"max",(n=>r?n<t:n<=t||"Expected a "+e.type+" greater than "+(r?"":"or equal to ")+t+" but received `"+n+"`"))},e.min=function(e,t,n={}){const{exclusive:r}=n;return j(e,"min",(n=>r?n>t:n>=t||"Expected a "+e.type+" greater than "+(r?"":"or equal to ")+t+" but received `"+n+"`"))},e.never=u,e.nullable=function(e){return new v({...e,validator:(t,n)=>null===t||e.validator(t,n),refiner:(t,n)=>null===t||e.refiner(t,n)})},e.number=function(){return s("number",(e=>"number"==typeof e&&!isNaN(e)||"Expected a number, but received: "+o(e)))},e.object=f,e.omit=function(e,t){const{schema:n}=e,r={...n};for(const e of t)delete r[e];return f(r)},e.optional=l,e.partial=function(e){const t=e instanceof v?{...e.schema}:{...e};for(const e in t)t[e]=l(t[e]);return f(t)},e.pattern=function(e,t){return j(e,"pattern",(n=>t.test(n)||"Expected a "+e.type+" matching `/"+t.source+'/` but received "'+n+'"'))},e.pick=function(e,t){const{schema:n}=e,r={};for(const e of t)r[e]=n[e];return f(r)},e.record=function(e,t){return new v({type:"record",schema:null,*entries(r){if(n(r))for(const n in r){const o=r[n];yield[n,n,e],yield[n,o,t]}},validator:e=>n(e)||"Expected an object, but received: "+o(e)})},e.refine=j,e.regexp=function(){return s("regexp",(e=>e instanceof RegExp))},e.set=function(e){return new v({type:"set",schema:null,*entries(t){if(e&&t instanceof Set)for(const n of t)yield[n,n,e]},coercer:e=>e instanceof Set?new Set(e):e,validator:e=>e instanceof Set||"Expected a `Set` object, but received: "+o(e)})},e.size=function(e,t,n=t){const r="Expected a "+e.type,o=t===n?"of `"+t+"`":"between `"+t+"` and `"+n+"`";return j(e,"size",(e=>{if("number"==typeof e||e instanceof Date)return t<=e&&e<=n||r+" "+o+" but received `"+e+"`";if(e instanceof Map||e instanceof Set){const{size:i}=e;return t<=i&&i<=n||r+" with a size "+o+" but received one with a size of `"+i+"`"}{const{length:i}=e;return t<=i&&i<=n||r+" with a length "+o+" but received one with a length of `"+i+"`"}}))},e.string=d,e.struct=function(e,t){return console.warn("superstruct@0.11 - The `struct` helper has been renamed to `define`."),s(e,t)},e.trimmed=function(e){return y(e,d(),(e=>e.trim()))},e.tuple=function(e){const t=u();return new v({type:"tuple",schema:null,*entries(n){if(Array.isArray(n)){const r=Math.max(e.length,n.length);for(let o=0;o<r;o++)yield[o,n[o],e[o]||t]}},validator:e=>Array.isArray(e)||"Expected an array, but received: "+o(e)})},e.type=function(e){const t=Object.keys(e);return new v({type:"type",schema:e,*entries(r){if(n(r))for(const n of t)yield[n,r[n],e[n]]},validator:e=>n(e)||"Expected an object, but received: "+o(e)})},e.union=function(e){const t=e.map((e=>e.type)).join(" | ");return new v({type:"union",schema:null,validator(n,r){const i=[];for(const t of e){const[...e]=a(n,t,r),[o]=e;if(!o[0])return[];for(const[t]of e)t&&i.push(t)}return["Expected the value to satisfy a union of `"+t+"`, but received: "+o(n),...i]}})},e.unknown=p,e.validate=w,Object.defineProperty(e,"__esModule",{value:!0})})); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
504849
9086