{"id":21480477,"url":"https://github.com/paperhive/fefe","last_synced_at":"2025-07-15T12:32:03.789Z","repository":{"id":33169936,"uuid":"153828925","full_name":"paperhive/fefe","owner":"paperhive","description":"Validate, sanitize and transform values with proper TypeScript types and zero dependencies.","archived":false,"fork":false,"pushed_at":"2023-03-04T02:55:52.000Z","size":570,"stargazers_count":39,"open_issues_count":3,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-11-05T11:58:20.205Z","etag":null,"topics":["functional","parse","parsing","sanitization","sanitize","schema","transform","typescript","validate","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/paperhive.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-19T19:00:18.000Z","updated_at":"2024-05-31T15:34:14.000Z","dependencies_parsed_at":"2024-06-19T02:58:00.071Z","dependency_job_id":"22bb61ab-de3e-4c97-b8fa-91ab2f50efb9","html_url":"https://github.com/paperhive/fefe","commit_stats":{"total_commits":155,"total_committers":3,"mean_commits":"51.666666666666664","dds":"0.032258064516129004","last_synced_commit":"c372b41011a616bde11444dac2ae86e05f324a43"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperhive%2Ffefe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperhive%2Ffefe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperhive%2Ffefe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperhive%2Ffefe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paperhive","download_url":"https://codeload.github.com/paperhive/fefe/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226038783,"owners_count":17564046,"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":["functional","parse","parsing","sanitization","sanitize","schema","transform","typescript","validate","validation"],"created_at":"2024-11-23T12:15:25.176Z","updated_at":"2024-11-23T12:15:25.867Z","avatar_url":"https://github.com/paperhive.png","language":"TypeScript","readme":"# fefe\n\n[![npm version](https://badge.fury.io/js/fefe.svg)](https://badge.fury.io/js/fefe)\n[![Test status](https://github.com/paperhive/fefe/actions/workflows/test.yaml/badge.svg)](https://github.com/paperhive/fefe/actions/workflows/test.yaml)\n[![codecov](https://codecov.io/gh/paperhive/fefe/branch/main/graph/badge.svg?token=OZcHEYFYrQ)](https://codecov.io/gh/paperhive/fefe)\n\nValidate, sanitize and transform values with proper TypeScript types and with a single dependency ([fp-ts](https://www.npmjs.com/package/fp-ts)).\n\n**🔎\u0026nbsp;\u0026nbsp;Validation:** checks a value (example: check if value is string)\u003cbr/\u003e\n**:nut_and_bolt:\u0026nbsp;\u0026nbsp;Sanitization:** if a value is not valid, try to transform it (example: transform value to `Date`)\u003cbr/\u003e\n**🛠️\u0026nbsp;\u0026nbsp;Transformation:** transforms a value (example: parse JSON)\u003cbr/\u003e\n**🔌\u0026nbsp;\u0026nbsp;Everything is a function**: functional approach makes it easy to extend – just plug in your own function anywhere!\u003cbr/\u003e\n**↔️\u0026nbsp;\u0026nbsp;Based on `Either`:** explicit and type-safe error handling – `left` path is a (typed!) error, `right` path is a valid value (see below).\n\n## Installation\n\n```bash\nnpm install fefe\n```\n\n## Usage\n\n### 🔎 Validation example\n\nValidation checks the provided value and returns it with proper types.\n\n```typescript\nimport { object, string } from 'fefe'\n\nconst validatePerson = object({ name: string() })\n\nconst result = validatePerson({ name: 'Leia' })\nif (isFailure(result)) {\n  return console.error(result.left)\n\n// result is of type { name: string }\nconst person = result.right\n```\n\n☝️ You can also use `fefe` to define your types easily:\n\n```typescript\nimport { ValidatorReturnType } from 'fefe'\ntype Person = ValidatorReturnType\u003ctypeof validatePerson\u003e // { name: string }\n```\n\n### ⚙️ Basic transformation example\n\n#### Parse a value\n\nIn this example a `string` needs to be parsed as a `Date`. You can use `pipe()` to pass a value through multiple functions:\n\n```typescript\nimport { object, parseDate, pipe, string, ValidatorReturnType } from 'fefe'\n\nconst sanitizeMovie = object({\n  title: string(),\n  releasedAt: pipe(string()).pipe(parseDate())\n})\n\n// { title: string, releasedAt: Date }\ntype Movie = ValidatorReturnType\u003ctypeof sanitizeMovie\u003e\n\nconst movie: Movie = sanitizeMovie({\n  title: 'Star Wars',\n  releasedAt: '1977-05-25T12:00:00.000Z'\n})\n```\n\nThen `movie.right` equals `{ title: 'Star Wars', releasedAt: Date(1977-05-25T12:00:00.000Z) }` (`releasedAt` now is a date).\n\n**Note:** Chaining functions can also be achieved by the standard functional tools like `flow` and `chain` in [fp-ts](https://www.npmjs.com/package/fp-ts).\n\n#### Parse a value on demand (sanitize)\n\nSometimes a value might already be of the right type. In the following example we use `union()` to create a sanitizer that returns a provided value if it is a `Date` already and parse it otherwise. If it can't be parsed either the function will throw:\n\n```typescript\nimport { date, parseDate, pipe, union } from 'fefe'\n\nconst sanitizeDate = union(\n  date(),\n  pipe(string()).pipe(parseDate())\n)\n```\n\n### 🛠️ Complex transformation example\n\nThis is a more complex example that can be applied to parsing environment variables or query string parameters. Again, we use `pipe` to compose functions. Here, we also add a custom function that splits a string into an array.\n\n```typescript\nimport { object, parseJson, pipe, string, success } from 'fefe'\n\nconst parseConfig = object({\n  gcloudCredentials: pipe(string())\n    .pipe(parseJson())\n    .pipe(object({ secret: string() })),\n  whitelist: pipe(string()\n    .pipe(secret =\u003e success(str.split(',')))\n})\n\n// { gcloudCredentials: { secret: string }, whitelist: string[] }\ntype Config = ValidatorReturnType\u003ctypeof parseConfig\u003e\n\nconst config: Config = parseConfig({\n  gcloudCredentials: '{\"secret\":\"foobar\"}',\n  whitelist: 'alice,bob'\n})\n```\n\nThen `config.right` will equal `{ gcloudCredentials: { secret: 'foobar'}, whitelist: ['alice', 'bob'] }`.\n\n## Documentation\n\n### Transformer\u003cT\u003e\n\nA transformer is a function that accepts some value of type `V` (it could be `unknown`) and returns a type `T`:\n```typescript\ntype Transform\u003cT\u003e = (v: V) =\u003e Result\u003cT\u003e\n```\nThe result can either be a `FefeError` (see below) or the validated value as type `T`:\n```typescript\ntype Result\u003cT\u003e = Either\u003cFefeError, T\u003e\n```\n\n`fefe` uses the `Either` pattern with types and functions from [fp-ts](https://www.npmjs.com/package/fp-ts). `Either` can either represent an error (the \"left\" path) or the successfully validated value (the \"right\" path). This results in type-safe errors and explicit error-handling. Example:\n\n```typescript\nimport { isFailure } from 'fefe'\n\nconst result: Result\u003cstring\u003e = ...\nif (isFailure(result)) {\n  console.error(result.left)\n  process.exit(1)\n}\nconst name = result.right\n```\n\nYou may wonder why `fefe` does not just throw an error and the answer is:\n1. Throwing an error is a side-effect which goes against pure functional programming.\n2. Lack of type-safety: A thrown error can be anything and needs run-time checking before it can be used in a meaningful way.\n\nYou can read more about it [here](https://medium.com/nmc-techblog/functional-error-handling-in-js-8b7f7e4fa092).\n\nFor simplifying the transition from a 2.x codebase you can use the `toThrow(t: Transformer\u003cV, T\u003e)` function that returns a funtion `(v: V) =\u003e T` that returns the value directly and throws instead of returning a `FefeError` in the case of an error. Note that the thrown `FefeThrowError` has a different structure than the pre-3.x `FefeError`.\n\n\n### Validator\u003cT\u003e\n\nA validator is just a special (but common) case of a transformer where the input is `unknown`:\n\n```typescript\ntype Validator\u003cT\u003e = Transformer\u003cunknown, T\u003e\n```\n\n### `FefeError`\n\n`fefe` validators return a `FefeError` if a value can't be validated/transformed. Note that `FefeError` is *not* derived from the JavaScript `Error` object but is a simple object.\n\nIf an error occurs it will allow you to pinpoint where exactly the error(s) occured and why. The structure is the following:\n\n```typescript\ntype FefeError = LeafError | BranchError\n```\n\n#### `LeafError`\n\nA `LeafError` can be seen as the source of an error which can happen deep in a nested object and it carries both the value that failed and a human-readable reason describing why it failed.\n\n```typescript\ninterface LeafError {\n  type: 'leaf'\n  value: unknown\n  reason: string\n}\n```\n\n#### `BranchError`\n\nA `BranchError` is the encapsulation of one or more errors on a higher level.\n\n```typescript\ninterface BranchError {\n  type: 'branch'\n  value: unknown\n  childErrors: ChildError[]\n}\n\ninterface ChildError {\n  key: Key\n  error: FefeError\n}\n```\n\nImagine an array of values where the values at position 2 and 5 fail. This would result in two `childErrors`: one with `key` equal to 2 and `key` equal to 5. The `error` property is again a `FefeError` so this is a full error tree.\n\n#### `getErrorString(error: FefeError): string`\n\nTo simplify handling of errors, you can use `getErrorString()` which traverses the tree and returns a human-readable error message for each `LeafError` – along with the paths and reasons.\n\nExample error message: `user.id: Not a string.`\n\n### `array(elementValidator, options?): Validator\u003cT[]\u003e`\n\nReturns a validator that checks that the given value is an array and that runs `elementValidator` on all elements. A new array with the results is returned as `Result\u003cT[]\u003e`.\n\nOptions:\n* `elementValidator: Validator\u003cT\u003e`: validator that is applied to each element. The return values are returned as a new array.\n* `options.minLength?: number`, `options.maxLength?: number`: restrict length of array\n* `options.allErrors?: boolean`: set to `true` to return all errors instead of only the first.\n\n### `boolean(): Validator\u003cboolean\u003e`\n\nReturns a validator that returns `value` if it is a boolean and returns an error otherwise.\n\n### `date(options?): Validator\u003cDate\u003e`\n\nReturns a validator that returns `value` if it is a Date and returns an error otherwise.\n\nOptions:\n* `options.min?: Date`, `options.max?: Date`: restrict date\n\n### `discriminatedUnion(key, definition, options?): Validator\u003cObjectResult\u003cD\u003e\u003e`\n\nReturns a validator that returns `value` if:\n* it is an object and\n* the `value[key]` is a key of `definition`\n* `value` (without `key`) passes the validation as specified in `definition[key]`.\nOtherwise it returns an error. A new object is returned that has the results of the validator functions as values.\n\nOptions: see `object()`.\n\n### `enumerate(value1, value2, ...): Validator\u003cvalue1 | value2 | ...\u003e`\n\nReturns a validator that returns `value` if if equals one of the strings `value1`, `value2`, .... and returns an error otherwise.\n\n### `mapObjectKeys(map): Transformer\u003cS, T\u003e`\n\nReturns a transformer that takes the input object and returns a new object with the keys of `map`. For each key `k` the resulting object's value is the value for the key `map[k]` of the input object.\n\nOptions:\n* `map: Record\u003cstring, keyof S\u003e`: maps output object keys to input object keys.\n\nThis function is very useful in combination with `object()`:\n\n```typescript\nconst validateEnv = pipe(\n    object({\n      FOO: string(),\n      BAR: optional(pipe(string()).pipe(parseNumber())),\n    })\n  )\n  .pipe(mapObjectKeys({ foo: 'FOO', bar: 'BAR' }))\n\nconst result = validatEnv({ FOO: 'str', BAR: '1337' })\n```\nThen `isSuccess(result)` will be `true` and `result.right` equals to `{ foo: 'str', bar: 1337 }`.\n\n\n### `number(options?): Validator\u003cnumber\u003e`\n\nReturns a validator that returns `value` if it is a number and returns an error otherwise.\n\nOptions:\n* `options.min?: number`, `options.max?: number`: restrict number\n* `options.integer?: boolean`: require number to be an integer (default: `false`)\n* `options.allowNaN?: boolean`, `options.allowInfinity?: boolean`: allow `NaN` or `infinity` (default: `false`)\n\n### `object(definition, options?): Validator\u003cObjectResult\u003cD\u003e\u003e`\n\nReturns a validator that returns `value` if it is an object and all values pass the validation as specified in `definition`, otherwise it returns an error. A new object is returned that has the results of the validator functions as values.\n\nOptions:\n* `definition: ObjectDefinition`: an object where each value is a `Validator\u003cT\u003e`.\n* `allowExcessProperties?: boolean`: allow excess properties in `value` (default: `false`). Excess properties are not copied to the returned object.\n* `allErrors?: boolean`: set to `true` to return all errors instead of only the first (default: `false`).\n\nYou can use the following helpers:\n* `optional(validator: Validator\u003cT\u003e)`: generates an optional key validator with the given `validator`.\n* `defaultTo(validator: Validator\u003cT\u003e, default: D | () =\u003e D`: generates a validator that defaults to `default()` if it is a function and `default` otherwise.\n\n### `objectMap(valueValidator, options?): Validator\u003c{ [k: string]?: T }\u003e`\n\nReturns a validator that returns a map with value type T if all values pass the `valueValidator`, otherwise it returns an error. A new object is returned that has the results of the validator functions as values.\n\nOptions:\n* `valueValidator: Validator\u003cT\u003e`: validator that is applied to each value.\n* `options.allErrors?: boolean`: set to `true` to return all errors instead of only the first.\n\n### `pipe(validator1: Transformer\u003cA, B\u003e): Pipe\u003cA, B\u003e`\n\nReturns a transformer that offers a `.pipe(validator2: Transformer\u003cB, C\u003e): Pipe\u003cA, C\u003e` method.\n\n### `string(options?): Validator\u003cstring\u003e`\n\nReturns a validator that returns `value` if it is a string and returns an error otherwise.\n\nOptions:\n* `options.minLength?: number`, `options.maxLength?: number`: restrict length of string\n* `options.regex?: RegExp`: require string to match regex\n\n### `union(validator1, validator2, ...): Validator\u003cA | B | ...\u003e`\n\nReturns a validator that returns the return value of the first validator called with `value` that does not return an error. The function returns an error if all validators return an error. All arguments are validators (e.g., `validator1: Validator\u003cA\u003e, validator2: Validator\u003cB\u003e, ...`)\n\n### `parseBoolean(): Transformer\u003cstring, boolean\u003e`\n\nReturns a transformer that parses a string as a boolean.\n\n### `parseDate(options?): Transformer\u003cstring, Date\u003e`\n\nReturns a transformer that parses a string as a date.\n\nOptions:\n* `options.iso?: boolean`: require value to be an ISO 8601 string.\n\n### `parseJson(): Transformer\u003cstring, unknown\u003e`\n\nReturns a transformer that parses a JSON string. Since parsed JSON can in turn be almost anything, it is usually combined with another validator like `object({ ... })`.\n\n### `parseNumber(): Transformer\u003cstring, number\u003e`\n\nReturns a transformer that parses a number string.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperhive%2Ffefe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaperhive%2Ffefe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperhive%2Ffefe/lists"}