@starbeam/verify
Advanced tools
Comparing version 0.7.3 to 0.8.0
# @starbeam/verify | ||
## 0.8.0 | ||
### Minor Changes | ||
- 1a553c5: Prepare for 0.8 | ||
### Patch Changes | ||
- Updated dependencies [1a553c5] | ||
- @starbeam/core-utils@0.8.0 | ||
## 0.7.3 | ||
@@ -4,0 +15,0 @@ |
@@ -0,1 +1,2 @@ | ||
import { isPresentArray } from "@starbeam/core-utils"; | ||
type FixedArray<T, N extends number, SoFar extends unknown[] = [ | ||
@@ -11,3 +12,3 @@ ]> = SoFar["length"] extends N ? SoFar : FixedArray<T, N, [ | ||
]>; | ||
declare function isPresent<T>(value: T | null | undefined | void): value is T; | ||
declare function isPresent<T>(value: T | null | undefined): value is Exclude<T, null | undefined>; | ||
declare function exhaustive(_value: never, type?: string): never; | ||
@@ -17,2 +18,3 @@ declare function isEqual<T>(value: T): (other: unknown) => other is T; | ||
declare function isObject(value: unknown): value is object; | ||
declare function isWeakKey(value: unknown): value is Record<string, unknown>; | ||
interface HasLength<L extends number> { | ||
@@ -23,9 +25,6 @@ <T>(value: T[]): value is FixedArray<T, L>; | ||
declare function hasLength<L extends number>(length: L): HasLength<L>; | ||
declare function hasItems<T>(value: readonly T[]): value is [ | ||
T, | ||
...(readonly T[]) | ||
]; | ||
declare const hasItems: typeof isPresentArray; | ||
declare function isNullable<In, Out extends In>(verifier: (value: In) => value is Out): (value: In | null) => value is Out | null; | ||
declare function isOneOf<In, Out extends In>(...verifiers: ((value: In) => value is Out)[]): (value: In) => value is Out; | ||
declare function hasType<K extends keyof TypeOfTypes>(type: K): (value: unknown) => value is TypeOfTypes[K]; | ||
declare function hasType<K extends keyof TypeOfTypes>(type: K): (value: any) => value is TypeOfTypes[K]; | ||
interface TypeOfTypes { | ||
@@ -40,3 +39,4 @@ string: string; | ||
object: object; | ||
function: (...args: unknown[]) => unknown; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function: (...args: any[]) => unknown; | ||
} | ||
@@ -48,6 +48,9 @@ type TypeOf = keyof TypeOfTypes; | ||
} | ||
declare function verify<T, U extends T>(value: T, check: (input: T) => input is U, error?: Expectation<T>): asserts value is U; | ||
declare function verify<T>(value: T, check: (input: T) => boolean, error?: Expectation<T>): asserts value is T; | ||
type VerifyFn = <Input extends Value, Value, Output extends Input>(value: Value, check: (input: Input) => input is Output, error?: Expectation<Value>) => asserts value is Output & Value; | ||
declare function verify<Input extends Value, Value, Output extends Input>(value: Value, check: (input: Input) => input is Output, error?: Expectation<Value>): asserts value is Output & Value; | ||
declare namespace verify { | ||
var noop: <T, U extends T>(value: T, _check: ((input: T) => input is U) | ((input: T) => boolean), _error?: Expectation<T> | undefined) => asserts value is U; | ||
var noop: typeof verify | { | ||
<Input, Output extends Input, Value extends Output>(value: Value, check: (input: Input) => input is Output, error?: Expectation<Value> | undefined): void; | ||
<Input_1, Value_1 extends Input_1, Output_1 extends Input_1>(value: Value_1, check: (input: Input_1) => input is Output_1, error?: Expectation<Value_1> | undefined): asserts value is Output_1 & Value_1; | ||
}; | ||
} | ||
@@ -68,3 +71,3 @@ declare function verified<T, U extends T>(value: T, check: (input: T) => input is U, error?: Expectation<T>): U; | ||
toHave(items: string): Expectation<In>; | ||
butGot<In>(kind: string | ((value: In) => string)): Expectation<In>; | ||
butGot<NewIn extends In>(kind: string | ((value: NewIn) => string)): Expectation<NewIn>; | ||
when(situation: string): Expectation<In>; | ||
@@ -85,16 +88,15 @@ message(input: In): string; | ||
var butGot: <In>(kind: string | ((input: In) => string)) => Expectation<In>; | ||
var associate: <Check extends (input: In) => any, In>(check: Check, expected: Expectation<In>) => Check extends infer C ? C : never; | ||
var associate: <Check extends (input: In) => any, In>(check: Check, expectation: Expectation<In>) => Check extends infer C ? C : never; | ||
var updated: <In, NewIn = In>(check: (input: In) => boolean, updater: Updater<In, NewIn>) => Expectation<NewIn>; | ||
} | ||
interface Updater<In, NewIn = In> { | ||
description?: (description: string | void | undefined) => string | void; | ||
to?: (to: To | void | undefined) => string | To | void; | ||
actual?: (actual: ((input: In) => string | void) | void | undefined) => ((input: NewIn) => string | void) | void; | ||
when?: (when: string | void | undefined) => string | void; | ||
description?: (description: string | undefined) => string | undefined; | ||
to?: (to: To | undefined) => string | To | undefined; | ||
actual?: (actual: ((input: In) => string | undefined) | undefined) => ((input: NewIn) => string | undefined) | undefined; | ||
when?: (when: string | undefined) => string | undefined; | ||
} | ||
declare const verifiedDev: typeof verified; | ||
declare const verifyDev: typeof verify; | ||
declare const verify$0: typeof verifyDev["noop"]; | ||
declare const verify$0: VerifyFn; | ||
declare const verified$0: typeof verifiedDev["noop"]; | ||
export { exhaustive, hasItems, hasLength, isEqual, isNotEqual, isNullable, isObject, isPresent, isOneOf, TypeOf, hasType, Expectation, expected, VerificationError, verify$0 as verify, verified$0 as verified }; | ||
export { exhaustive, hasItems, hasLength, isEqual, isNotEqual, isNullable, isObject, isPresent, isWeakKey, isOneOf, TypeOf, hasType, Expectation, expected, VerificationError, verify$0 as verify, verified$0 as verified }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -0,1 +1,3 @@ | ||
import { isPresentArray } from '@starbeam/core-utils'; | ||
class VerificationError extends Error { | ||
@@ -6,3 +8,2 @@ constructor(message, expectation) { | ||
} | ||
} | ||
@@ -13,3 +14,2 @@ function verify$1(value, check, error) { | ||
const expectation = Expectation.merge(associated, error); | ||
if (expectation === undefined) { | ||
@@ -23,7 +23,9 @@ const name = check.name; | ||
} | ||
verify$1.noop = (value, _check, _error) => { | ||
verify$1.noop = () => { | ||
/** noop */ | ||
}; | ||
function noop() { | ||
return; | ||
}; | ||
} | ||
verify$1.noop = noop; | ||
function verified$1(value, check, error) { | ||
@@ -33,8 +35,7 @@ verify$1(value, check, error); | ||
} | ||
verified$1.noop = (value, _check, _error) => { | ||
return value; | ||
}; // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
}; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
class Expectation { | ||
@@ -44,3 +45,2 @@ static create(description) { | ||
} | ||
static merge(associated, specified) { | ||
@@ -50,14 +50,10 @@ if (!associated && !specified) { | ||
} | ||
if (!associated) { | ||
return specified; | ||
} | ||
if (!specified) { | ||
return associated; | ||
} | ||
return new Expectation(specified.#description, specified.#to ?? associated.#to, specified.#actual ?? associated.#actual, specified.#when ?? associated.#when); | ||
} | ||
#description; | ||
@@ -67,3 +63,2 @@ #to; | ||
#when; | ||
constructor(description, to, got, when) { | ||
@@ -75,54 +70,47 @@ this.#description = description; | ||
} | ||
as(description) { | ||
return new Expectation(description, this.#to, this.#actual, this.#when); | ||
} | ||
update(updater) { | ||
var _this$to; | ||
const description = updater.description ? updater.description(this.#description) : this.#description; | ||
const updatedTo = updater.to ? updater.to(this.#to) : this.#to; | ||
const to = typeof updatedTo === "string" ? [((_this$to = this.#to) === null || _this$to === void 0 ? void 0 : _this$to[0]) ?? "to be", updatedTo] : updatedTo; | ||
const to = typeof updatedTo === "string" ? [toRelationship(this.#to) ?? "to be", updatedTo] : updatedTo; | ||
const actual = updater.actual ? updater.actual(this.#actual) : this.#actual; | ||
return new Expectation(description, to, actual, updater.when ? updater.when(this.#when) : this.#when); | ||
} | ||
toBe(kind) { | ||
return new Expectation(this.#description, ["to be", kind], this.#actual, this.#when); | ||
} | ||
toHave(items) { | ||
return new Expectation(this.#description, ["to have", items], this.#actual, this.#when); | ||
} | ||
butGot(kind) { | ||
return new Expectation(this.#description, this.#to, typeof kind === "string" ? () => kind : kind, this.#when); | ||
} | ||
when(situation) { | ||
return new Expectation(this.#description, this.#to, this.#actual, situation); | ||
} | ||
message(input) { | ||
let message = ``; | ||
if (this.#when) { | ||
message += `When ${this.#when}: `; | ||
} | ||
message += `Expected ${this.#description ?? "value"}`; | ||
if (this.#to) { | ||
message += ` ${this.#to[0]} ${this.#to[1]}`; | ||
message += ` ${toRelationship(this.#to)} ${toKind(this.#to)}`; | ||
} | ||
if (this.#actual) { | ||
message += `, but got ${String(this.#actual(input))}`; | ||
message += `, but it was ${String(this.#actual(input))}`; | ||
} | ||
return message; | ||
} | ||
} | ||
const REL_INDEX = 0; | ||
const KIND_INDEX = 1; | ||
function toRelationship(to) { | ||
return to === null || to === void 0 ? void 0 : to[REL_INDEX]; | ||
} | ||
function toKind(to) { | ||
return to === null || to === void 0 ? void 0 : to[KIND_INDEX]; | ||
} | ||
function expected(description) { | ||
@@ -132,19 +120,15 @@ return Expectation.create(description); | ||
expected.as = expected; | ||
expected.toBe = kind => expected().toBe(kind); | ||
expected.toHave = items => expected().toHave(items); | ||
expected.when = situation => expected().when(situation); | ||
expected.butGot = kind => expected().butGot(kind); | ||
expected.butGot = kind => expected().butGot(kind); // eslint-disable-next-line | ||
// eslint-disable-next-line | ||
const ASSOCIATED = new WeakMap(); | ||
const ASSOCIATED = new WeakMap(); // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
expected.associate = (check, expected) => { | ||
ASSOCIATED.set(check, expected); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
expected.associate = (check, expectation) => { | ||
ASSOCIATED.set(check, expectation); | ||
return check; | ||
}; | ||
expected.updated = (check, updater) => { | ||
@@ -163,13 +147,10 @@ const expectation = ASSOCIATED.get(check) ?? expected(); | ||
return String(value); | ||
case "bigint": | ||
return `${value}n`; | ||
case "string": | ||
return JSON.stringify(value); | ||
case "function": | ||
{ | ||
const fn = String(value); // if it's an ES6 class; detect by using F.p.toString and looking for class | ||
const fn = String(value); | ||
// if it's an ES6 class; detect by using F.p.toString and looking for class | ||
if (fn.startsWith("class")) { | ||
@@ -187,3 +168,2 @@ return `{class ${value.name}}`; | ||
} | ||
case "object": | ||
@@ -194,5 +174,3 @@ { | ||
} | ||
const proto = Object.getPrototypeOf(value); | ||
if (proto === null || proto === Object.prototype) { | ||
@@ -212,3 +190,2 @@ const entries = Object.entries(value).map(_ref => { | ||
/* eslint-enable */ | ||
} | ||
@@ -231,5 +208,13 @@ | ||
} | ||
return expected.associate(verify, expected.toBe(String(value)).butGot(format)); | ||
return expected.associate(verify, expected.toBe(inspect(value)).butGot(format)); | ||
} | ||
function inspect(value) { | ||
if (isObject(value) && Symbol.for("nodejs.util.inspect.custom") in value) { | ||
return JSON.stringify( | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call | ||
value[Symbol.for("nodejs.util.inspect.custom")]()); | ||
} else { | ||
return JSON.stringify(value); | ||
} | ||
} | ||
function isNotEqual(value) { | ||
@@ -239,3 +224,2 @@ function verify(input) { | ||
} | ||
return expected.associate(verify, expected.toBe(`not ${String(value)}`).butGot(format)); | ||
@@ -247,2 +231,6 @@ } | ||
expected.associate(isObject, expected.toBe("an object").butGot(value => value === null ? "null" : typeof value)); | ||
function isWeakKey(value) { | ||
return (typeof value === "object" || typeof value === "function") && value !== null; | ||
} | ||
expected.associate(isWeakKey, expected.toBe("an object or function").butGot(value => value === null ? "null" : typeof value)); | ||
function hasLength(length) { | ||
@@ -252,8 +240,12 @@ function has(value) { | ||
} | ||
return expected.associate(has, expected.toHave(`${length} items`)); | ||
} | ||
function hasItems(value) { | ||
return value.length > 0; | ||
} | ||
const hasItems = isPresentArray; | ||
// export function hasItems<T>( | ||
// value: readonly T[] | ||
// ): value is [T, ...(readonly T[])] { | ||
// return value.length > 0; | ||
// } | ||
expected.associate(hasItems, expected.toHave(`at least one item`)); | ||
@@ -268,3 +260,2 @@ function isNullable(verifier) { | ||
} | ||
const expectation = expected.updated(verifier, { | ||
@@ -275,3 +266,3 @@ to: to => { | ||
} else { | ||
return `${to[1]} or null`; | ||
return `${toKind(to)} or null`; | ||
} | ||
@@ -299,3 +290,2 @@ }, | ||
} | ||
function verify(input) { | ||
@@ -307,6 +297,4 @@ for (const verifier of verifiers) { | ||
} | ||
return false; | ||
} | ||
const expectation = expected.updated(verify, { | ||
@@ -317,3 +305,3 @@ to: to => { | ||
} else { | ||
return `${to[1]} or any`; | ||
return `${toKind(to)} or any`; | ||
} | ||
@@ -341,6 +329,6 @@ }, | ||
} | ||
/** | ||
* Define a property that ECMAScript provides by default but allows us to override, such as `Function.prototype.name` and `Symbol.toStringTag`. | ||
*/ | ||
define.builtin = (object, property, value) => { | ||
@@ -356,5 +344,13 @@ Object.defineProperty(object, property, { | ||
function hasType(type) { | ||
return IS_TYPEOF[type]; | ||
} | ||
const TYPE_DESC = { | ||
object: "an object", | ||
null: "null", | ||
undefined: "undefined", | ||
function: "a function", | ||
string: "a string", | ||
number: "a number", | ||
boolean: "a boolean", | ||
symbol: "a symbol", | ||
bigint: "a bigint" | ||
}; | ||
const IS_TYPEOF = { | ||
@@ -371,2 +367,5 @@ object: isObject, | ||
}; | ||
function hasType(type) { | ||
return IS_TYPEOF[type]; | ||
} | ||
/** | ||
@@ -376,3 +375,2 @@ * Verify that a value has a specified typeof type. If `type` is `"object"`, | ||
*/ | ||
function isTypeof(type) { | ||
@@ -382,10 +380,8 @@ if (type === "object") { | ||
} | ||
const verify = define.builtin(value => { | ||
return typeof value === type; | ||
}, "name", `is${type}`); | ||
}, "name", `is:${type}`); | ||
define.builtin(verify, Symbol.toStringTag, `Verifier`); | ||
return expected.associate(verify, expected.toBe(type).butGot(typeName)); | ||
return expected.associate(verify, expected.toBe(TYPE_DESC[type]).butGot(typeName)); | ||
} | ||
function typeName(value) { | ||
@@ -398,3 +394,3 @@ return value === null ? "null" : typeof value; | ||
export { VerificationError, exhaustive, expected, hasItems, hasLength, hasType, isEqual, isNotEqual, isNullable, isObject, isOneOf, isPresent, verified, verify }; | ||
export { VerificationError, exhaustive, expected, hasItems, hasLength, hasType, isEqual, isNotEqual, isNullable, isObject, isOneOf, isPresent, isWeakKey, verified, verify }; | ||
//# sourceMappingURL=index.js.map |
11
index.ts
@@ -10,2 +10,3 @@ export { | ||
isPresent, | ||
isWeakKey, | ||
} from "./src/assertions/basic.js"; | ||
@@ -16,9 +17,13 @@ export { isOneOf } from "./src/assertions/multi.js"; | ||
import { verified as verifiedDev, verify as verifyDev } from "./src/verify.js"; | ||
export const verify: typeof verifyDev["noop"] = import.meta.env.DEV | ||
import { | ||
type VerifyFn, | ||
verified as verifiedDev, | ||
verify as verifyDev, | ||
} from "./src/verify.js"; | ||
export const verify: VerifyFn = import.meta.env.DEV | ||
? verifyDev | ||
: verifyDev.noop; | ||
export const verified: typeof verifiedDev["noop"] = import.meta.env.DEV | ||
? verifiedDev | ||
: verifiedDev.noop; |
{ | ||
"name": "@starbeam/verify", | ||
"version": "0.8.0", | ||
"type": "module", | ||
"main": "dist/index.cjs", | ||
"version": "0.7.3", | ||
"starbeam:type": "library", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
@@ -14,3 +14,14 @@ ".": { | ||
}, | ||
"types": "dist/index.d.ts" | ||
"starbeam:type": "library:public", | ||
"dependencies": { | ||
"@starbeam/core-utils": "^0.8.0" | ||
}, | ||
"devDependencies": { | ||
"@starbeam-dev/build-support": "1.0.0" | ||
}, | ||
"scripts": { | ||
"test:lint": "eslint", | ||
"test:specs": "vitest --run", | ||
"test:types": "tsc -b" | ||
} | ||
} |
@@ -1,6 +0,8 @@ | ||
import { expected } from "../verify.js"; | ||
import { expected, toKind } from "../verify.js"; | ||
import { format } from "./describe.js"; | ||
import type { FixedArray, ReadonlyFixedArray } from "./type-utils.js"; | ||
export function isPresent<T>(value: T | null | undefined | void): value is T { | ||
export function isPresent<T>( | ||
value: T | null | undefined | ||
): value is Exclude<T, null | undefined> { | ||
return value !== null && value !== undefined; | ||
@@ -35,6 +37,17 @@ } | ||
verify, | ||
expected.toBe(String(value)).butGot(format) | ||
expected.toBe(inspect(value)).butGot(format) | ||
); | ||
} | ||
function inspect(value: unknown): string { | ||
if (isObject(value) && Symbol.for("nodejs.util.inspect.custom") in value) { | ||
return JSON.stringify( | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call | ||
(value as any)[Symbol.for("nodejs.util.inspect.custom")]() | ||
); | ||
} else { | ||
return JSON.stringify(value); | ||
} | ||
} | ||
export function isNotEqual<T>( | ||
@@ -64,2 +77,15 @@ value: T | ||
export function isWeakKey(value: unknown): value is Record<string, unknown> { | ||
return ( | ||
(typeof value === "object" || typeof value === "function") && value !== null | ||
); | ||
} | ||
expected.associate( | ||
isWeakKey, | ||
expected | ||
.toBe("an object or function") | ||
.butGot((value) => (value === null ? "null" : typeof value)) | ||
); | ||
interface HasLength<L extends number> { | ||
@@ -78,8 +104,12 @@ <T>(value: T[]): value is FixedArray<T, L>; | ||
export function hasItems<T>( | ||
value: readonly T[] | ||
): value is [T, ...(readonly T[])] { | ||
return value.length > 0; | ||
} | ||
import { isPresentArray } from "@starbeam/core-utils"; | ||
export const hasItems = isPresentArray; | ||
// export function hasItems<T>( | ||
// value: readonly T[] | ||
// ): value is [T, ...(readonly T[])] { | ||
// return value.length > 0; | ||
// } | ||
expected.associate(hasItems, expected.toHave(`at least one item`)); | ||
@@ -103,3 +133,3 @@ | ||
} else { | ||
return `${to[1]} or null`; | ||
return `${toKind(to)} or null`; | ||
} | ||
@@ -106,0 +136,0 @@ }, |
@@ -1,2 +0,2 @@ | ||
import { expected } from "../verify.js"; | ||
import { expected, toKind } from "../verify.js"; | ||
@@ -21,3 +21,3 @@ export function isOneOf<In, Out extends In>( | ||
} else { | ||
return `${to[1]} or any`; | ||
return `${toKind(to)} or any`; | ||
} | ||
@@ -24,0 +24,0 @@ }, |
@@ -5,5 +5,30 @@ import { define } from "../define.js"; | ||
const TYPE_DESC = { | ||
object: "an object", | ||
null: "null", | ||
undefined: "undefined", | ||
function: "a function", | ||
string: "a string", | ||
number: "a number", | ||
boolean: "a boolean", | ||
symbol: "a symbol", | ||
bigint: "a bigint", | ||
}; | ||
const IS_TYPEOF = { | ||
object: isObject, | ||
null: isEqual(null), | ||
undefined: isTypeof("undefined"), | ||
function: isTypeof("function"), | ||
string: isTypeof("string"), | ||
number: isTypeof("number"), | ||
boolean: isTypeof("boolean"), | ||
symbol: isTypeof("symbol"), | ||
bigint: isTypeof("bigint"), | ||
} as const; | ||
export function hasType<K extends keyof TypeOfTypes>( | ||
type: K | ||
): (value: unknown) => value is TypeOfTypes[K] { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
): (value: any) => value is TypeOfTypes[K] { | ||
return IS_TYPEOF[type] as (value: unknown) => value is TypeOfTypes[K]; | ||
@@ -21,3 +46,4 @@ } | ||
object: object; | ||
function: (...args: unknown[]) => unknown; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function: (...args: any[]) => unknown; | ||
} | ||
@@ -27,14 +53,2 @@ | ||
const IS_TYPEOF = { | ||
object: isObject, | ||
null: isEqual(null), | ||
undefined: isTypeof("undefined"), | ||
function: isTypeof("function"), | ||
string: isTypeof("string"), | ||
number: isTypeof("number"), | ||
boolean: isTypeof("boolean"), | ||
symbol: isTypeof("symbol"), | ||
bigint: isTypeof("bigint"), | ||
} as const; | ||
/** | ||
@@ -56,3 +70,3 @@ * Verify that a value has a specified typeof type. If `type` is `"object"`, | ||
"name", | ||
`is${type}` | ||
`is:${type}` | ||
); | ||
@@ -62,3 +76,6 @@ | ||
return expected.associate(verify, expected.toBe(type).butGot(typeName)); | ||
return expected.associate( | ||
verify, | ||
expected.toBe(TYPE_DESC[type]).butGot(typeName) | ||
); | ||
} | ||
@@ -65,0 +82,0 @@ |
@@ -7,17 +7,18 @@ export class VerificationError<T = unknown> extends Error { | ||
export function verify<T, U extends T>( | ||
value: T, | ||
check: (input: T) => input is U, | ||
error?: Expectation<T> | ||
): asserts value is U; | ||
export function verify<T>( | ||
value: T, | ||
check: (input: T) => boolean, | ||
error?: Expectation<T> | ||
): asserts value is T; | ||
export function verify<T, U extends T>( | ||
value: T, | ||
check: ((input: T) => input is U) | ((input: T) => boolean), | ||
error?: Expectation<T> | ||
): asserts value is U { | ||
export type VerifyFn = <Input extends Value, Value, Output extends Input>( | ||
value: Value, | ||
check: (input: Input) => input is Output, | ||
error?: Expectation<Value> | ||
) => asserts value is Output & Value; | ||
export function verify<Input extends Value, Value, Output extends Input>( | ||
value: Value, | ||
check: (input: Input) => input is Output, | ||
error?: Expectation<Value> | ||
): asserts value is Output & Value; | ||
export function verify<Value, Narrow extends Value>( | ||
value: Value, | ||
check: (input: Value) => input is Narrow, | ||
error?: Expectation<Value> | ||
): asserts value is Narrow { | ||
if (!check(value)) { | ||
@@ -39,10 +40,22 @@ const associated = ASSOCIATED.get(check); | ||
verify.noop = <T, U extends T>( | ||
value: T, | ||
_check: ((input: T) => input is U) | ((input: T) => boolean), | ||
_error?: Expectation<T> | ||
): asserts value is U => { | ||
verify.noop = (() => { | ||
/** noop */ | ||
}) as unknown as Exclude<typeof verify, "noop">; | ||
function noop<Input, Output extends Input, Value extends Output>( | ||
value: Value, | ||
check: (input: Input) => input is Output, | ||
error?: Expectation<Value> | ||
): void; | ||
function noop<Input, Value extends Input, Output extends Input>( | ||
value: Value, | ||
check: (input: Input) => input is Output, | ||
error?: Expectation<Value> | ||
): asserts value is Output & Value; | ||
function noop(): void { | ||
return; | ||
}; | ||
} | ||
verify.noop = noop; | ||
export function verified<T, U extends T>( | ||
@@ -95,15 +108,12 @@ value: T, | ||
readonly #description: string | void | undefined; | ||
readonly #to: To | void | undefined; | ||
readonly #actual: | ||
| ((input: In) => string | void | undefined) | ||
| void | ||
| undefined; | ||
readonly #when: string | void | undefined; | ||
readonly #description: string | undefined; | ||
readonly #to: To | undefined; | ||
readonly #actual: ((input: In) => string | undefined) | undefined; | ||
readonly #when: string | undefined; | ||
private constructor( | ||
description: string | void | undefined, | ||
to: To | void | undefined, | ||
got: ((input: In) => string | void | undefined) | void | undefined, | ||
when: string | void | undefined | ||
description: string | undefined, | ||
to: To | undefined, | ||
got: ((input: In) => string | undefined) | undefined, | ||
when: string | undefined | ||
) { | ||
@@ -125,5 +135,5 @@ this.#description = description; | ||
const updatedTo = updater.to ? updater.to(this.#to) : this.#to; | ||
const to: To | void = | ||
const to: To | undefined = | ||
typeof updatedTo === "string" | ||
? [this.#to?.[0] ?? "to be", updatedTo] | ||
? [toRelationship(this.#to) ?? "to be", updatedTo] | ||
: updatedTo; | ||
@@ -160,3 +170,5 @@ const actual = updater.actual | ||
butGot<In>(kind: string | ((value: In) => string)): Expectation<In> { | ||
butGot<NewIn extends In>( | ||
kind: string | ((value: NewIn) => string) | ||
): Expectation<NewIn> { | ||
return new Expectation( | ||
@@ -189,7 +201,7 @@ this.#description, | ||
if (this.#to) { | ||
message += ` ${this.#to[0]} ${this.#to[1]}`; | ||
message += ` ${toRelationship(this.#to)} ${toKind(this.#to)}`; | ||
} | ||
if (this.#actual) { | ||
message += `, but got ${String(this.#actual(input))}`; | ||
message += `, but it was ${String(this.#actual(input))}`; | ||
} | ||
@@ -202,4 +214,20 @@ | ||
export type Relationship = "to be" | "to have" | "to be one of"; | ||
const REL_INDEX = 0; | ||
const KIND_INDEX = 1; | ||
export type To = [relationship: Relationship, kind: string]; | ||
export function toRelationship(to: To): Relationship; | ||
export function toRelationship(to?: To): Relationship | undefined; | ||
export function toRelationship(to?: To): Relationship | undefined { | ||
return to?.[REL_INDEX]; | ||
} | ||
export function toKind(to: To): string; | ||
export function toKind(to?: To): string | undefined; | ||
export function toKind(to?: To): string | undefined { | ||
return to?.[KIND_INDEX]; | ||
} | ||
export function expected(description?: string): Expectation { | ||
@@ -223,5 +251,5 @@ return Expectation.create(description); | ||
check: Check, | ||
expected: Expectation<In> | ||
expectation: Expectation<In> | ||
): Check extends infer C ? C : never => { | ||
ASSOCIATED.set(check, expected); | ||
ASSOCIATED.set(check, expectation); | ||
return check as Check extends infer C ? C : never; | ||
@@ -231,8 +259,8 @@ }; | ||
interface Updater<In, NewIn = In> { | ||
description?: (description: string | void | undefined) => string | void; | ||
to?: (to: To | void | undefined) => string | To | void; | ||
description?: (description: string | undefined) => string | undefined; | ||
to?: (to: To | undefined) => string | To | undefined; | ||
actual?: ( | ||
actual: ((input: In) => string | void) | void | undefined | ||
) => ((input: NewIn) => string | void) | void; | ||
when?: (when: string | void | undefined) => string | void; | ||
actual: ((input: In) => string | undefined) | undefined | ||
) => ((input: NewIn) => string | undefined) | undefined; | ||
when?: (when: string | undefined) => string | undefined; | ||
} | ||
@@ -239,0 +267,0 @@ |
@@ -10,14 +10,12 @@ import "./support.js"; | ||
test("isPresent", () => { | ||
expect((value: unknown) => verify(value, isPresent)).toFail( | ||
null, | ||
expected.toBe("present") | ||
); | ||
expect((value: unknown) => { | ||
verify(value, isPresent); | ||
}).toFail(null, expected.toBe("present")); | ||
}); | ||
test("isEqual", () => { | ||
expect((value: unknown) => verify(value, isEqual(null))).toFail( | ||
undefined, | ||
expected.toBe("null").butGot("undefined") | ||
); | ||
expect((value: unknown) => { | ||
verify(value, isEqual(null)); | ||
}).toFail(undefined, expected.toBe("null").butGot("undefined")); | ||
}); | ||
}); |
@@ -39,9 +39,9 @@ import { expected } from "@starbeam/verify"; | ||
expect(e.message({ nodeType: 1 })).toEqual( | ||
`When appending to the DOM: Expected node to be a text node, but got {"nodeType":1}` | ||
`When appending to the DOM: Expected node to be a text node, but it was {"nodeType":1}` | ||
); | ||
expect(e.message({ toJSON: () => `something weird` })).toEqual( | ||
`When appending to the DOM: Expected node to be a text node, but got "something weird"` | ||
`When appending to the DOM: Expected node to be a text node, but it was "something weird"` | ||
); | ||
}); | ||
}); |
@@ -7,16 +7,9 @@ { | ||
"starbeam:type": "tests", | ||
"scripts": { | ||
"test:lint": "eslint", | ||
"test:types": "tsc -b" | ||
}, | ||
"dependencies": { | ||
"@starbeam/verify": "workspace:^" | ||
}, | ||
"publishConfig": { | ||
"main": "dist/index.cjs", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"import": "./dist/index.js", | ||
"default": "./dist/index.cjs" | ||
} | ||
} | ||
} | ||
} |
@@ -66,5 +66,5 @@ import type { Expectation } from "@starbeam/verify"; | ||
interface Matchers<R = unknown> { | ||
toFail<T>(value: T, expectation: Expectation): R; | ||
toFail: <T>(value: T, expectation: Expectation) => R; | ||
} | ||
} | ||
} |
{ | ||
"extends": "../../../.config/tsconfig/tsconfig.-package.json", | ||
"extends": "../../../../.config/tsconfig/tsconfig.shared.json", | ||
"compilerOptions": { | ||
"outDir": "../../../dist/packages", | ||
"composite": true, | ||
"declaration": true, | ||
"declarationDir": "../../../../dist/types", | ||
"declarationMap": true, | ||
"declarationDir": "../../../dist/types", | ||
"declaration": true, | ||
"composite": true, | ||
"types": ["../../env"] | ||
} | ||
"outDir": "../../../../dist/packages", | ||
"types": ["../../../env"] | ||
}, | ||
"exclude": ["dist/**/*"] | ||
} |
import { expected, verify } from "@starbeam/verify"; | ||
import { describe, expect, test } from "vitest"; | ||
const isProd: boolean = import.meta.env.PROD; | ||
const isProd = !!import.meta.env.PROD; | ||
@@ -14,5 +14,5 @@ describe.skipIf(isProd)("verify", () => { | ||
expect(() => verify(absent, isPresent)).toThrowError( | ||
"Assumption was incorrect: isPresent" | ||
); | ||
expect(() => { | ||
verify(absent, isPresent); | ||
}).toThrowError("Assumption was incorrect: isPresent"); | ||
}); | ||
@@ -27,3 +27,3 @@ | ||
expect(() => | ||
expect(() => { | ||
verify( | ||
@@ -35,4 +35,4 @@ absent, | ||
.butGot(() => String(absent)) | ||
) | ||
).toThrowError("Expected absent to be present, but got null"); | ||
); | ||
}).toThrowError("Expected absent to be present, but it was null"); | ||
}); | ||
@@ -49,3 +49,3 @@ | ||
expect(() => | ||
expect(() => { | ||
verify( | ||
@@ -55,4 +55,4 @@ absent, | ||
expected("absent").butGot(() => String(absent)) | ||
) | ||
).toThrowError("Expected absent to be present, but got null"); | ||
); | ||
}).toThrowError("Expected absent to be present, but it was null"); | ||
}); | ||
@@ -69,3 +69,3 @@ | ||
expect(() => | ||
expect(() => { | ||
verify( | ||
@@ -75,7 +75,7 @@ absent, | ||
expected.when("some scenario").butGot(() => String(absent)) | ||
) | ||
).toThrowError( | ||
"When some scenario: Expected value to be present, but got null" | ||
); | ||
}).toThrowError( | ||
"When some scenario: Expected value to be present, but it was null" | ||
); | ||
}); | ||
}); |
{ | ||
"extends": "../../.config/tsconfig/tsconfig.-package.json", | ||
"extends": "../../../.config/tsconfig/tsconfig.shared.json", | ||
"compilerOptions": { | ||
"outDir": "../../dist/packages", | ||
"composite": true, | ||
"declaration": true, | ||
"composite": true, | ||
"declarationDir": "../../../dist/types", | ||
"declarationMap": true, | ||
"declarationDir": "../../dist/types", | ||
"types": ["../env"] | ||
} | ||
"outDir": "../../../dist/packages", | ||
"types": ["../../env"] | ||
}, | ||
"exclude": ["dist/**/*"] | ||
} |
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
133684
30
1671
1
1
+ Added@starbeam/core-utils@^0.8.0
+ Added@starbeam/core-utils@0.8.9(transitive)