{"id":17229106,"url":"https://github.com/stefcameron/rtvjs","last_synced_at":"2026-01-20T14:08:01.027Z","repository":{"id":222859211,"uuid":"758525379","full_name":"stefcameron/rtvjs","owner":"stefcameron","description":"Runtime Verification JavaScript Library that supports serialization as JSON.","archived":false,"fork":false,"pushed_at":"2026-01-14T16:30:29.000Z","size":5763,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T19:52:21.624Z","etag":null,"topics":["assertion","integration","isomorphic","javascript","nodejs","runtime","test","type","verification","verify"],"latest_commit_sha":null,"homepage":"https://rtvjs.stefcameron.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stefcameron.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-02-16T14:08:51.000Z","updated_at":"2026-01-14T16:30:34.000Z","dependencies_parsed_at":"2024-03-13T15:57:22.972Z","dependency_job_id":"406f141b-8b93-452d-8550-7f3ad73b2176","html_url":"https://github.com/stefcameron/rtvjs","commit_stats":null,"previous_names":["stefcameron/rtvjs"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/stefcameron/rtvjs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefcameron%2Frtvjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefcameron%2Frtvjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefcameron%2Frtvjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefcameron%2Frtvjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stefcameron","download_url":"https://codeload.github.com/stefcameron/rtvjs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stefcameron%2Frtvjs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28604712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T12:01:53.233Z","status":"ssl_error","status_checked_at":"2026-01-20T12:01:46.545Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assertion","integration","isomorphic","javascript","nodejs","runtime","test","type","verification","verify"],"created_at":"2024-10-15T04:46:01.399Z","updated_at":"2026-01-20T14:08:01.003Z","avatar_url":"https://github.com/stefcameron.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI](https://github.com/stefcameron/rtvjs/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/stefcameron/rtvjs/actions/workflows/ci.yml) [![license](https://badgen.now.sh/badge/license/MIT)](./LICENSE)\n\n# RTV.js\n\nRuntime Verification Library for browsers and Node.js.\n\nThis library is isomorphic: It runs equally well in modern browsers and on the server with Node.js.\n\nThe latest versions of major browsers, and Node LTS releases, are supported.\n\nGive it a [test drive with RunKit](https://npm.runkit.com/rtvjs)!\n\n# Installation\n\n\u003e ❗️ This library is ESM/CJS in Node, but __ESM-only in the browser__.\n\n```bash\nnpm install rtvjs\n```\n\nThe package's `./dist` directory contains various types of builds:\n\n*   `./node/rtv[.dev].cjs`: CJS (not minified, primarily for use in Node)\n*   `./node/rtv[.slim][.dev].mjs`: ESM (not minified, primarily for use in Node)\n*   `./browser/rtv.esm[.slim][.dev].js`: ESM (not minified, for use by bundlers; non-slim/self-contained Dev version is good for browser debugging as a counterpart to the minified Prod version)\n*   `./browser/rtv.esm.min.js`: ESM (minified, primarily for use directly in the browser in Prod)\n\n## Dev\n\nThe `.dev` CJS and ESM builds include dev-only features such as deprecation warnings and more helpful console logs.\n\n## Slim\n\nThese builds are smaller in size to optimize on download time and bundling efficiency.\n\nThe `.slim` CJS and ESM builds depend on [@babel/runtime](https://babeljs.io/docs/en/babel-runtime) and [lodash-es](https://lodash-es.com/) external dependencies, both of which should automatically get installed when you install `rtvjs`.\n\n## CJS\n\nThe CJS build can be used like this in a Node.js script:\n\n```javascript\nconst rtv = require('rtvjs');\n```\n\n## ESM\n\nThe ESM build can be used like this either in Node.js, a bundler, or the browser (note a default export is not provided):\n\n```javascript\nimport * as rtv from 'rtvjs'; // import all into an `rtv` namespace\nimport { verify, STRING, ... } from 'rtvjs'; // selective imports only\n```\n\n### ESM in browser\n\nWhile a UMD build isn't provided, it's still possible to load the library as a global directly in the browser as follows:\n\n\u003e ❗️ It's important to __specify the full path__ to the bundle you want to use. Neither one of the CDNs suggested below seem to understand `package.json:exports` in order to find the right file.\n\n```html\n\u003cscript type=\"module\"\u003e\n  import * as rtv from 'https://esm.sh/rtvjs@5/dist/browser/rtv.esm.min.js';\n  // OR: import * as rtv from 'https://unpkg.com/rtvjs@5/dist/browser/rtv.esm.min.js';\n  window.rtv = rtv;\n\u003c/script\u003e\n```\n\n\u003e The __non-slim__ builds are self-contained and optimized for browsers.\n\n# Documentation\n\nThis `README`, as well as the [API](API.md), are hosted at [rtvjs.stefcameron.com](https://rtvjs.stefcameron.com/api).\n\n# Changes\n\n[Changelog](CHANGELOG.md)\n\n# Purpose\n\nTo provide an easy, intuitive way to perform validations at __runtime__ on values whenever they cross the boundaries of an API or a function call.\n\nTools like [TypeScript](http://www.typescriptlang.org/) and [Flow](https://flow.org/) are useful for static analysis (i.e. as code is being written and then transpiled to regular JavaScript), but [they come at a price](https://medium.com/javascript-scene/the-typescript-tax-132ff4cb175b) and they don't work at __runtime__.\n\nFor example, they can't signal when there are integration issues between frontend and backend systems that are being co-developed. In one conversation, an API may be designed to return an object with certain properties. Later on, an on-the-fly decision to alter the implementation (yes, it happens in spite of the best intentions and processes), or simply a bug in the implementation, may result in an object that is missing an expected property, or has a property with an unexpected value.\n\nLet's consider a case where a \"state\" property, which is really an enumeration of string values, ends-up set to an unexpected state. What should a client do with an unexpected state when there's no implementation to back it up? Ignoring it could be an option, but perhaps not the best course of action. Even worse, the unexpected state _somehow_ could trickle deep down into code before it finally causes an exception, making it really difficult to find the true source of the problem.\n\nRTV.js can help signal the unexpected state by failing early, right at the API boundary:\n\n```javascript\nasync function getTodoList() {\n  const response = await fetch('/api/todos');\n  const json = await response.json();\n\n  // verify (require) that the response be a list of TODO items: this function\n  //  will throw if `json` doesn't meet the specified typeset (requirement)\n  rtv.verify(json, [[{ // list of objects (could be empty)\n    // non-empty string\n    title: rtv.STRING,\n    // 'YYYY-MM-DD', or null\n    due: [rtv.EXPECTED, rtv.STRING, {exp: '\\\\d{4}-\\\\d{2}-\\\\d{2}'}],\n    // string (could be empty), null, or not even defined\n    note: [rtv.OPTIONAL, rtv.STRING]\n  }]]);\n\n  return json;\n}\n```\n\nThere may also be a need to ensure that a critical function call is being given the parameters it expects. Rather than write a series of `if (!state) { throw new Error('state is required'); }` (which don't tell us much about what \"state\" is expected to be, other than it's _required_), it would be more helpful to have an easy way to express that \"state\" should be a non-empty string with a value in a given list (i.e. a value found in an enumeration).\n\nRTV.js can help signal the unexpected state immediately when execution enters the function:\n\n```javascript\nfunction applyState(state) {\n  rtv.verify(state, [rtv.STRING, {oneOf: ['on', 'off']}]);\n\n  if (state === 'on') {\n    // turn the lights on\n  } else {\n    // turn the lights off\n  }\n}\n\napplyState('on'); // ok\napplyState('dimmed'); // ERROR\n```\n\nWhile tools like TypeScript and Flow have their merits, [they come at a price](https://medium.com/javascript-scene/the-typescript-tax-132ff4cb175b). Typings or not, integration issues __will__ remain. RTV.js allows you to check for types __at runtime__, when it _really_ matters, and has a simple API so it's easy to learn.\n\n# Goals\n\nThe following statement verifies that the variable \"state\" is a non-empty string whose value is found in a list of permitted values:\n\n```javascript\nrtv.verify(state, [rtv.STRING, {oneOf: ['on', 'off']}]);\n```\n\nThe `[rtv.STRING, {oneOf: ['on', 'off']}]` portion of the example above is called a _typeset_. It expresses the expectation for the value of the \"state\" variable.\n\n[Typesets](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.typeset) must be:\n\n*   Easy to express, using rich [types](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvreftypes-object) and [qualifiers](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvrefqualifiers-object).\n*   Composable, whereby complex typesets can be built by combining multiple typesets into larger ones.\n*   Easy to customize, using [custom validators](https://github.com/stefcameron/rtvjs/blob/master/API.md#typescustom_validator-function) when the existing types and arguments don't provide the exact verification needed on a value.\n*   Intuitive, using simple native JavaScript language constructs like strings (for types), inline Arrays `[]` for lists and complex typesets, and inline objects `{}` for [shapes](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.shape_descriptor) (i.e. _interfaces_).\n*   [Serializable](https://github.com/stefcameron/rtvjs/blob/master/API.md#json-serialization) to JSON via `JSON.stringify()` so they can be easily transferred between systems.\n    *   Backend and frontend systems in JavaScript stacks could dynamically inform one another of expectations by sharing typesets.\n    *   Similar to the `@context` property of a JavaScript Object for [JSON-LD](https://json-ld.org/), an object's expected [shape](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.shape_descriptor) could be transferred along with the object itself.\n    *   With the exceptions of [custom validator](https://github.com/stefcameron/rtvjs/blob/master/API.md#typescustom_validator-function) functions and the `ctor` property of [shape object arguments](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.shape_object_args).\n\n# Tutorials\n\nTutorials and example uses of the RTV.js library.\n\n## Getting Started\n\nTo make it clear, in this tutorial, which properties and functions from RTV.js, we'll start by importing everything into an `rtv` object:\n\n```javascript\nimport * as rtv from 'rtvjs';\n```\n\nYou could also drop the object and import individual names, such as:\n\n```javascript\nimport { check, verify, STRING, ... } from 'rtvjs';\n```\n\n### Checks and Verifications\n\nRTV.js provides two functions for verifying values against [typesets](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.typeset). A _typeset_ is simply a set of one or more types that form an expectation about the value:\n\n```javascript\nrtv.verify(value, typeset); // will throw an error if verification fails\nrtv.check(value, typeset); // returns the error instead of throwing it\n```\n\n### Simple Types\n\nTypesets can be strings, objects (shapes), functions (custom validators), or Arrays (multiple possibilities).\n\nAt their simplest, typesets are strings that represent type names like `STRING`, `INT`, `DATE`, etc. See the full list of types [here](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.types).\n\n```javascript\nrtv.verify('Hello world!', rtv.STRING); // ok\nrtv.verify('', rtv.STRING); // ERROR: a required string cannot be empty\n```\n\n### Qualifiers\n\nThe first verification succeeds because the value is a non-empty string. The second one fails because the typeset uses the default [qualifier](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.qualifiers.qualifiers), which is `REQUIRED`. A _required_ string cannot be empty (nor can it be `null` or `undefined`).\n\nIn some implementations, an empty string is considered a bad value because it's a _falsy_ value in JavaScript, just like `null`, `undefined`, `false`, `0`, and `NaN`.\n\nThere are 3 other qualifiers, `EXPECTED`, `OPTIONAL`, and `TRUTHY`. A typeset may only have one qualifier, and it must be specified before any types.\n\nThe only way to specify an alternate qualifier is to use an Array to describe the typeset: `[\u003cqualifier\u003e, types...]`\n\nIf we wanted to accept an empty string (or `null`) as the value, we could use the `EXPECTED` qualifier:\n\n```javascript\nrtv.verify('Hello world!', [rtv.EXPECTED, rtv.STRING]); // ok\nrtv.verify('', [rtv.EXPECTED, rtv.STRING]); // ok\nrtv.verify(null, [rtv.EXPECTED, rtv.STRING]); // ok\n```\n\nIf we had a variable which we expect to be an object whenever it's value is _truthy_, we could use the `TRUTHY` qualifier, which would permit any _falsy_ value, but require the value to be of a specified type otherwise:\n\n```javascript\nlet objectOrFalsy = false;\nrtv.verify(objectOrFalsy, [rtv.TRUTHY, rtv.PLAIN_OBJECT]); // ok\n\nobjectOrFalsy = {hello: 'world!'};\nrtv.verify(objectOrFalsy, [rtv.TRUTHY, rtv.PLAIN_OBJECT]); // ok\nrtv.verify(objectOrFalsy, [rtv.TRUTHY, rtv.ARRAY]); // ERROR: value is not an array\n\n// similar to how the following code would either print \"world!\" or not get executed\n//  depending on the truthiness of `objectOrFalsy`\nif (objectOrFalsy) {\n  console.log(objectOrFalsy.hello);\n}\n```\n\n### Type Arguments\n\nSome types accept arguments. Arguments are simple objects that map argument names to values, and immediately follow a type in a typeset. Once again, an Array must be used to describe the typeset. Type arguments are optional, unless otherwise stated; some types don't accept arguments.\n\nThe `STRING` type accepts [arguments](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.STRING_args), one of which is `min`. It lets us specify the minimum length of the string. By default, when the qualifier is `REQUIRED`, `min` defaults to 1, but we can override that:\n\n```javascript\nrtv.verify('Hello world!', [rtv.STRING, {min: 0}]); // ok\nrtv.verify('', [rtv.STRING, {min: 0}]); // ok\nrtv.verify(null, [rtv.STRING, {min: 0}]); // ERROR\n```\n\nThis verifies the value cannot be `null` or `undefined` because of the (implied) `REQUIRED` qualifier. However, it could be empty because the `min` argument allows a zero-length string as the value.\n\n### Multiple Types\n\nSo far, we've seen simple typesets: Either just a string as the type name, or the type name and some arguments, and an optional qualifier that precedes it. There may be cases where a value could be one of multiple types. To verify against additional types, an Array is used to state all the possibilities: `[\u003cqualifier\u003e, \u003ctype1\u003e, \u003ctype1-args\u003e, \u003ctype2\u003e, \u003ctype2-args\u003e, ...]`. This is called an \"Array typeset\", which we've already seen in the two previous sections.\n\nSince a value can only be of a single type at any given time, Array typesets are evaluated using a __short-circuit OR conjunction__, which means the verification will pass as long as at least one type verifies the value (and verification will stop evaluating any other types against the value once a match is made).\n\nFor example, we could verify that a value is either a boolean, or a string that looks like a boolean:\n\n```javascript\nconst typeset = [rtv.BOOLEAN, rtv.STRING, {\n  exp: '^(?:true|false)$',\n  expFlags: 'i'\n}];\nrtv.verify(true, typeset); // ok\nrtv.verify('true', typeset); // ok\nrtv.verify('True', typeset); // ok\nrtv.verify('TRUE', typeset); // ok\nrtv.verify(false, typeset); // ok\nrtv.verify('false', typeset); // ok\n```\n\n\u003e Since the check for the `BOOLEAN` type is faster than evaluating a regular expression against a string, we list the `BOOLEAN` type first in the typeset.\n\nIt's worth pointing out here that:\n\n\u003e The same type can appear __multiple times__ in the same typeset.\n\nThis is very useful when a value could be of one or another set of values.\n\nFor example, we could verify that a value is a finite number in two different ranges:\n\n```javascript\nconst typeset = [\n  rtv.FINITE, {min: 0, max: 9},\n  rtv.FINITE, {min: 100, max: 199}\n];\nrtv.verify(1, typeset); // ok\nrtv.verify(50, typeset); // ERROR\nrtv.verify(150, typeset); // ok\nrtv.verify(-1, typeset); // ERROR\nrtv.verify(200, typeset); // ERROR\n```\n\nThis is also useful for _composition_ where you need to combine multiple smaller typesets into a larger one:\n\n```javascript\nconst lowerRangeTs = [rtv.FINITE, {min: 0, max: 9}];\nconst upperRangeTs = [rtv.FINITE, {min: 100, max: 199}];\nconst typeset = [...lowerRangeTs, ...upperRangeTs];\nrtv.verify(1, typeset); // ok\nrtv.verify(50, typeset); // ERROR\n```\n\n`typeset` here will yield the same results as in the previous example.\n\n### Shapes\n\nMost of the time, especially when integrating with an API, you'll want to verify what you receive against an expected [shape](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.shape_descriptor). A _shape_ describes the __interface__ an __object__ is expected to have. As the term implies, an interface describes the properties, and types thereof, expected on an object while ignoring any other properties that the object may have (since the code using this object shouldn't care about them anyway).\n\nPlain JavaScript objects are used to describe shapes, where expected property names are own-enumerable properties mapped to typesets. For example, we could describe a simple TODO item like this:\n\n```javascript\n{\n  title: rtv.STRING, // non-empty string\n  created: rtv.DATE, // Date instance\n  priority: rtv.INT // some whole number\n}\n```\n\nSince typesets are fully nestable/composable, we can get a bit more sophisticated by using Array typesets so we can provide arguments and different qualifiers:\n\n```javascript\n{\n  title: rtv.STRING, // required (non-empty) title\n  created: [rtv.OPTIONAL, rtv.DATE], // either a TODO or just a note\n  priority: [rtv.INT, {oneOf: [0, 1, 2]}] // 0=none, 1=low, 2=high\n}\n```\n\nSince shapes also represent objects, they have an _implied_ (default) type of [OBJECT](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.OBJECT). When [fully-qualified](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.fully_qualified_typeset) (which means not using any implied typeset elements like the qualifier and type), the shape would __move into the special `$` argument__ of the `OBJECT` type:\n\n```javascript\n[rtv.REQUIRED, rtv.OBJECT, {$: {\n    title: rtv.STRING,\n    created: [rtv.OPTIONAL, rtv.DATE],\n    priority: [rtv.INT, {oneOf: [0, 1, 2]}]\n  }\n}]\n```\n\nWhen the default object type is sufficient, it's really easy to nest shapes. Let's say our TODO item also had a note, which is an object with \"text\" and \"updated\" properties:\n\n```javascript\nconst {STRING, DATE, INT} = rtv;\nconst {EXPECTED, OPTIONAL} = rtv;\n\nconst item = {\n  title: 'Make Christmas Oatmeal',\n  due: new Date('12/25/2018'),\n  priority: 1,\n  note: {\n    text: 'Make 4 cups to have enough to share!',\n    updated: new Date('09/21/2018')\n  }\n};\n\nrtv.verify(item, {\n  title: STRING,\n  created: [OPTIONAL, DATE],\n  priority: [INT, {oneOf: [0, 1, 2]}],\n  note: {         // \u003c- nested shape\n    text: STRING, // \u003c- required contents\n    updated: DATE // \u003c- required Date\n  }\n}); // ok\n```\n\nThe typeset above would require a TODO item to have a \"note\" with a non-empty string value for \"text\", and a `Date` instance for \"updated\". We could make the entire note optional, however, by _expecting_ it to be either `null` if a note wasn't provided, or the shape if one was:\n\n```javascript\nconst {STRING, DATE, INT} = rtv;\nconst {EXPECTED, OPTIONAL} = rtv;\n\nconst item = {\n  title: 'Make Christmas Oatmeal',\n  due: new Date('12/25/2018'),\n  priority: 1,\n  note: null\n};\n\nrtv.verify(item, {\n  title: STRING,\n  created: [OPTIONAL, DATE],\n  priority: [INT, {oneOf: [0, 1, 2]}],\n  note: [EXPECTED, { // \u003c- null, or note object\n    text: STRING,\n    updated: DATE\n  }]\n}); // ok\n```\n\n\u003e When the default object type is implied, this is called the _shorthand syntax_. For shapes, it may be used when the typeset is the shape itself, or in an Array typeset that is _not_ [fully-qualified](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.fully_qualified_typeset), when a qualifier immediately precedes the shape (as we've done above for the \"note\" property).\n\n### Lists\n\nMany times, an API response or a function's arguments will contain a list of values or objects. At their most basic, lists are simple JavaScript Arrays that contain values of some type. The simplest way to verify a list is homogenous is to use the _shorthand_ syntax for the [ARRAY](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.ARRAY) type:\n\n```javascript\n[[rtv.STRING]]\n```\n\nThis would verify that an Array contains non-empty string values, but the Array could be __empty__, given the default [arguments](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.ARRAY_args).\n\n\u003e __Note the nested Array.__\n\nWhat the example above defines is an Array typeset that has a single _implied_ `ARRAY` type with an element typeset of `STRING` that will be applied to all elements found in the Array.\n\nWhen the __full notation__ is used, the element typeset __moves into the `$` argument__:\n\n```javascript\n[rtv.ARRAY, {$: [rtv.STRING]}] // same as before, but in full notation\n```\n\nEither form is acceptable, and either form can show-up anywhere in a typeset. Therefore, we could verify a value is either a boolean, an Array of non-empty strings, or an Array of integers like this:\n\n```javascript\n[rtv.BOOLEAN, [rtv.STRING], [rtv.INT]]\n```\n\nA more practical example could be requiring a TODO item to have a non-empty list of notes associated with it, if \"notes\" isn't `null`, meaning there are no notes (i.e. either \"notes\" is `null` because there are no notes, or \"notes\" is an Array of note objects containing at least one note):\n\n```javascript\nconst {STRING, DATE, INT} = rtv;\nconst {EXPECTED, OPTIONAL} = rtv;\n\nconst item = {\n  title: 'Make Christmas Oatmeal',\n  due: new Date('12/25/2018'),\n  priority: 1,\n  note: null\n};\n\nconst shape = {\n  title: STRING,\n  created: [OPTIONAL, DATE],\n  priority: [INT, {oneOf: [0, 1, 2]}],\n  note: [EXPECTED, ARRAY, { // \u003c- null, or non-empty Array of notes\n    $: {\n      text: STRING,\n      updated: DATE\n    },\n    min: 1 // \u003c- require a non-empty Array when not null\n  }]\n};\n\nrtv.verify(item, shape); // ok\n\nitem.notes = [];\n\nrtv.verify(item, shape); // ERROR: `notes` cannot be empty\n\nitem.notes.push({\n  text: 'Make 4 cups to have enough to share!',\n  updated: new Date('09/21/2018')\n});\n\nrtv.verify(item, shape); // ok\n```\n\n### Custom Validations\n\nWhile built-in validations are convenient, there may be occasions where a type, or even its arguments, aren't sufficient to verify the value. In that case, the typeset can be customized with a [custom validator](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.custom_validator) function.\n\n\u003e The function on its own is considered a valid typeset, and gets an _implied_ type of [ANY](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.ANY), which validates _anything_, even `undefined` and `null`, regardless of the qualifier.\n\nLet's say we wanted to verify that a value is a multiple of two. None of the [numeric type arguments](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.numeric_args) will verify that on their own, so we would need a custom validator:\n\n```javascript\nfunction validator(value) {\n  const n = parseInt(value);\n  return (!IsNaN(n) \u0026\u0026 n % 2 === 0);\n}\n\nrtv.verify(2, validator); // ok\nrtv.verify(3, validator); // ERROR\n```\n\nA custom validator can fail the verification either by returning a _falsy_ value (other than `undefined`), or throwing an `Error`. When a _falsy_ value is returned, a default `Error` will be generated. Throwing an error with a helpful message is the recommended way to fail verification because of a custom validator:\n\n```javascript\nfunction(value) {\n  const n = parseInt(value);\n  if (IsNaN(n) || n % 2 != 0) {\n    throw new Error('Not a number, or not a multiple of two.');\n  }\n}\n\nrtv.verify(2, validator); // ok\nrtv.verify(3, validator); // ERROR (rootCause: 'Not a number...')\n```\n\n\u003e The error thrown by the custom validator (or the one generated by the library) will be included in the [rootCause](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.RtvError+rootCause) property of the failed verification results.\n\nCustom validators are intended to be used as _compliments_ to existing types rather than complete replacements. For example, rather than worry about parsing the value as an integer and checking to see if it's not a number, we could let RTV.js first verify the value is an [integer](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.INT) by using an Array typeset:\n\n```javascript\nconst typeset = [rtv.INT, (v) =\u003e v % 2 === 0];\n\nrtv.verify(2, typeset); // ok\nrtv.verify(3, typeset); // ERROR (rootCause: 'Verification failed...')\n```\n\nAn Array typeset may have at most __one__ custom validator, and it must be the __last__ element. Each sub-typeset may have its own validator. When one or more types are in the typeset, the validator is immediately invoked if one of the types matches (i.e. verifies) the value (any remaining types are ignored):\n\n```javascript\nconst typeset = [rtv.INT, rtv.STRING, (v) =\u003e v % 2 === 0];\n\n// in both cases, STRING verification is skipped because INT matches first\nrtv.verify(2, typeset); // ok\nrtv.verify(3, typeset); // ERROR (rootCause: 'Verification failed...')\n```\n\nFinally, we could enhance our TODO item verification with a custom validator that verifies the `created` Date is not in the past:\n\n```javascript\nconst {STRING, DATE, INT} = rtv;\nconst {EXPECTED, OPTIONAL} = rtv;\n\nconst item = {\n  title: 'Make Christmas Oatmeal',\n  due: new Date(Date.now() + 24 * 60 * 60 * 1000), // tomorrow\n  priority: 1,\n  note: null\n};\n\nconst shape = {\n  title: STRING,\n  created: [\n    OPTIONAL,\n    DATE,\n    (v) =\u003e !v || v.getTime() \u003e= Date.now()) // \u003c- validator\n  ],\n  priority: [INT, {oneOf: [0, 1, 2]}],\n  note: [EXPECTED, ARRAY, {\n    $: {\n      text: STRING,\n      updated: DATE\n    },\n    min: 1\n  }]\n};\n\nrtv.verify(item, shape); // ok\n\nitem.due = new Date(Date.now() - 12 * 60 * 1000); // 12 hours ago\n\nrtv.verify(item, shape); // ok\n```\n\n\u003e Notice how the validator must handle `null` and `undefined` values because of the [OPTIONAL](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.qualifiers.OPTIONAL) qualifier, and is careful to return a _truthy_ result so that the property remains _optional_.\n\n### Minimum Viable Values\n\nOne of the main [goals](#purpose) of this library is to help validate API payloads at runtime, which is where the backend/frontend contract tends to get broken for various reasons.\n\nSometimes, the payloads returned from the API are much larger than your client cares for. That's why RTV.js makes it easy to define shapes that verify only the [subset](#shapes) of properties your app really cares about.\n\nTo go even further, while it verifies a given value, RTV.js also generates a _Minimum Viable Value_ (MVV) that represents the \"smallest\" dataset that would still pass the same validation.\n\nTo put this in perspective, if you're dealing with, say, a [Kubernetes API](https://kubernetes.io/docs/reference/kubernetes-api/) and you get a massive payload consisting of a long list of cluster objects, each further described by a very large Kubernetes cluster JSON object from which you only care to use 10 properties, you'll only write a [shape](#shapes) that describes those 10 properties. RTV.js will then generate a new object containing only those 10 properties (no matter how deeply nested they are) based on the shape you used for verification.\n\nDiscarding the original value and using the [MVV](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvsuccessmvv-) instead could potentially save your app from heavy memory use depending on how many closures in your code end-up imprisoning those large objects.\n\nTo illustrate this, let's say you had retrieved a list of tasks from an API endpoint:\n\n```javascript\nconst tasks = [\n  {\n    title: 'Implement the feature',\n    description: 'A very long description...',\n    due: new Date(),\n    tags: [\n      { id: 1, name: 'tag1' },\n      { id: 2, name: 'tag2' },\n      { id: 3, name: 'tag3' },\n    ],\n    notes: [\n      {\n        text: 'Note 1',\n        author: 'Sam',\n        date: new Date(),\n        tags: [\n          { id: 4, name: 'tag4' },\n          { id: 5, name: 'tag5' },\n          { id: 6, name: 'tag6' },\n        ],\n      },\n      {\n        text: 'Note 2',\n        author: 'Susie',\n        date: new Date(),\n        tags: [\n          { id: 7, name: 'tag7' },\n          { id: 8, name: 'tag8' },\n          { id: 9, name: 'tag9' },\n        ],\n      },\n    ],\n  },\n  {\n    title: 'Add the tests',\n    description: 'Long description...',\n    due: new Date(),\n    tags: [\n      { id: 1, name: 'tag1' },\n      { id: 2, name: 'tag2' },\n    ],\n    notes: [\n      {\n        text: 'Note 1',\n        author: 'Melissa',\n        date: new Date(),\n        tags: [{ id: 4, name: 'tag4' }],\n      },\n      {\n        text: 'Note 2',\n        author: 'Patrick',\n        date: new Date(),\n        tags: [\n          { id: 8, name: 'tag8' },\n          { id: 9, name: 'tag9' },\n        ],\n      },\n      {\n        text: 'Note 3',\n        author: 'Melissa',\n        date: new Date(),\n        tags: [\n          { id: 3, name: 'tag3' },\n          { id: 4, name: 'tag4' },\n        ],\n      },\n    ],\n  },\n];\n```\n\nYou might verify such a payload like this, because you only care about `title`, `tags` (specifically, tag `name`s), and `notes` for the tasks, and only `text` and `tags` (specifically, tag `id`s):\n\n```javascript\nconst result = rtv.verify(tasks, [[{\n  title: rtv.STRING,\n  tags: [[{ name: rtv.STRING }]],\n  notes: [[{\n    text: rtv.STRING,\n    tags: [\n      [\n        {\n          id: rtv.SAFE_INT,\n        },\n      ],\n    ],\n  }]],\n}]]);\n```\n\nIf the validation is successful, the returned [RtvSuccess](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvrefrtvsuccess) object would have the MVV stored in its `mvv` property, and it would look like this:\n\n```javascript\n[\n  {\n    title: 'Implement the feature',\n    tags: [\n      { name: 'tag1' },\n      { name: 'tag2' },\n      { name: 'tag3' },\n    ],\n    notes: [\n      {\n        text: 'Note 1',\n        tags: [\n          { id: 4 },\n          { id: 5 },\n          { id: 6 },\n        ],\n      },\n      {\n        text: 'Note 2',\n        tags: [\n          { id: 7 },\n          { id: 8 },\n          { id: 9 },\n        ],\n      },\n    ],\n  },\n  {\n    title: 'Add the tests',\n    tags: [\n      { name: 'tag1' },\n      { name: 'tag2' },\n    ],\n    notes: [\n      {\n        text: 'Note 1',\n        tags: [{ id: 4 }],\n      },\n      {\n        text: 'Note 2',\n        tags: [\n          { id: 8 },\n          { id: 9 },\n        ],\n      },\n      {\n        text: 'Note 3',\n        tags: [\n          { id: 3 },\n          { id: 4 },\n        ],\n      },\n    ],\n  },\n]\n```\n\n## Configuration\n\nRTV.js provides a [configuration](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtv.config) interface which allows [checks](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtv.check) (`rtv.check(value, typeset)`) and [verifications](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtv.verify) (`rtv.verify(value, typeset)`) to be globally enabled or disabled:\n\n```javascript\nrtv.config.enabled = false; // default: true\n\nrtv.verify('foo', rtv.INT); // no-op, always returns RtvSuccess\nrtv.check('foo', rtv.INT); // no-op, always returns RtvSuccess\n```\n\nBut why even make the call at all?\n\n```javascript\nif (rtv.config.enabled) {\n  rtv.verify('foo', rtv.INT);\n}\n\n// OR more terse:\n\nrtv.config.enabled \u0026\u0026 rtv.verify('foo', rtv.INT);\n\n// OR with your own global:\n\nDO_TYPE_CHECKS \u0026\u0026 rtv.verify('foo', rtv.INT);\n```\n\nNow, a JavaScript bundler that supports _tree shaking_ (e.g. Webpack or Rollup) can be configured to completely _exclude_ the entire code for a build. This could be handy if you're concerned about script download size over runtime checks, say, in a production build. See the [Rollup example](https://github.com/stefcameron/rtvjs/blob/master/API.md#enabled-example-rollup) for more information.\n\n## Verifications\n\nLet's say we're building a simple TODO app. We might use the following object as representative of a \"todo\" item in a list:\n\n```javascript\nconst item = {\n  title: 'Make Christmas Oatmeal',\n  due: new Date('12/25/2018'),\n  priority: 1,\n  notes: [\n    {\n      text: 'Ingredients: Cranberries, apples, cinnamon, walnuts, raisins, maple syrup.',\n      updated: new Date('09/20/2018')\n    },\n    {\n      text: 'Make 4 cups to have enough to share!',\n      updated: new Date('09/21/2018')\n    }\n  ]\n};\n```\n\nWe can describe this object using two [shapes](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.shape_descriptor):\n\n```javascript\nconst {STRING, DATE, INT} = rtv;\nconst priorities = [1, 2, 3, 4]; // simple enumeration of priority levels\n\nconst shapes = {\n  get todo() { // 'todo' shape\n    return {\n      title: STRING,\n      due: DATE,\n      priority: [INT, {oneOf: priorities}], // use 'priorities' enum\n      notes: [[this.note]] // compose 'note' shape into this 'todo' shape\n    };\n  },\n  get note() { // 'note' shape\n    return {\n      text: STRING,\n      updated: DATE\n    };\n  }\n};\n```\n\nNow we can verify that \"todo\" is a valid TODO item:\n\n```javascript\nrtv.verify(item, shapes.todo);\n```\n\nThe above verification will pass because \"todo\" meets the requirements of the shape.\n\nNow let's change the second note in \"todo\" such that its \"updated\" property is a boolean, `true` (a simple indication that the note was changed _at some point_ -- a change that seems to make sense, but would break code that expects a `Date` object to use for formatting in the UI, for example):\n\n```javascript\ntodo.notes[1].updated = true;\n```\n\nLexically, there's no reason for this assignment to fail, but the boolean value violates what is stated in the spec for a TODO item.\n\nIf we were to run the same verification again, __an exception would be thrown__. The exception would be an [RtvError](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.RtvError) with the following properties:\n\n```javascript\nrtv.verify(item, shapes.todo);\n\n// RtvError exception thrown from the above statement:\n{\n  message: 'Verification failed: path=\"/notes/1/updated\", mismatch=[\"!\",\"DATE\"], typeset={\"title\":\"STRING\",\"due\":\"DATE\",\"priority\":[\"INT\",{\"oneOf\":[1,2,3,4]}],\"notes\":[[{\"text\":\"STRING\",\"updated\":\"DATE\"}]]}',\n  path: ['notes', '1', 'updated'], // path to the property that failed verification\n  mismatch: ['!', 'DATE'], // fully-qualified typeset that caused the failure\n  typeset: {...}, // reference to \"shapes.todo\"\n  value: {...}, // reference to \"todo\"\n  ...\n}\n```\n\nThe `cause` property is providing us with the [fully-qualified](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.fully_qualified_typeset) version of the nested typeset that caused the failure. The original typeset simply specified `DATE` as the nested typeset for the `note.updated` property.\n\nIn reality, all typesets have a [qualifier](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.qualifiers), and the default qualifier is `'!'` which means the value is _required_. Required values can neither be `undefined` nor `null`. Depending on the type, other restrictions may be imposed, such as the [STRING](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.STRING) type, which must also not be empty (by default).\n\nFor brevity, typesets don't always have to be fully-qualified since the default qualifier is implied when not specified. Note that a typeset must have exactly __one__ qualifier, implied or not, but each nested typeset may have its own qualifier.\n\nFor example, some TODO items may not have due dates. However, our shape currently requires them. To handle this requirement, we could alter the nested typeset of the `todo.due` property to be `['*', DATE]` This would state that the `due` property is _expected_ rather than _required_, which means its value could be `null` (but still not `undefined`). There is a third qualifier, `'?'`, which would indicate the value is _optional_, in which case it could also be `undefined` (which, in JavaScript terms, means the property could also not even exist anywhere up the prototype chain of the `todo` object).\n\nThe `RtvError` object can also be obtained without catching an exception thrown by using the `rtv.check()` method:\n\n```javascript\nrtv.check(item, shapes.todo); // returns the RtvError object\n```\n\nIf the check was successful, an [RtvSuccess](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvrefrtvsuccess) would be returned instead. Since both `RtvError` and `RtvSuccess` objects have a common `valid: boolean` property, it's easy to check for success and failure:\n\n```javascript\nif (rtv.check(item, shapes.todo).valid) {\n  // check passed, \"todo\" is valid!\n} else {\n  // check failed, ignore the item\n}\n```\n\nFinally, we can check simple values too:\n\n```javascript\nrtv.verify('1', rtv.INT); // ERROR: not an integer number\nrtv.verify('', [rtv.EXPECTED, rtv.STRING]); // ok: expected strings can be null/empty\n```\n\n## Dynamic Classes\n\nThis is an advanced use of the RTV.js library. I recommend you read through the [Getting Started](#getting-started) guide or the [Verifications](#verifications) example first.\n\nLet's suppose we have the following [shape](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.shape_descriptor) that describes a simple note:\n\n```javascript\nconst {STRING, DATE} = rtv; // some types\nconst {EXPECTED} = rtv; // some qualifiers\nconst tags = ['car', 'money', 'reminder', 'grocery'];\n\nconst noteShape = {\n  // required, non-empty string\n  text: STRING,\n  // required Array (could be empty) of non-empty tags names from the user's\n  //  list of \"tags\"\n  tags: [[STRING, {oneOf: tags}]],\n  // required Date when the note was created\n  created: DATE,\n  // expected date of update (either null, or Date)\n  updated: [EXPECTED, DATE]\n};\n```\n\nBased on this shape, we can dynamically define a JavaScript class with getters and setters that ensure they are being set correctly:\n\n```javascript\nconst classGenerator = function(shape) {\n  const ctor = function(initialValues) {\n    // by definition, a shape descriptor is made-up of its own-enumerable\n    //  properties, so we enumerate them\n    const props = Object.keys(shape);\n\n    const typesets = {}; // prop -\u003e fully-qualified Array typeset\n    const values = {}; // prop -\u003e value\n\n    let initializing = true; // true while we apply \"initialValues\"\n\n    props.forEach((prop) =\u003e {\n      typesets[prop] = rtv.fullyQualify(shape[prop]);\n\n      Object.defineProperty(this, prop, {\n        enumerable: true,\n        configurable: true, // could be false to lock this down further\n        get() {\n          return values[prop];\n        },\n        set(newValue) {\n          const typeset = typesets[prop].concat(); // shallow clone\n\n          if (initializing) {\n            // allow each property to be initially null, or as the typeset specifies\n            //  so we don't end-up with junk data\n            // NOTE: in a fully-qualified typeset, the qualifier is always the\n            //  first element\n            typeset[0] = EXPECTED;\n          }\n\n          // we assume there are no interdependencies between nested typesets\n          // this verification will throw an RtvError if the \"newValue\"\n          //  violates the property's typeset\n          rtv.verify(newValue, typeset);\n\n          values[prop] = newValue;\n        }\n      });\n\n      if (initialValues \u0026\u0026 initialValues.hasOwnProperty(prop)) {\n        // go through the setter for verification\n        this[prop] = initialValues[prop];\n      } else {\n        // initialize to null\n        values[prop] = null;\n      }\n    });\n\n    initializing = false;\n  };\n\n  return ctor;\n};\n```\n\nNow we can generate a Note class and create an instance:\n\n```javascript\nconst Note = classGenerator(noteShape);\nconst note = new Note({text: 'Hello world!'});\n\nnote.text; // \"Hello world!\", since it was initialized\nnote.created; // null, since it wasn't initialized\nnote.text = ''; // ERROR: \"text\" must be a non-empty string\n```\n\n## Reactive Validations\n\n### originalValue\n\nLet's revisit the Note shape from the [Dynamic Classes](#dynamic-classes) example, but we'll add one more property, `tagCount`:\n\n```javascript\nconst {STRING, DATE, SAFE_INT} = rtv; // some types\nconst {EXPECTED} = rtv; // some qualifiers\nconst tags = ['car', 'money', 'reminder', 'grocery'];\n\nconst noteShape = {\n  // required, non-empty string\n  text: STRING,\n  // required Array (could be empty) of non-empty tags names from the user's\n  //  list of \"tags\"\n  tags: [[STRING, {oneOf: tags}]],\n                                                       //\n  tagCount: SAFE_INT,                                  // \u003c- NEW\n                                                       //\n  // required Date when the note was created\n  created: DATE,\n  // expected date of update (either null, or Date)\n  updated: [EXPECTED, DATE]\n};\n```\n\nThe `tagCount` property should always be an integer equal to the length of the `tags` array. The most basic validation we could do is the above: Mark it as a `SAFE_INT`. The problem is, it's not a complete validation because the following Note would pass, however __it would still be invalid__:\n\n```javascript\nconst note = {\n  text: 'Buy potatoes',\n  tags: ['reminder', 'grocery'],\n  tagCount: 1,                    // \u003c- does not match length of `tags` array\n  created: new Date(Date.now()),\n  updated: null\n}\n\nrtv.verify(note, noteShape); // ok (but not ok...)\n```\n\nTo address this issue, we can use the `context` parameter provided to any [custom validator](https://github.com/stefcameron/rtvjs/blob/master/API.md#typescustom_validator-function) since it provides a reference to the `originalValue` being validated:\n\n```javascript\nconst noteShape = {\n  ...,\n\n  // tagCount: SAFE_INT,                             // \u003c- BEFORE\n\n  tagCount: [\n    SAFE_INT,\n    (value, match, typeset, context) =\u003e {            // \u003c- AFTER\n      if (value !== context.originalValue.tags.length) {\n          throw new Error('tags and tagCount mismatch');\n      }\n    }\n  ],\n  ...\n};\n```\n\nThe first parameter, `value`, is the value of the `tagCount` property being validated by the [typeset](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.types.typeset) in which the custom validator is located. The fourth parameter, `context`, provides some additional information such as the original value, that being the Node object itself (the `note` object given to `rtv.verify(note, typeset)`).\n\n```javascript\nrtv.verify(note, noteShape); // ERROR: 'tags and tagCount mismatch'\n```\n\nWith this change, we now have a _reactive validation_, since it reacts (or adjusts) according to some of the data its given.\n\n### parent and parentKey\n\n`originalValue`, however, may not always be sufficient because it never changes regardless of the hierarchy of objects being validated. Let's say we wanted to validate a __list__ of notes:\n\n```javascript\nrtv.verify([note], [[noteShape]]); // ERROR: 'cannot read property \"length\" of undefined'\n    // (because `originalValue` is the array and does not have a `tags` property,\n    // so `originalValue.tags.length` causes an exception)\n```\n\nThis is where `parent` (and `parentKey`) are handy: `parent` will always refer to the immediate enclosing `Object`, `Array`, `Map`, or `Set`, whenever a property or element _within_ it is being validated. `parentKey` will be the property or index, depending on `parent`'s type, whose _value_ is being validated. See [custom validator context](https://github.com/stefcameron/rtvjs/blob/master/API.md#rtvref.validator.type_validator_context) for more details on these two properties.\n\nTherefore, we can change our typeset to this:\n\n```javascript\nconst noteShape = {\n  ...,\n  tagCount: [\n    SAFE_INT,\n    (value, match, typeset, context) =\u003e {\n      // if (value !== context.originalValue.tags.length) {    // \u003c- BEFORE\n      if (value !== context.parent.tags.length) {              // \u003c- AFTER\n          throw new Error('tags and tagCount mismatch');\n      }\n    }\n  ],\n  ...\n};\n```\n\nAnd now the validation works again:\n\n```javascript\nrtv.verify([note], [[noteShape]]); // ok!\n```\n\n# Alternatives\n\nRTV.js is not your only choice for runtime verification of values. Here are some alternatives you should consider. Compare them to what this library offers and choose the best one to fit your needs!\n\n*   [Joi](https://github.com/hapijs/joi) offers object schema validation. In the _hapi ecosystem_, this is commonly paired with [Hoek](https://github.com/hapijs/hoek). Note that `Joi` is __only supported in Node.js__ environments.\n*   [prop-types](https://github.com/facebook/prop-types) is useful __if you're building a React app__, but you can't get it to fail on purpose (it's React's support for it that causes errors in the console at runtime in a development build).\n*   [yup](https://github.com/jquense/yup) is useful if you want to __validate an object schema__ (and can be a viable alternative to `Joi` which is only supported on Node.js since `yup` is also supported in the browser).\n\n    While it has similarities to RTV.js, the picture is very different in practice: Yup has a concept of _coercions and transformations_ such that, for instance, `yup.string().required().validateSync(1)` would __not__ fail. Even if `strict` mode is enabled (for which there's no global setting; that would be on a per-statement basis), there are cases where it would still pass! [You wouldn't be the only one concerned with this](https://github.com/jquense/yup/issues/54) (and it's never been resolved, which is fine since `yup` isn't strictly focused on exact types for validations).\n\n    RTV.js, on the other hand, is __specially designed to validate values against exact types, no implicit type coercions or transformations__. It's either a string or it's not. That's what's needed for type validation. Otherwise, (IMO) it's useless, in terms of validation, because you can't be certain of what you have (e.g. if `\"1\"` and `1` were the same, would `value.substr()` _always_ work...?).\n\n# Contributing\n\n[Contributing](CONTRIBUTING.md), including local development guide.\n\n# License\n\n[MIT](LICENSE)\n\n# Future\n\nSee the list of proposed [enhancements](https://github.com/stefcameron/rtvjs/issues?label_name%5B%5D=enhancement). Up-vote the ones you like to help contributors prioritize them!\n\nFeel free to log an __enhancement__ if you have an idea! You may also file a PR, although it might be best to discuss your idea with the community first by creating an enhancement issue.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefcameron%2Frtvjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstefcameron%2Frtvjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefcameron%2Frtvjs/lists"}