UNPKG

runtypes

Version:

Runtime validation for static types

324 lines (323 loc) 13.1 kB
"use strict"; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _a, _Runtype_createInnerValidate, _b, _c, _d, _e; Object.defineProperty(exports, "__esModule", { value: true }); const Brand_js_1 = __importDefault(require("./Brand.js")); const Constraint_js_1 = __importDefault(require("./Constraint.js")); const Intersect_js_1 = __importDefault(require("./Intersect.js")); const Literal_js_1 = __importDefault(require("./Literal.js")); const Optional_js_1 = __importDefault(require("./Optional.js")); const Parser_js_1 = __importDefault(require("./Parser.js")); const Union_js_1 = __importDefault(require("./Union.js")); const ValidationError_js_1 = __importDefault(require("./result/ValidationError.js")); const SUCCESS_js_1 = __importDefault(require("./utils-internal/SUCCESS.js")); const copyProperties_js_1 = __importDefault(require("./utils-internal/copyProperties.js")); const defineIntrinsics_js_1 = __importDefault(require("./utils-internal/defineIntrinsics.js")); const isObject_js_1 = __importDefault(require("./utils-internal/isObject.js")); const show_js_1 = __importDefault(require("./utils-internal/show.js")); const RuntypeSymbol = globalThis.Symbol(); const RuntypeConformance = globalThis.Symbol(); const RuntypePrivate = globalThis.Symbol(); /** * A runtype determines at runtime whether a value conforms to a type specification. */ class Runtype { get [(_b = RuntypeSymbol, _c = RuntypeConformance, _d = RuntypePrivate, globalThis.Symbol.toStringTag)]() { return `Runtype<${(0, show_js_1.default)(this)}>`; } toString() { return `[object ${this[globalThis.Symbol.toStringTag]}]`; } /** @internal */ constructor(validate, base) { Object.defineProperty(this, "tag", { enumerable: true, configurable: true, writable: true, value: void 0 }); /** @internal */ Object.defineProperty(this, _b, { enumerable: true, configurable: true, writable: true, value: void 0 }); /** @internal */ Object.defineProperty(this, _c, { enumerable: true, configurable: true, writable: true, value: void 0 }); /** @internal */ Object.defineProperty(this, _d, { enumerable: true, configurable: true, writable: true, value: void 0 }); // @ts-expect-error: type-only delete this[RuntypeSymbol]; // @ts-expect-error: type-only delete this[RuntypeConformance]; (0, copyProperties_js_1.default)(this, base); (0, defineIntrinsics_js_1.default)(this, { [RuntypePrivate]: globalThis.Object.assign(({ expected, received, visited, parsing, memoParsed, }) => { if ((0, isObject_js_1.default)(received)) { const memo = visited.memo(received, expected, null); if (memo) return memo; else if (memo === undefined) { const innerValidate = __classPrivateFieldGet(_a, _a, "f", _Runtype_createInnerValidate).call(_a, visited); const result = expected[RuntypePrivate].validate({ received, innerValidate, expected, parsing, memoParsed, }); visited.memo(received, expected, result); return result; } else return (0, SUCCESS_js_1.default)((parsing && memoParsed?.get(received)) || received); } else { const innerValidate = __classPrivateFieldGet(_a, _a, "f", _Runtype_createInnerValidate).call(_a, visited); return expected[RuntypePrivate].validate({ received, innerValidate, expected, parsing, memoParsed, }); } }, { validate, extensions: [] }), }); (0, copyProperties_js_1.default)(base, this); bindThis(base, new.target.prototype); return base; } /** * Process a value with this runtype, returning a detailed information of success or failure. Does not throw on failure. */ inspect(x, options = {}) { return this[RuntypePrivate]({ expected: this, received: x, visited: createVisitedState(), parsing: options.parse ?? true, }); } /** * Validates that a value conforms to this runtype, returning the original value, statically typed. Throws `ValidationError` on failure. */ check(x) { const result = this.inspect(x, { parse: false }); if (result.success) return result.value; else throw new ValidationError_js_1.default(result); } /** * Validates that a value conforms to this runtype, returning a `boolean` that represents success or failure. Does not throw on failure. */ guard(x) { return this.inspect(x, { parse: false }).success; } /** * Validates that a value conforms to this runtype. Throws `ValidationError` on failure. */ assert(x) { return void this.check(x); } /** * Validates that a value conforms to this runtype and returns another value returned by the function passed to `withParser`. Throws `ValidationError` on failure. Does not modify the original value. */ parse(x) { const result = this.inspect(x, { parse: true }); if (result.success) return result.value; else throw new ValidationError_js_1.default(result); } /** * Returns a shallow clone of this runtype with additional properties. Useful when you want to integrate related values, such as the default value and utility functions. */ with(extension) { const cloned = this.clone(); cloned[RuntypePrivate].extensions = [...this[RuntypePrivate].extensions, extension]; for (const extension of cloned[RuntypePrivate].extensions) (0, copyProperties_js_1.default)(cloned, typeof extension === "function" ? extension(cloned) : extension); return cloned; } /** * Creates a shallow clone of this runtype. */ clone() { const base = typeof this === "function" ? this.bind(undefined) : globalThis.Object.create(globalThis.Object.getPrototypeOf(this)); (0, copyProperties_js_1.default)(base, this); const cloned = new _a(this[RuntypePrivate].validate, base); cloned[RuntypePrivate].extensions = this[RuntypePrivate].extensions; for (const extension of cloned[RuntypePrivate].extensions) (0, copyProperties_js_1.default)(cloned, typeof extension === "function" ? extension(cloned) : extension); return cloned; } /** * Unions this Runtype with another. */ or(other) { return (0, Union_js_1.default)(this, other); } /** * Intersects this Runtype with another. */ and(other) { return (0, Intersect_js_1.default)(this, other); } /** * Optionalizes this property. * * Note that `Optional` is not a runtype, but just a contextual modifier which is only meaningful when defining the content of `Object`. If you want to allow the validated value to be `undefined`, use `undefinedable` method. */ optional() { return (0, Optional_js_1.default)(this); } /** * Optionalizes this property, defaulting to the given value if this property was absent. Only meaningful for parsing. */ default(value) { return (0, Optional_js_1.default)(this, value); } /** * Unions this runtype with `Null`. */ nullable() { return (0, Union_js_1.default)(this, (0, Literal_js_1.default)(null)); } /** * Unions this runtype with `Undefined`. */ undefinedable() { return (0, Union_js_1.default)(this, (0, Literal_js_1.default)(undefined)); } /** * Unions this runtype with `Null` and `Undefined`. */ nullishable() { return (0, Union_js_1.default)(this, (0, Literal_js_1.default)(null), (0, Literal_js_1.default)(undefined)); } /** * Uses a constraint function to add additional constraints to this runtype, and manually converts a static type of this runtype into another via the type argument if passed. */ withConstraint(constraint) { return (0, Constraint_js_1.default)(this, (x) => { const message = constraint(x); if (typeof message === "string") throw message; else if (!message) throw undefined; }); } /** * Uses a guard function to add additional constraints to this runtype, and automatically converts a static type of this runtype into another. */ withGuard(guard) { return (0, Constraint_js_1.default)(this, (x) => { if (!guard(x)) throw undefined; }); } /** * Uses an assertion function to add additional constraints to this runtype, and automatically converts a static type of this runtype into another. */ withAssertion(assert) { return (0, Constraint_js_1.default)(this, assert); } /** * Adds a brand to the type. */ withBrand(brand) { return (0, Brand_js_1.default)(brand, this); } /** * Chains custom parser after this runtype. Basically only works in the `parse` method, but in certain cases parsing is implied within a chain of normal validation, such as before execution of a constraint, or upon function boundaries enforced with `Contract` and `AsyncContract`. */ withParser(parser) { return (0, Parser_js_1.default)(this, parser); } /** * Statically ensures this runtype is defined for exactly `T`, not for a subtype of `T`. `X` is for the parsed type. */ conform() { return this; } } _a = Runtype, _e = globalThis.Symbol.hasInstance; /** @internal */ Object.defineProperty(Runtype, "create", { enumerable: true, configurable: true, writable: true, value: (validate, base) => new _a(validate, base) }); _Runtype_createInnerValidate = { value: (visited) => context => context.expected[RuntypePrivate]({ ...context, visited }) }; /** * Guards if a value is a runtype. */ Object.defineProperty(Runtype, "isRuntype", { enumerable: true, configurable: true, writable: true, value: (x) => x instanceof globalThis.Object && globalThis.Object.hasOwn(x, RuntypePrivate) }); /** * Asserts if a value is a runtype. */ Object.defineProperty(Runtype, "assertIsRuntype", { enumerable: true, configurable: true, writable: true, value: x => { if (!_a.isRuntype(x)) throw new Error("Expected runtype, but was not"); } }); Object.defineProperty(Runtype, _e, { enumerable: true, configurable: true, writable: true, value: _a.isRuntype }); const bindThis = (self, prototype) => { const descriptors = globalThis.Object.getOwnPropertyDescriptors(prototype); delete descriptors["constructor"]; for (const key of globalThis.Reflect.ownKeys(descriptors)) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const descriptor = descriptors[key]; if ("value" in descriptor && typeof descriptor.value === "function") descriptor.value = descriptor.value.bind(self); if ("get" in descriptor && descriptor.get) descriptor.get = descriptor.get.bind(self); if ("set" in descriptor && descriptor.set) descriptor.set = descriptor.set.bind(self); } globalThis.Object.defineProperties(self, descriptors); }; const createVisitedState = () => { const map = new WeakMap(); const memo = (candidate, runtype, result) => { const inner = map.get(candidate) ?? new WeakMap(); map.set(candidate, inner); const memo = inner.get(runtype); inner.set(runtype, result); return memo; }; return { memo }; }; exports.default = Runtype;