{"id":18552479,"url":"https://github.com/andrejewski/trent","last_synced_at":"2025-04-09T22:31:56.095Z","repository":{"id":57379768,"uuid":"109194443","full_name":"andrejewski/trent","owner":"andrejewski","description":"Type specifications","archived":false,"fork":false,"pushed_at":"2020-05-11T17:14:01.000Z","size":52,"stargazers_count":9,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T00:02:40.677Z","etag":null,"topics":["specification","types"],"latest_commit_sha":null,"homepage":"","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/andrejewski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-01T23:21:12.000Z","updated_at":"2023-09-08T17:32:02.000Z","dependencies_parsed_at":"2022-09-06T05:01:38.974Z","dependency_job_id":null,"html_url":"https://github.com/andrejewski/trent","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Ftrent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Ftrent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Ftrent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrejewski%2Ftrent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrejewski","download_url":"https://codeload.github.com/andrejewski/trent/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248123758,"owners_count":21051528,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["specification","types"],"created_at":"2024-11-06T21:14:20.380Z","updated_at":"2025-04-09T22:31:55.772Z","avatar_url":"https://github.com/andrejewski.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Trent\n\u003e Type specifications\n\n```sh\nnpm install trent\n```\n\n[![npm](https://img.shields.io/npm/v/trent.svg)](https://www.npmjs.com/package/trent)\n[![Build Status](https://travis-ci.org/andrejewski/trent.svg?branch=master)](https://travis-ci.org/andrejewski/trent)\n[![Greenkeeper badge](https://badges.greenkeeper.io/andrejewski/trent.svg)](https://greenkeeper.io/)\n\nThis library provides runtime type specifications with a focus on:\n\n- A small, native-feeling syntax for defining specifications\n- Good error messages\n\n## Usage\n\n```js\nimport {createSpec} from 'trent'\n\nconst Matrix = createSpec(() =\u003e [[Number]])\n\nconst errors = Matrix.getErrors([\n  [1, 2, 3],\n  [4, 5, 'wrong']\n])\n\nconsole.log(errors)\n/* =\u003e [ Error('Value[1][2] must be of type \"number\"') ] */\n```\n\n## Documentation\n\n### `createSpec(builder: builtInChecks -\u003e Check): Spec`\nCreate a `Spec` using the `builder` function which receives the built-in checks described below. The builder must return a valid check or an error throws. The `Spec` object returned has a method `getErrors(value)` which returns an array of type errors for `value`.\n\n### Checks\n- Value checks\n  - [`Spec`](#spec)\n  - [`Constructor`](#constructor)\n  - [Array `[check]`](#array-check)\n  - [Object `{key: check, ..., keyN: checkN}`](#object-key-check--keyn-checkn)\n- Built-in checks\n  - [`is(value : any)`](#isvalue--any)\n  - [`or(checks : [Check])`](#orchecks--check)\n  - [`and(checks : [Check])`](#andchecks--check)\n  - [`not(check: Check)`](#notcheck--check)\n  - [`maybe(check : Check)`](#maybecheck--check)\n  - [`tuple(checks : [Check])`](#tuplechecks--check)\n  - [`nullable(check : Check)`](#nullablecheck--check)\n  - [`voidable(check : Check)`](#voidablecheck--check)\n- Custom checks\n  - [Using `checkCustomCheck({descriptor: String, isValid: Function})`](#using-checkcustomcheckdescriptor-string-isvalid-function)\n\n---\n\n#### `Spec`\nCheck that a provided value matches the `Spec`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Num = createSpec(() =\u003e Number)\nconst Matrix = createSpec(() =\u003e [[Num]])\n\nconst errors = Matrix.getErrors([[1, 2, 3], [4, 5, 6]])\nassert(errors.length === 0)\n```\n\n---\n\n#### `Constructor`\nCheck that a provided value is an `instanceof Constructor`, or `typeof` if the constructor is for a primitive such as `Number` or `String`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Num = createSpec(() =\u003e Number)\nconst errors = Num.getErrors(8)\nassert(errors.length === 0)\n```\n\n*Note:* Any Constructor will work if `root[Constructor.name] === Constructor` where `root` is the `window` in browsers and `global` in Node.\n\n---\n\n#### Array `[check]`\nCheck that a provided value is an array where every element passes the `check`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Numbers = createSpec(() =\u003e [Number])\nconst errors = Numbers.getErrors([8])\nassert(errors.length === 0)\n```\n\n---\n\n#### Object `{key: check, ..., keyN: checkN}`\nCheck that a provided value is an object where every `key` value passes its `check`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Point = createSpec(() =\u003e ({x: Number, y: Number}))\nconst errors = Point.getErrors({x: 8, y: 8})\nassert(errors.length === 0)\n```\n\n---\n\n#### `is(value : any)`\nCheck that a provided value must strictly equal (`===`) the `value`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({is}) =\u003e is(8))\nconst errors = Eight.getErrors(8)\nassert(errors.length === 0)\n```\n\n---\n\n#### `or(checks : [Check])`\nCheck that a provided value matches at least one of the `checks`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst NumberOrString = createSpec(({or}) =\u003e or([Number, String]))\nconst errors = NumberOrString.getErrors(8)\nassert(errors.length === 0)\n```\n\n---\n\n#### `and(checks : [Check])`\nCheck that a provided value matches at every one of the `checks`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({and, is}) =\u003e and([Number, is(8)]))\nconst errors = Eight.getErrors(8)\nassert(errors.length === 0)\n```\n\n---\n\n#### `not(check : Check)`\nCheck that a provided value does not pass the `check`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({not, is}) =\u003e not(is(9)))\nconst errors = Eight.getErrors(8)\nassert(errors.length === 0)\n```\n\n---\n\n#### `maybe(check : Check)`\nCheck that a provided value matches `check` or is `null` or `undefined`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({maybe, is}) =\u003e maybe(is(8)))\nassert(Eight.getErrors(8).length === 0)\nassert(Eight.getErrors(null).length === 0)\nassert(Eight.getErrors(undefined).length === 0)\n```\n\n---\n\n#### `tuple(checks : [Check])`\nCheck that a provided value is an array with length equal to `checks.length` and the first element passes the first check and so on.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({tuple, is}) =\u003e tuple([is(8), is(8)]))\nassert(Eight.getErrors([8, 8]).length === 0)\n```\n\n---\n\n#### `nullable(check : Check)`\nCheck that a provided value matches `check` or is `null`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({nullable, is}) =\u003e nullable(is(8)))\nassert(Eight.getErrors(8).length === 0)\nassert(Eight.getErrors(null).length === 0)\n```\n\n---\n\n#### `voidable(check : Check)`\nCheck that a provided value matches `check` or is `undefined`.\n\n```js\nimport {createSpec} from 'trent'\nimport assert from 'assert'\n\nconst Eight = createSpec(({voidable, is}) =\u003e voidable(is(8)))\nassert(Eight.getErrors(8).length === 0)\nassert(Eight.getErrors(undefined).length === 0)\n```\n\n---\n\n#### Using `checkCustomCheck({descriptor: String, isValid: Function})`\nCreate a check where `descriptor` fits the sentence `{subject} must be {descriptor}` for error messages and `isValid(value: any)` returns whether the value passes the check.\n\n```js\nimport {createSpec, createCustomCheck} from 'trent'\nimport assert from 'assert'\n\nconst lowercase = createCustomCheck({\n  descriptor: 'lowercase',\n  isValid (x) {\n    return typeof x === 'string' \u0026\u0026 x.toLowerCase() === x\n  }\n})\n\nconst Code = createSpec(() =\u003e lowercase)\nassert(Code.getErrors('8').length === 0)\n```\n\n*Note:* Custom checks cannot nest checks within them. In a tree structure analogy, custom checks must be leaves. The reason for this limitation is the complexity of creating good error messages.\n\n### `createDependentSpecs(builder : builtInChecks -\u003e {Check}) {Spec}`\nCreate a collection of `Spec`s which are dependent and/or recursive.\n\nFor example, we have two types `Foo` and `Bar` which both can contain each other. We can try to write this system with `createSpec`:\n\n```js\nimport {createSpec} from 'trent'\nvar Foo = createSpec(() =\u003e ({barList: [Bar]}))\nvar Bar = createSpec(() =\u003e ({fooList: [Foo]}))\n```\n\nCreating `Foo` with undefined `Bar` will not work. We need `createDependentSpecs` to enable a \"late-binding\" where order does not matter. We use the `ref` built-in check available to `createDependentSpecs` to reference `Spec`s.\n\n```js\nimport {createDependentSpecs} from 'trent'\nimport assert from 'assert'\nconst {Foo} = createDependentSpecs(({ref}) =\u003e ({\n  Foo: {barList: [ref('Bar')]},\n  Bar: {fooList: [ref('Foo')]}\n}))\n\nassert(Foo.getErrors({\n  barList: [{\n    fooList: [{\n      barList: [{\n        fooList: []\n      }]\n    }]\n  }]\n}).length === 0)\n```\n\n#### Real-life example\nI maintain the HTML parser [Himalaya](https://github.com/andrejewski/himalaya) which follows a strict [specification](https://github.com/andrejewski/himalaya/blob/master/text/ast-spec-v1.md) for its output. The output contains Nodes which can have children Nodes, so we need a recursive type.\n\n```js\nimport {\n  createSpec,\n  createDependentSpecs\n} from 'trent'\n\n// I pull this out to show that you can\nconst Text = createSpec(({is}) =\u003e ({\n  type: is('text'),\n  content: String\n}))\n\nexport const {Node} = createDependentSpecs(({is, or, ref, nullable}) =\u003e ({\n  Node: or([\n    ref('Element'),\n    ref('Comment'),\n    Text\n  ]),\n  Element: {\n    type: is('element'),\n    tagName: String,\n    children: [ref('Node')],\n    attributes: [{\n      key: String,\n      value: nullable(String)\n    }]\n  },\n  Comment: {\n    type: is('comment'),\n    content: String\n  }\n}))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrejewski%2Ftrent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrejewski%2Ftrent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrejewski%2Ftrent/lists"}