{"id":17579394,"url":"https://github.com/fponticelli/partsing","last_synced_at":"2025-04-28T15:18:29.084Z","repository":{"id":33505372,"uuid":"153971542","full_name":"fponticelli/partsing","owner":"fponticelli","description":"decoder library for TypeScript","archived":false,"fork":false,"pushed_at":"2023-01-03T18:17:24.000Z","size":4113,"stargazers_count":8,"open_issues_count":17,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-13T22:09:27.919Z","etag":null,"topics":["functional","parser","parser-combinators","typescript"],"latest_commit_sha":null,"homepage":"https://fponticelli.github.io/partsing/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fponticelli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-21T03:56:57.000Z","updated_at":"2023-04-21T01:40:08.000Z","dependencies_parsed_at":"2023-01-15T01:16:18.348Z","dependency_job_id":null,"html_url":"https://github.com/fponticelli/partsing","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fponticelli%2Fpartsing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fponticelli%2Fpartsing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fponticelli%2Fpartsing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fponticelli%2Fpartsing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fponticelli","download_url":"https://codeload.github.com/fponticelli/partsing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250693842,"owners_count":21472330,"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","parser","parser-combinators","typescript"],"created_at":"2024-10-22T00:44:41.721Z","updated_at":"2025-04-28T15:18:29.057Z","avatar_url":"https://github.com/fponticelli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ParTSing\n[![build status](https://travis-ci.org/fponticelli/partsing.svg?branch=master)](https://travis-ci.org/fponticelli/partsing) [![npm version](https://badge.fury.io/js/partsing.svg)](https://badge.fury.io/js/partsing) ![npm version](https://img.shields.io/github/license/fponticelli/partsing.svg) [![codecov](https://codecov.io/gh/fponticelli/partsing/branch/master/graph/badge.svg)](https://codecov.io/gh/fponticelli/partsing)\n\n*ParTSing* is a decoder combinator library for TypeScript. You can use it to build parsers/decoders from text, tokens or really any input value.\n\nIf you want full control over what to decode your should start from `partsing/core/decoder`. It provides 3 types parameters\n\n* `In` for the input. Remember that you will need to decode one portion of your input at the time and your input probably needs to track somehow the current position.\n* `Out` it's the type of the value if successfully decoded.\n* `Err` it's the type of the failure returned when the parser fails.\n\nThe library provides two additional set of utility functions to decode `string` values (`partsing/text`) and native JS values (`partsing/value`).\n\n## Decoder Error\n\nIf you adopt the generic `Decoder` directly, you can define the shape of your error. The library provides a `DecoderError` type that should fit most decoding needs. The type of errors available are defined in `partsing/error`.\n\n`DecoderError` provides a simple method to debug the result of the decoding (`toString`) but still leaves the flexibility to give granular control on the representation of the error.\n\nBoth [Text Decoding] and [Value Decoding] use `DecodeError`.\n\n## Text Decoding\n\nTo be able to keep track of the position of the decoding within a `string`, Text Decoding uses `TextInput` to track both the entire text `input` and the current `index`.\n\nThe `decodeText` function simplifies the inputs and outputs of decoding text. It takes a text decoder (`Decoder\u003cTextInput, T, DecoderError\u003e`) and return a function that takes a string input and returns a `DecodeResult\u003cstring, T, string\u003e` (where input and error are of type `string`).\n\nIf you intend to write your own regular expressions decoder functions, consider using the `y` (`sticky`) flag. When used, there is no need to reallocate slices of the input string saving memory and CPU cycles. The `sticky` flag is not available for all implementations of JS.\n\n## Value Decoding\n\nTo be able to keep track of the position of the decoding within `any` value, Value Decoding uses `ValueInput` to track both the `input` value and the current position within it using a `path`. `path` is an array of either `string` (object field name) or `number` (array/tuple index position).\n\nThe `decodeValue` function simplifies the inputs and outputs of decoding values. It takes a value decoder (`Decoder\u003cValueInput, T, DecoderError\u003e`) and return a function that takes `any` and returns a `DecodeResult\u003cany, T, string\u003e` (where input is of type `any` and error is of type `string`).\n\n## Example\n\nA simple decoder combinator to parse color values from strings into class instances.\n\n```typescript\nclass RGB {\n  constructor(readonly rgb: number) {}\n  toString() {\n    let s = this.rgb.toString(16)\n    while (s.length \u003c 6) s = `0${s}`\n    return `#${s}`\n  }\n}\n\nclass Grey {\n  constructor(readonly value: number) {}\n  toString() {\n    return `grey ${this.value}`\n  }\n}\n\nclass HSL {\n  constructor(readonly hue: number, readonly saturation: number, readonly lightness: number) {}\n  toString() {\n    return `hsl(${this.hue},${this.saturation},${this.lightness})`\n  }\n}\n\ntype Color = RGB | Grey | HSL\n\n// Hue in HSL is generally measured as an angle, not a ratio\nconst ratioDecoder = regexp(/0[.]\\d+/y).map(Number)\nconst rgbDecoder = regexp(/[#]([0-9a-f]{6})/iy, 1)\n  .map(v =\u003e parseInt(v, 16))\n  .map(v =\u003e new RGB(v))\nconst greyDecoder = matchInsensitive('grey')\n  .or(matchInsensitive('gray'))\n  .skipNext(optionalWhitespace)\n  .pickNext(ratioDecoder)\n  .map(v =\u003e new Grey(v))\nconst hslDecoder = ratioDecoder\n  .repeatWithSeparator(3, match(','))\n  .map(v =\u003e new HSL(v[0], v[1], v[2]))\n  .surroundedBy(matchInsensitive('hsl('), match(')'))\n\nconst colorTextDecoder = decodeText(\n  // the `eoi` at the end, makes sure that there is nothing left to decode\n  oneOf(rgbDecoder, greyDecoder, hslDecoder).skipNext(eoi)\n)\n\n// all results are wrapped in a DecodeSuccess\n// colorTextDecoder('#003355')          == new RGB(0x003355)\n// colorTextDecoder('gray 0.3')         == new Grey(0.3)\n// colorTextDecoder('gray0.2')          == new Grey(0.2)\n// colorTextDecoder('HSL(0.1,0.2,0.3)') == new HSL(0.1,0.2,0.3)\n```\n\nAnother scenario where decoding comes in handy is to validate, type and transform payloads from JSON requests. You can decode a value (after being parsed by `JSON.parse`) into one of the `Color` types described above.\n\nA few examples of valid JSON payloads:\n\n```json\n\"#003366\"\n```\n\n```json\n{ \"grey\": 0.5 }\n```\n\n```json\n{ \"kind\": \"hsl\", \"h\": 0.2, \"s\": 0.5, \"l\": 0.8 }\n```\n\nHere is a `colorValueDecoder` definition that can deal with those cases:\n\n```typescript\nconst ratioValue = numberValue.test(v =\u003e v \u003e= 0 \u0026\u0026 v \u003c= 1, DecodeError.expectedWithinRange('0', '1'))\n\n// reuse the rgbDecoder defined above to validate and trasform the string value into an RGB instance\n// example: \"#003366\"\nconst rgbValue = stringValue.sub(rgbDecoder, input =\u003e ({ input, index: 0 }), v =\u003e v)\n\n// example: { \"grey\": 0.5 }\nconst greyValue = objectValue(\n    { grey: ratioValue },\n    [] // the empty array means that no fields are optional\n  ).map(v =\u003e new Grey(v.grey))\n\n// example: { \"kind\": \"hsl\", \"h\": 0.2, \"s\": 0.5, \"l\": 0.8 }\nconst hslValue = objectValue(\n    {\n      kind: literalValue('hsl'),\n      h: ratioValue,\n      s: ratioValue,\n      l: ratioValue\n    },\n    []\n  ).map(v =\u003e new HSL(v.h, v.s, v.l))\n\nconst colorValueDecoder = decodeValue(\n    oneOf(\n      DecodeError.combine,\n      rgbValue,\n      greyValue,\n      hslValue\n    )\n  )\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffponticelli%2Fpartsing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffponticelli%2Fpartsing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffponticelli%2Fpartsing/lists"}