Releases: smikhalevski/doubter
v5.1.0
Changelog: v5.0.0...v5.1.0
- A prototype is preserved when an object is cloned:
class Foo {
hello = 42
}
const input = new Foo();
const output = d.object({}).strip().parse(input);
input === output
// ⮕ false
output instanceof Foo
// ❌ Before ⮕ false
// ✅ After ⮕ true
- Simple plugin imports:
import * as d from 'doubter/core';
import 'doubter/plugin/array-essentials';
d.array().nonEmpty();
readonly()
support forArrayShape
,ObjectShape
,RecordShape
,MapShape
, andSetShape
:
d.array(d.string()).readonly();
// ⮕ Shape<string[], readonly string[]>
-
Enhanced formatting of issue messages and validation error messages.
-
Multiple performance optimizations.
v5.0.0
Changelog: v4.0.3...v5.0.0
-
Migrated to ES6.
-
Added essentials plugin for
RecordShape
:
import * as d from 'doubter/core';
import enableRecordEssentials from 'doubter/plugin/record-essentials';
enableRecordEssentials(RecordShape);
d.record(d.number()).allKeys('foo', 'bar');
- Issue messages can now be passed to parse methods of a shape:
d.string().parse(42, {
message: { 'type.string': 'Yo, not a string!' }
});
- Properties of options objects are now writable.
Breaking changes
-
The single issue code
type
was replaced with multiple codes:type.string
,type.number
, etc. -
Shape.messages
was removed. -
%s
placeholder isn't supported on messages anymore, use aMessageCallback
instead. -
Global error message formatter
ValidationError.formatIssues
andParseOptions.errorMessage
were removed. -
ApplyOptions
interface was merged intoParseOptions
.
v4.0.3
Changelog: v4.0.2...v4.0.3
Simplified types of plugin callbacks to support older TypeScript compiler.
v4.0.2
Changelog: v4.0.1...v4.0.2
Source maps were disabled in the published package.
v4.0.1
Changelog: v4.0.0...v4.0.1
Types are exported using export type
to enable isolatedModules
support.
v4.0.0
Changelog: v3.0.3...v4.0.0
const shape1 = d.string().addOperation(value => {
return { ok: true, value: value.trim() };
});
// ⮕ StringShape
shape1.parse(' Space ');
// ⮕ 'Space'
const shape = d
.string()
.addAsyncOperation(async value => {
if (await doAsyncCheck(value)) {
return null;
}
return [{ code: 'kaputs' }];
});
shape.isAsync;
// ⮕ true
shape.parseAsync('Hello');
ArrayShape.includes
now support an async shape as an argument:
const shape1 = d.string().checkAsync(async () => …);
const shape2 = d.array().includes(shape1);
shape2.isAsync;
// ⮕ true
-
Added
Shape.checkAsync
,Shape.refineAsync
, andShape.alterAsync
; -
Forced operations were replaced with the more flexible "tolerance for issues" concept. Tolerance setting changes the operation behavior depending on the validation issues are raised by the shape and preceding operations:
- If
skip
then if preceding operations have raised issues, then this operation is skipped but consequent operations are still applied. - If
abort
then if preceding operations have raised issues, then this operation is skipped and consequent operations aren't applied. Also, if this operation itself raises issues then consequent operations aren't applied. - If
auto
then the operation is applied regardless of previously raised issues.
- If
d.string().min(10).alter(
value => value.substring(5),
// 🟡 Skips alter operation if string length is insufficient
{ tolerance: 'skip' }
);
- Default issue messages can now be overridden using
Shape.messages
:
d.Shape.messages['type.string'] = 'Yo, not a string!';
d.string().parse(42);
// ❌ ValidationError: type at /: Yo, not a string!
ConstShape
andEnumShape
now support type-coercion:
const shape = d.const(42n).coerce();
shape.parse([new String('42')]);
// ⮕ 42n
-
Shape._getInputs()
now returns a readonly array of types and literals, since these arrays are reused for most cases to avoid excessive allocations. -
CoercibleShape.coerce()
method doesn't accept a callback anymore. UseShape.catch()
ord.convert()
to implement a custom coercion. -
ApplyOptions.coerce
flag was removed. Coercion can now be enabled on shape level only. -
Doubter is now shipped with TypeScript sources, definitions, sourcemaps, ES6 modules, and plain-old JS. Bundling was removed to enhance tree-shaking.
Other changes
-
Shape.use
was replaced withShape.addOperation
. -
Fixed unexpected unwrapping of primitive wrappers in
Array.coerce
. -
Removed async to sync auto downgrade optimization, so if
parseAsync
is called on a sync shape it would use the async path instead of wrapping a sync path in a promise. -
_applyOperations
now returns a promise if there are async operations.-
If the shape overrides only
_apply
and doesn't override_applyAsync
then it's only safe to call this method as the last statement in_apply
. Otherwise, it may return an unexpected promise. -
If the shape overrides both
_apply
and_applyAsync
then this method would always synchronously return aResult
inside_apply
.
-
v3.0.3
Changelog: v3.0.2...v3.0.3
Paths of imported and exported modules now have an explicit extension specified.
v3.0.2
Changelog: v3.0.1...v3.0.2
Enabled side effects for index.js and index.mjs in package.json.
v3.0.1
Changelog: v3.0.0...v3.0.1
Fixed LazyShape.providedShape
caching.
v3.0.0
Changelog: v2.1.0...v3.0.0
-
The concept of checks was replaced with generic operations. While you can implement custom operations there are three methods that simplify adding operations: checks, refinements, and alterations.
-
The plugin system was introduced. All built-in checks, such as
StringShape.length
andNumberShape.gte
, were moved to separate modules. -
Built-in plugins provide lots of new checks, for example
ArrayShape.includes
andObjectShape.allKeys
.DateShape
andBigIntShape
now have various checks as well. -
Previously, type inference was done using
INPUT
andOUTPUT
constants. They were replaced withInput
andOutput
types. -
Error codes now follow
<type>.<check>
notation, for example:number.int
,string.nonBlank
, etc. -
PromiseShape
can now be used in a sync context, if it doesn't parse the resolved value.
const shape = d.promise();
shape.coerce().parse('aaa');
// ⮕ Promise<'aaa'>
shape.isAsync;
// ⮕ false
LazyShape
can now be used to describe cyclic objects:
type Foo {
child?: Foo;
}
const parent: Foo = {};
// 👇 Cyclic objects are handled without stack overflow exceptions
parent.child = parent;
const lazyShape: d.Shape<Foo> = d.lazy(
() => d.object({
child: lazyShape
})
);
lazyShape.parse(parent);
// ⮕ parent
- Built-in type coercion can now be overridden with a custom callback. The callback passed to
corce
method is called only if the input value doesn't conform the requested type. If coercion isn't possible,d.NEVER
must be returned.
const yesNoShape = d.boolean().coerce(value => {
if (value === 'yes') {
return true;
}
if (value === 'no') {
return false;
}
// Coercion is not possible
return d.NEVER;
});
d.array(yesNoShape).parse(['yes', 'no'])
// ⮕ [true, false]
yesNoShape.parse('true')
// ❌ ValidationError: type at /: Must be a boolean
-
Shape._clone
was introduced to enable shape-specific cloning. -
Shape.check
signature was simplified to avoid the ambiguity betweenparam
andoptions
arguments. Nowparam
is the part ofCheckOptions
interface:
check<Param>(cb: CheckCallback<OutputValue, Param>, options: CheckOptions & { param: Param }): this;
check(cb: CheckCallback<OutputValue>, options?: CheckOptions): this;
- Readonly arrays of keys are now supported as an argument for in
pick
,omit
,partial
, andrequired
:
const keys = ['aaa'] as const;
d
.object({
aaa: d.string(),
bbb: d.number()
})
// 👇 No typing errors here anymore
.pick(keys)
// ⮕ Shape<{ aaa: string }>
-
FuntionShape.noWrap()
was replaced withFuntionShape.strict(options)
.FuntionShape
doesn't wrap input functions andstrinct
must be explicitly called to enable wrapping. -
ApplyOptions.verbose
was replaced withApplyOptions.earlyReturn
. Shapes now collect all issues by default, andearlyReturn: true
should be used to exit after the first issue is encountered. -
ObjectShape
now hasvalueShapes
property that holds an array of property shapes that is parallel tokeys
array. -
ObjectShape.keyof()
was replaced withObjectShape.keysShape
getter that returns the cached enum shape of known object keys.
Other changes
Removed members
Removed | Replacement |
---|---|
d.finite() |
d.number().finite() |
d.int() |
d.number().int() |
d.integer() |
d.number().int() |
BrandShape |
Shape<Branded<Value>> |
getSheck
, hasCheck
, and deleteCheck
were removed and don't have a replacement since checks were replaced with generic operations.
Renamed members
Old name | New name |
---|---|
Shape.transform |
Shape.convert |
Shape.transformAsync |
Shape.convertAsync |
CoercibleShape.isCoerced |
CoercibleShape.isCoercing |
ApplyOptions.coerced |
ApplyOptions.coerce |
FunctionShape.isWrapperAsync |
FunctionShape.isAsyncFunction |
FunctionShape.wrap |
FunctionShape.ensure |
FunctionShape.wrapAsync |
FunctionShape.ensureAsync |
ObjectShape.shapes |
ObjectShape.propShapes |
ObjectShape.shapes |
ObjectShape.propShapes |
ObjectShape.shapes |
ObjectShape.propShapes |
ObjectShape.shapes |
ObjectShape.propShapes |
KeysMode |
ObjectKeysMode |
SetShape.shape |
SetShape.valueShape |
NotShape.shape |
NotShape.baseShape |
TransformShape.callback |
ConvertShape.converter |
ReplaceShape.shape |
ReplaceShape.baseShape |
DenyShape.shape |
DenyShape.baseShape |
CatchShape.sahpe |
CatchShape.baseShape |
ExcludeShape.shape |
ExcludeShape.baseShape |
AllowLiteralShape |
AllowShape |
DenyLiteralShape |
DenyShape |
ReplaceLiteralShape |
ReplaceShape |
Literal |
Any |
ConstraintOptions |
IssueOptions |