succulent
Version:
Powerful and easy runtime type checking
489 lines (461 loc) • 12.9 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/filters/length.ts
function hasLength(length) {
return (x) => x.length === length;
}
function minLength(length) {
return (x) => x.length >= length;
}
function maxLength(length) {
return (x) => x.length <= length;
}
function nonEmpty(x) {
return x.length > 0;
}
// src/filters/matches.ts
function matches(expression) {
return function(t) {
return expression.test(t);
};
}
// src/filters/range.ts
function inRange(min, max) {
return (x) => min <= x && x <= max;
}
// src/base/errorMessages.ts
var errorMessages_exports = {};
__export(errorMessages_exports, {
invalidProperty: () => invalidProperty,
invalidValue: () => invalidValue
});
// src/base/toDisplayKey.ts
function toDisplayKey(x) {
switch (typeof x) {
case "string":
return x;
default:
return `[${String(x)}]`;
}
}
// src/base/toDisplayString.ts
function toDisplayString(x) {
switch (typeof x) {
case "bigint":
return `${x}n`;
case "string":
return `"${x}"`;
default:
return String(x);
}
}
// src/base/errorMessages.ts
function invalidProperty(key, schema) {
const displayKey = toDisplayKey(key);
const typeName = Schema.displayName(schema);
return `Expected property ${displayKey} to have type ${typeName}`;
}
function invalidValue(value, schema) {
const displayValue = toDisplayString(value);
const typeName = Schema.displayName(schema);
return `Expected ${typeName}, got ${displayValue}`;
}
// src/base/indent.ts
function indent(str, level = 2) {
const indentation = typeof level === "number" ? " ".repeat(level) : level;
return str.split("\n").map((line) => `${indentation}${line}`).join("\n");
}
// src/base/trace.ts
function trace(message, error) {
if (error instanceof Error) {
return `${message}
${indent(error.message)}`;
}
return message;
}
// src/base/keyReporter.ts
function keyReporter(check2, onError) {
const errorTraces = [];
const report = (...args) => {
try {
check2(...args);
} catch (error) {
errorTraces.push(trace(onError(...args), error));
}
};
const resolve = () => {
if (errorTraces.length > 0) {
throw new TypeError(errorTraces.join("\n"));
}
return true;
};
return [report, resolve];
}
// src/schema.ts
var Schema = class {
static check(base, x) {
if (base instanceof Schema) {
return base.check(x);
}
return new Schema(base).check(x);
}
static is(base, x, ref) {
try {
Schema.check(base, x);
return true;
} catch (error) {
if (ref && error instanceof Error)
ref.error = error;
return false;
}
}
static displayName(base) {
return Schema.from(base).displayName;
}
static every(base, predicate) {
return Array.from(Schema.from(base)).every(predicate);
}
static from(base) {
if (base instanceof Schema) {
return base;
}
return new Schema(base);
}
[Symbol.iterator] = function* () {
};
_check;
displayName = "(unknown)";
constructor(base, options = {}) {
const { displayName, iter } = options;
if (base instanceof Schema) {
this._check = base._check;
this.displayName = displayName || base.displayName;
this[Symbol.iterator] = iter ?? base[Symbol.iterator];
return;
}
if (typeof base === "function") {
this._check = base;
if (displayName)
this.displayName = displayName;
if (iter)
this[Symbol.iterator] = iter;
return;
}
this._check = (x) => Object.is(x, base);
this.displayName = displayName || toDisplayString(base);
this[Symbol.iterator] = iter ?? function* () {
yield base;
};
}
check(x) {
let ok;
try {
ok = this._check(x);
} catch (error) {
throw new TypeError(trace(errorMessages_exports.invalidValue(x, this), error));
}
if (!ok) {
throw new TypeError(errorMessages_exports.invalidValue(x, this));
}
return true;
}
that(...filters) {
return new Schema((x) => this.check(x) && filters.every((filter) => filter(x)));
}
toString() {
return this.displayName;
}
};
// src/operators/check.ts
function check(x, schema) {
if (!Schema.check(schema, x)) {
throw new Error("check returned false instead of throwing, which is bad");
}
}
var guard = check;
// src/operators/is.ts
function is(x, schema, ref) {
return Schema.is(schema, x, ref);
}
// src/operators/iterables.ts
function oneOf(x) {
return new Schema((t) => {
for (const value of x) {
if (Object.is(t, value)) {
return true;
}
}
return false;
});
}
// src/operators/logic.ts
function union(...schemas) {
return new Schema((t) => schemas.some((schema) => is(t, schema)), {
displayName: `(${schemas.map((schema) => Schema.from(schema).displayName).join(" | ")})`,
*iter() {
for (const schema of schemas)
yield* Schema.from(schema);
}
});
}
function or(x, y) {
return new Schema((t) => is(t, x) || is(t, y), {
displayName: `(${Schema.displayName(x)} | ${Schema.displayName(y)})`,
*iter() {
yield* Schema.from(x);
yield* Schema.from(y);
}
});
}
function and(x, y) {
return new Schema((t) => is(t, x) && is(t, y), {
displayName: `(${Schema.displayName(x)} & ${Schema.displayName(y)})`
});
}
// src/types/array.ts
function $Array(base) {
const itemSchema = Schema.from(base);
const baseDisplayName = itemSchema.displayName;
return new Schema((t) => {
if (!Array.isArray(t)) {
return false;
}
for (const each of t) {
itemSchema.check(each);
}
return true;
}, {
displayName: baseDisplayName.includes(" ") ? `${baseDisplayName}[]` : `Array<${baseDisplayName}>`
});
}
// src/types/collections.ts
function $Map(keySchemaBase, valueSchemaBase) {
const keySchema = Schema.from(keySchemaBase);
const valueSchema = Schema.from(valueSchemaBase);
const keyTypeName = keySchema.displayName;
const valueTypeName = valueSchema.displayName;
return new Schema((x) => {
if (!(x instanceof Map)) {
return false;
}
for (const [key, value] of x) {
keySchema.check(key);
valueSchema.check(value);
}
return true;
}, { displayName: `Map<${keyTypeName}, ${valueTypeName}>` });
}
function $Set(schemaBase) {
const schema = Schema.from(schemaBase);
return new Schema((x) => {
if (!(x instanceof Set)) {
return false;
}
for (const key of x) {
schema.check(key);
}
return true;
}, { displayName: `Set<${schema.displayName}>` });
}
// src/types/misc.ts
function $instanceof(t) {
return new Schema((x) => x instanceof t, {
displayName: t.name
});
}
var a = $instanceof;
function $literal(t) {
return new Schema(t);
}
var $falsy = new Schema((x) => !x, {
displayName: "falsy"
});
var $nullish = new Schema((x) => x == null, {
displayName: "nullish"
});
function $optional(base) {
const schema = Schema.from(base);
return new Schema((x) => Schema.is(schema, x) || Schema.is($undefined, x), { displayName: `${schema.displayName}?` });
}
function $maybe(base) {
const schema = Schema.from(base);
return new Schema((x) => Schema.is(schema, x) || Schema.is($nullish, x), { displayName: `maybe ${schema.displayName}` });
}
var $any = new Schema((x) => true, { displayName: "any" });
var $never = new Schema((x) => false, {
displayName: "never"
});
var $Date = $instanceof(Date);
var $Error = $instanceof(Error);
var $RegExp = $instanceof(RegExp);
var $URL = $instanceof(URL);
var $ArrayBuffer = $instanceof(ArrayBuffer);
var $ArrayBufferView = new Schema((x) => ArrayBuffer.isView(x), { displayName: "ArrayBufferView" });
var $Int8Array = $instanceof(Int8Array);
var $Int16Array = $instanceof(Int16Array);
var $Int32Array = $instanceof(Int32Array);
var $BigInt64Array = $instanceof(BigInt64Array);
var $Uint8Array = $instanceof(Uint8Array);
var $Uint8ClampedArray = $instanceof(Uint8ClampedArray);
var $Uint16Array = $instanceof(Uint16Array);
var $Uint32Array = $instanceof(Uint32Array);
var $BigUint64Array = $instanceof(BigUint64Array);
var $Float32Array = $instanceof(Float32Array);
var $Float64Array = $instanceof(Float64Array);
// src/types/constants.ts
var $boolean = new Schema((x) => typeof x === "boolean", {
displayName: "boolean"
});
var $NaN = new Schema((x) => x !== x, {
displayName: "NaN"
});
var $false = $literal(false);
var $true = $literal(true);
var $undefined = $literal(void 0);
var $null = $literal(null);
// src/types/enum.ts
function isEnumMemberName(x, enumObject) {
return x in enumObject && typeof enumObject[enumObject[x]] !== "number";
}
function enumKeys(enumObject) {
return Object.keys(enumObject).filter((key) => isEnumMemberName(key, enumObject));
}
function $enum(enumObject, options = {}) {
const keys = enumKeys(enumObject);
const values = new Set(keys.map((key) => enumObject[key]));
return new Schema((x) => values.has(x), {
displayName: options.displayName ?? `enum { ${keys.join(", ")} }`,
*iter() {
yield* values;
}
});
}
// src/types/number.ts
var $number = new Schema((x) => typeof x === "number" && x === x, { displayName: "number" });
var $int = new Schema((x) => Number.isInteger(x), {
displayName: "int"
});
var $finite = new Schema((x) => Number.isFinite(x), {
displayName: "finite"
});
var $bigint = new Schema((x) => typeof x === "bigint", {
displayName: "bigint"
});
// src/types/object.ts
function hasOwn(target, prop) {
if (!{}.hasOwnProperty.call(target, prop)) {
throw null;
}
return true;
}
var toDisplayKeyValue = ([key, valueSchema]) => `${toDisplayKey(key)}: ${Schema.displayName(valueSchema)}`;
var $object = new Schema((x) => typeof x === "object" && x != null, { displayName: "object" });
function $interface(template) {
return new Schema((x) => {
const [report, resolve] = keyReporter((key) => Schema.check(template[key], x[key]), (key) => errorMessages_exports.invalidProperty(key, template[key]));
return $object.check(x) && (Reflect.ownKeys(template).forEach(report), resolve());
}, {
displayName: `{${Reflect.ownKeys(template).map((key) => [key, template[key]]).map(toDisplayKeyValue).join(", ")}}`
});
}
function $Exact(template) {
return new Schema((x) => {
const [reportUnknown, resolveUnknown] = keyReporter((key) => hasOwn(template, key), (key) => `Unexpected property ${toDisplayKey(key)}`);
const [reportKnown, resolveKnown] = keyReporter((key) => Schema.check(template[key], x[key]), (key) => errorMessages_exports.invalidProperty(key, template[key]));
return $object.check(x) && (Reflect.ownKeys(x).forEach(reportUnknown), resolveUnknown()) && (Reflect.ownKeys(template).forEach(reportKnown), resolveKnown());
}, {
displayName: `{|${Reflect.ownKeys(template).map((key) => [key, template[key]]).map(toDisplayKeyValue).join(", ")}|}`
});
}
// src/types/record.ts
function $Record(keySchemaBase, valueSchemaBase) {
const keySchema = Schema.from(keySchemaBase);
const valueSchema = Schema.from(valueSchemaBase);
return new Schema((x) => typeof x === "object" && x != null && Schema.every(keySchema, (key) => ({}).hasOwnProperty.call(x, key)) && Object.entries(x).every(([key, value]) => Schema.is(keySchema, key) ? Schema.is(valueSchema, value) : true), { displayName: `Record<${keySchema.displayName}, ${valueSchema.displayName}>` });
}
// src/types/string.ts
var $string = new Schema((x) => typeof x === "string", {
displayName: "string"
});
// src/types/symbol.ts
var $symbol = new Schema((x) => typeof x === "symbol", {
displayName: "symbol"
});
// src/types/tuple.ts
function $Tuple(...schemas) {
return new Schema((t) => Array.isArray(t) && t.length === schemas.length && schemas.every((schema, i) => Schema.check(schema, t[i])), {
displayName: `[${schemas.map((schema) => Schema.displayName(schema)).join(", ")}]`
});
}
// src/ref.ts
function createErrorRef() {
return { error: null };
}
export {
$Array,
$ArrayBuffer,
$ArrayBufferView,
$BigInt64Array,
$BigUint64Array,
$Date,
$Error,
$Exact,
$Float32Array,
$Float64Array,
$Int16Array,
$Int32Array,
$Int8Array,
$Map,
$NaN,
$Record,
$RegExp,
$Set,
$Tuple,
$URL,
$Uint16Array,
$Uint32Array,
$Uint8Array,
$Uint8ClampedArray,
$any,
$bigint,
$boolean,
$enum,
$false,
$falsy,
$finite,
$instanceof,
$int,
$interface,
$literal,
$maybe,
$never,
$null,
$nullish,
$number,
$object,
$optional,
$string,
$symbol,
$true,
$undefined,
Schema,
a,
and,
check,
createErrorRef,
guard,
hasLength,
inRange,
is,
matches,
maxLength,
minLength,
nonEmpty,
oneOf,
or,
union
};
//# sourceMappingURL=index.mjs.map