{"_id":"alge","_rev":"86-79565a6b59d4592ca42b4600ebbf9a88","name":"alge","dist-tags":{"latest":"0.8.1","next":"0.8.1"},"versions":{"0.0.1-next.1":{"name":"alge","version":"0.0.1-next.1","repository":"git@github.com:jasonkuhrt/alge.git","author":"Jason Kuhrt","packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"tslib":"^2.3.1"},"devDependencies":{"@jest/types":"^27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"_id":"alge@0.0.1-next.1","gitHead":"c860bcc6c2d09019d022892805373beb34d935a3","dist":{"shasum":"cca49af9c3caebfcd4cc6aed902dd8a7b23a37d7","integrity":"sha512-ZURa1sjiLip+YBQ9aDUwE3p+1deLW2giAfxewQ1yOKhEEJmewN6HSMNcaNfAr01CSzYjCuUhCgkhlBDMR3fxOg==","tarball":"https://registry.npmjs.org/alge/-/alge-0.0.1-next.1.tgz","fileCount":21,"unpackedSize":17075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiP83FACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoLJw//cEzWQ7akYm7B+J/VXLhpVX3pPsvpS3iyVp9k6tMWrXb4g35U\r\n+BZauV80C8BcGP+KEyl5eWcIqO2xyS4/Kl3LcoekpaG8hxT0rbrrSefgCb31\r\n59cm2HSQRVUbvfsfTCYppWEg/SJOvbYtPKsxBt8bkSkMbo4W5fVa37gRA5LA\r\ngaSqeSNpgQN2zgxH9xvKjGFJs0bPqIrw9cevWkHmVX7vxMgOZK9v37c8p5J1\r\ne7HQnCiqzktsuo2dOoV8XQT1mW8JVFAmFIY8QgOTEmtqUtK0MkZmMQmyq5nQ\r\nGYytQ5EfXtR6Sg9k7PqrYUqxmhCEZcqs2e5iz2HV3x/5a9efvqZOSyubqSiP\r\n0eyP2b46MiTMlXYrA1s0e6gXCFomc6HFnZakysn01Wps50FOLPcmQFvM6Xht\r\n0UB5PR7UkcfqyDyGErXZgexFUSZ1f3W1s+EWj0bcorpTaGXW0mS79eR1vGaC\r\njdCgCYM7mJlNWabYm3cwZJ/VkvRmfGI1NZdt5gv1w5KImvHEX3ZwOjZkm6SZ\r\nEmuY/PpoRkP2OyeNwSEhiXDTNEGCw9JSFJHF8is05jFX1ekjOAFuasniliT1\r\nG1akNNlpLOxcifhOmJeUD0sBrpQGQNRj8qp2tDiXfr2wi/w2VsZZ18nYOHaj\r\n2P0wTX2bvEoYphSMdLDlMJhTilyheLiSlR4=\r\n=PFZd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKSWwj1N10YBgjdxsWpdmoQl4PCaz1+R2wpOZaPBQSwgIgUfAphiLEN8Ha2hUESs07EfG2yFnqBILs9Zbt5KB07Hk="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.0.1-next.1_1648348613600_0.7692881208685036"},"_hasShrinkwrap":false},"0.1.0-next.1":{"name":"alge","version":"0.1.0-next.1","repository":{"type":"git","url":"git@github.com:jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"^27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"readmeFilename":"README.md","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","licenseText":"MIT License\n\nCopyright (c) 2021 Jason Kuhrt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","_id":"alge@0.1.0-next.1","dist":{"shasum":"7adaf9a9f21aef8188e431b15e3b6d9638df9e2d","integrity":"sha512-NdZoO0yAyhvnp6rlT3m0DRj8i66NmgROp7EJvQgeMK2DlqitdS7OzH5w1tkJuMXNLPmFTQicPSyejH4tZnmmxw==","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.1.tgz","fileCount":28,"unpackedSize":64849,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiP9QyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqp9w//ZmVD5uvzy+wEUZYKRT5GVKLcpZ3MxqkqMQv+eD7qh/2E5a0A\r\nvqSI1FN7zYaGRR3H8iNFh/OXyYJ9EyD6uPtF98vM7NPFQT8HdyQxId5xWkD7\r\nEG2lddI60yW65Y8Zg+fAXymhtaAiss/LP/RDGDir5WpXWKU96583RDPHK641\r\nwUNRjUeJVvRUbeKiBesaAvTiXEy7zcPgbSPdXbM8qenwOgh7FoWbLd6MVP0x\r\ncCEUiTBwxxNtpQwnpNGtXEzia51fvnBGM1Aan8HZmXyCH0JK+jevfRk3/2qE\r\nmSb7+IqxBX5NQB5e3i1nfxEAKJ/8JU2hBDwQDSpb3MwtORy/9ulUdwMtdg5g\r\nQsF5Xhqjs8i1Z5uJee8FJ0+LhRO1PU8voOWm0sOXmuhroU7yeFHGGsRIsylV\r\n/oaO8vbO5emt1VLQ06i5jEQN1rlm9mg3cF4eOgd5NWkIP9If8oK2AKOe0YXe\r\nB81ntQmUknWZ3eiWZR7CTlAfRkAToCgwjB2YlraYXpaKIpUrJLC6LdAt2Skh\r\nfjP/ZUbYxaaAvPcfmTeTGVr4UQfVaIeJo9bIoF7vyGMn+wa/U3jl1Ki2IDuF\r\nE/OmDv9Vpa5fX4OIQvoYvXCUGrdMyZxR/OGu/foDwcLLTSHwixkHCn+6VHMb\r\nFk2QCjEMOrQ2lRsZpam6Fa2gGfXYn6/zXHA=\r\n=Bttd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHcH6bXJYOQN54vbbGgdmk3aGejmcI963beo3/yapQY4AiAsddGCD78evxjjzByENVlLP8rPPGOZXp0gJVkSPTSWhg=="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.1_1648350258248_0.2994062082331912"},"_hasShrinkwrap":false},"0.1.0-next.2":{"name":"alge","version":"0.1.0-next.2","repository":{"type":"git","url":"git@github.com:jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"readmeFilename":"README.md","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","licenseText":"MIT License\n\nCopyright (c) 2021 Jason Kuhrt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","_id":"alge@0.1.0-next.2","dist":{"shasum":"b694e8838d83835b096dfdecf8e70405a522632e","integrity":"sha512-8ePW4Y4YitRoQHu3Axg/yX4/Nfhmppiod2/TzvUUk6F0sSMjyTIEuLQQoNRbsNxGoMkCMqxFE+E5VQlGsPe8bQ==","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.2.tgz","fileCount":28,"unpackedSize":64848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiP9lCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrzSA//eCbSwwfvw6sDmnkUOST8lsehUQyRvUt86oVoN1O+0hiaxRCe\r\nNvqNIME3XfrirkjsaKWFlaU+wahnlTlNQj+WAH/0c17EVX+elo5hn2nLuKIb\r\nKLXtN6qZjEPD0GvdTv9GwlaYJtXCRF95ShxBxydb5Mfl2yV16vBDT1t6cQ8C\r\n3GY8ZYmWwTBYIiNAQ8IgqKzxXAHmVKS6LqZvfn6sb44KBQvDyhBiNCh7+UEe\r\n48qNkxWtqk9Cz60mRMnNhKaTjOrQXv9Kdq5U5GN2xyEKn6lUvxs8i8yDIn6D\r\nDSoFq2Zz79GcumxgmyIKxaQcxTGq6to6aWGzBBuiO5Q3VJoW5UdIvC5UEiG6\r\n+jJtYWex4zTd68K+5a4HRjiZAwU9pefDBeAAcNhlC4SIyafHSmCWyXTVZ9uS\r\nYO8DuWJdtT4XPvZBcFRbiqYkC+0NH435yKKNulD/aTwUpf2bT+NM30S/7Q04\r\n9zy04+J7+lY3ZCOsM0rvMYTWF15UHyMQAOXBgSuJoilWJCdv6Z7GAUFOj417\r\nG6RaAUp5oUZiCC1FSseaWZ0OSdIy67jo/q0Fc52IOjI8dBZbt1gMH0kJZqA9\r\netnBUDaVENfcarUnFFOO+na2foFH1zr3RzrXXFwNS7tLPg38AOveETy+03r/\r\nn4J3qgfbOgtTOdt6aU6WzHEaHCaYLN2qPeE=\r\n=JpMf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHll1vA7GG+wjuXVzct6oqvJCe6oHt1LGTKOc+Cobsp7AiEA/EnZMRygwNzyiB+FKfjYEVx+MNKqj+3r8zB4ot9ia88="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.2_1648351554369_0.13513959426619815"},"_hasShrinkwrap":false},"0.1.0-next.3":{"name":"alge","version":"0.1.0-next.3","repository":{"type":"git","url":"git@github.com:jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"readmeFilename":"README.md","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","licenseText":"MIT License\n\nCopyright (c) 2021 Jason Kuhrt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","_id":"alge@0.1.0-next.3","dist":{"shasum":"4e723d1a38bc2e2e432de345c449ae92b44b4eef","integrity":"sha512-H2YQktNj+i/8YKeeW2W/ffLT/5e3Ab/YAwwQy/64VS0Kx5FbtJ9zMFpuYvj6JJ15wpmTAiNJu+ebym5eIq6QMw==","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.3.tgz","fileCount":28,"unpackedSize":64848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiP90PACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoUzg//XrO8Stv7+oQMSGfrpVnRjpD6UxkL40kiljdHzE6xuOvTta0V\r\nTt0PVi0rP3XzJ2GXvK43g0M25gafl/G4Vz+HyW94neAN6ps24fFhrS0PzOe5\r\nXsW7mWcwUCjL5EjOcSsAmsD293NN1Sp2PWm6AdUPGMAj41fy7anqGiJFbX+M\r\nAwHSns5Ot59e+Cp98gYhuyA4NxwIE8jntrkJNwTYM7iJfq5KuS9XiY5PcUGk\r\nZho9JCdnhZnMJ/gmpwiDydO38ewGX2hRRUPLGGodBnW+mb3e8/cPW2AU5yeo\r\nzIXrITy+iavinv/mR5Cj/Mjlc4yGe2AUnN5oOeXYLCE15NTqpHZKlJ0PNK4E\r\nYs6bMll/PygxC/aptzBD172lSakvj4ZIDdwtBHtWXA16qkS1GtRvwkZhCM+j\r\nFd/aGDp3jB28AFqK6hk46nLFMRiXntm/wu35BrMjZ6IIvxqN3YqJ8l+NZxmW\r\nqDfO2wHsPbs82WhfjNmgJu9okboHtoZFhBmadXNAtEJsj16suZcgNESoivQR\r\nIHmTmIO0xwh+6jIghnQQAjWmY4ADauFfBtUPHWma/eg0+pLeZ1bwbIde6sbN\r\nyeNvnm8EBAow8xoonALltoWm+jKvbfmmrkNlb8mRIBqCwdE0cRzeLPvdFCFk\r\nN7p4NRmFTkQrewbM1527uOjSEMq0gvedXsU=\r\n=JcPh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCuU+A/CTi6ztLrjXCrBybszTZ1A9w/zpT7S1LI6vq6qgIhANrdLIKyW4PzyJjokSSDlMCrR0yPZH1y8MR3dc9nzAcX"}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.3_1648352527740_0.5829644068947268"},"_hasShrinkwrap":false},"0.1.0-next.4":{"name":"alge","version":"0.1.0-next.4","repository":{"type":"git","url":"git@github.com:jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"readmeFilename":"README.md","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","licenseText":"MIT License\n\nCopyright (c) 2021 Jason Kuhrt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","_id":"alge@0.1.0-next.4","dist":{"shasum":"482be3373f896619c48e3e7077a3dbcb2dca97d8","integrity":"sha512-BhrRE4Zjom2oRwCN9dTsX1dWbA1cb9tbFKi/1Mh1wwXc+lD868RibT+5i8xQDXHlW5g9I/XyoLPR4tnzq9b/Yw==","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.4.tgz","fileCount":28,"unpackedSize":64848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQFI+ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpzkQ//Q0lSTvffovBt9F2H7wgMZDj0LSowRelbqUNaGHCdSeXFJ+IO\r\nj4bx2xP+ZjXD/6OdpxlL5k9tV1Wqc3DjftLLs3ZlF7ghvol9RT0jDvxUMlxO\r\nhSX0xbfXcGFzsMJgMaYbAgkkVu2gv51S8O2hfDSecY8jaFhKuGXW1hjpEhO6\r\nnDvqT6zdfinO2/JCDMPei/Vxh+V5qCG6E5BVXxh20e4WsP9MLCGLeNMSjWf8\r\nskQhug+FKsBpiZ5e71z1eCI/+fKhPmEotMwnJ5EM5QKGbT3KhQ1FwS/4eGmq\r\nlBjahCJWgsvC877h5d2A5O9SykyzTZ0sVR4X5oO5pDIOmjDQNZ8lHo3egFCP\r\nDY9wwJkw3n7EbXR/ZefwLphp0F6S4EmulRiq3jmiGi5FDWKRUdhaa1KO6jIg\r\nuhYZttpY8km+GhZ/v84X7b3raSmBfW0KVRfLCdFttU2Omrcgg9h+HIj7nKPO\r\noQaGzdMMrWI418XKvTKGgoD5wa/myy78o+I0sqW4frSnJMri68T+ckwKomAp\r\n5985qfskOREIT61ojBiua0gwkn4OtFXysg2RSGxMO/ql4iNV/aVvZN4sIL9x\r\n8zG3Tpt8yyriqK3lv4KaKKueVKWxyfYYzoVkx8419JSQvpXqcjW5MbqXNZ/R\r\nMaMDDaeXk5mwWp4Ube8zrURpBunu93EOdn4=\r\n=xTYl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFYY2sgsXF45BoZFREF5U3KkpFj3qvSPrKLVRhAN1PPhAiEA3rqv88sslK0THpfBHufU6xuTrvQATG4XRph+2WHhqOw="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.4_1648382526103_0.4736009321983532"},"_hasShrinkwrap":false},"0.1.0-next.5":{"name":"alge","version":"0.1.0-next.5","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"2a1746be97c0b5c2fb802721ac2b0d64fd456a38","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.5","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-QoeL8LAwtiZ+JnuCOxqHqdeFIAEHq43UQ28fZW1i7KEXHuNoUAVLfAfoWikPEzSVB35uAZoyYv2pNDNgKTiuNg==","shasum":"487dc6634fc99b0697f6d5fdeb924c45a3473818","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.5.tgz","fileCount":22,"unpackedSize":64847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQFlaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmrd7g/+K65291I9Rie+kOGnrLADiC8CyKXJbopr7PCFuIG3WVuFF+km\r\nJgOPYra/9NBVWsFP++hsXOM8GBuv/dkFy0+vEiARATLYm0RdHXlhNgCZQO1D\r\ngOa6m+z0AY3eujAsYarPBmP87Mm0Zsm4+sjWijypNLQFfCboHvFK3gmEOU23\r\nAwodvDhzsdt3SU45VB0ZPYpnPn8nMlv7L5vxbIgIESUkOEhME6O9JmRA6gs7\r\nMn1JaDNV+/pbUQOAemLI6eg1cULhO3ffe/IzPHmiTOmIHUmSEDTrPn2vy3ru\r\nrbocxCXfPQAmQJ+z/DJyd/ShigW5Cucbnwo1rszw14Yzg9x6/sbLrNmK/8iX\r\ntvJq/7P9rv9l3f2oLenDpzlgoprOPUMGEwiBoLbeFzSxpKLyzzMOyBzDQveq\r\ng1XzTm/P7ZAi1dEe2i9P/ot+MMYen2ssTU+EQkaoQDeyxgrE4ucSLpSfjXGA\r\ncs3TWJWKUXlqM3h3cA3tFHAbkJ+JX9ikLt3+aXzWPOM9CNVRFEsTn0HKEP73\r\ncpIQY+6Q0K8FKKOTC/jOLJf2tCBNTfBqNznF3hM4/zoLoftM6+DIWWTvStF0\r\nnhxYvx3OF3xvyte/9bpAcjEFESJNgNKxmRj7DysngJMWgwZd5fbZIg7TrVqS\r\nYTfepnbek0AFauiKJo1ApztGhIlY+yUOhiw=\r\n=/SfD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtjv15je/4fGc/ew9GcfhgcEDIE4VFvEkfRp8GHjQRFQIgVAZClqs4UssIHv9C2gmN7ZbR7b030n7YObOuXKQqx5o="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.5_1648384346094_0.3352352425727212"},"_hasShrinkwrap":false},"0.1.0-next.6":{"name":"alge","version":"0.1.0-next.6","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"689861f364cf5406470bcc9124ddf588e7fd4b32","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.6","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-IcKQwTp9EEySZ21gMAOs4w6ceOmmwpcNoz4VwbFkRYovmHL9edupxz/nn/N+sCOW7UQXV9cEewUvjRcwBgFG7g==","shasum":"c135b6b97df3482f6e7439d0d50457cd6fe29de0","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.6.tgz","fileCount":94,"unpackedSize":77819,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQbk6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqxshAAhGLMECjFyXv7afL0EJ75xIlyoMfYJLvEFHHiRv7EstKdCukA\r\n4Cz60AhRFaAPD+fjp1IDEOywtoI+WRiafp9Wu0+HWgtK9nCleWhqd7vLJxgZ\r\nypRcjrsg0Nkme5I+W1KPAN4CrCmkArQPL7rR4q16HQ/nQHZSO8cY/m1W19zK\r\n1ujpa4MTNllxQlkzykvBXndJD61+a0q4gmrEfp0VfRgltDYzTL728HyimVRa\r\nPVv/lpd8Z3Y4cg5p5cc7CsO4ibOXRH2aPmdgIFan1/j7GzOILrjVEAXYJezt\r\nF1gxtvXIinWYn5bk3DaVRtYqE2BUXWBm5+yOH54NxikDLveOuDKsfDfJASme\r\nmyrTi2kSSDrKIVs+foU9ePsjm684pCHtsggZJ0Ke1/61KecLdPq/m+5JXWe2\r\noD+QSdBHBRJi2M/FP++/cT504LsQ/ft6Y+ggVZW1vS2HFn+JQ1aL1MerAOZC\r\n3L77hFpkIjJ78jY+w20ayPV0ung31rA2/fNge85uhEIq3+La5BClfozxcww1\r\nR463w+KIiYrevfTszIU7CioBLYAgPOTttxMBDbW2IlpuP+wNjcIE3Fpa8wGn\r\nzx4JYuzzJhsA4fflFCe8IYI5aYELObIFRCQb9yGBOkIc+qpPTYvYGgEoVC/m\r\nL/x8SSecDvX5bvr5tPUoPFYX4kK82x21s0k=\r\n=74kX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDeBI4ftizd+DryGGLy6ycRiDSgye7JRgoFoOtKLYjmHAiEAmqC13kscmGZc6OS7vRlrBoR5c8AaUmjMN7u5WP3sZzg="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.6_1648474426279_0.7688998500700719"},"_hasShrinkwrap":false},"0.1.0-next.7":{"name":"alge","version":"0.1.0-next.7","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.160","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.16.0","@typescript-eslint/parser":"5.16.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"dc8b315bc06e55332e4e12b52b2594038423b154","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.7","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-ccfRic5/aWzZuOQWFphD4BbXBXKkd5KoK0ZzsMDcB0jf4rExjLuo7gI0/4XM0o5fWH3KkmTQk71DxvE6BNHlaw==","shasum":"59e777ad24ecee9c01b1238d4233c6392c9c2aa9","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.7.tgz","fileCount":94,"unpackedSize":81255,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiQlR2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmor6g//fjZ0DKSSMHRTl4bbH1YMq+JKtN8l/PBsZyOmQ0bt5X4G9za0\r\nxhQDAqOnDptg4JZkse9aQSxP+UdmfXntt9EsAdFgX63UeJDAUuvd1xTIKvRl\r\n33/KsFHP1DiVGeob9iCFR6tdnBuv4CMGeWD+2xUb5jSaRdr0+OTYYkvG8zlI\r\nKMwBd2yOy7ST3P50z+W7F1C6V8ioJ4/kHDjLpVeC3Wc4IUmv8ARkWWZ5ujoI\r\napfvlXvoHKVGDntmmVz/2LrtfnUoXXiRvAZNJwLTnRWpdetedpd3zZx33FHy\r\nalC2i0Dnz008zvKgBwyXz8lx8yil3V9+0fnTmMRuU2JzypY1kVbN5d6LpNYa\r\nTDZLa+v5XUJz59Ivpg7jFtYlD3x8IKneC5fEPGPIShPeHaeTPgHjtCZjzJ0h\r\nO0CG5VhGs57fiaqonCQu+CYLtqXVH//Z3eLVGHD9DUgR0dZ7KkEbg2WRb5xS\r\nDyZuei1xiXDO8OSb1BGyucD+qjvfQSbm1FY45BW0EenzG8fn3A/hUfzdNSST\r\njE2nsDlAXaj464LKGIRur7RfnSbf/U0VvO/FNVQBW6yuzrLy4VoJr3iEI8Ws\r\n8C66E6FXwTPvOgb0H8WCW1zE5urimcdZnky5N6gRZXVy0mik0HfvdJHWb7+g\r\n0n4WHsfX+CmH7ZbjGTRDw63MbhG0hB7Q3wU=\r\n=gWje\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYDaNjWwJN2Z6gTFhljWXBRzrVzeUFmjKSW3jr47ch9gIgZm/yhfLU2LfvG5Lj0exV6we/tHAWqFGT70T29oOKwp0="}]},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.7_1648514166031_0.45340840428859397"},"_hasShrinkwrap":false},"0.1.0-next.8":{"name":"alge","version":"0.1.0-next.8","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.162","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.17.0","@typescript-eslint/parser":"5.17.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.1","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"^0.19.1","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n<!-- toc -->\n\n- [Quick Start](#quick-start)\n- [Features](#features)\n  - [TypeScript](#typescript)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n  - [Dripip](#dripip)\n  - [Simple succinct friendly low-barrier issue templates](#simple-succinct-friendly-low-barrier-issue-templates)\n  - [Prettier](#prettier)\n  - [npm scripts for development lifecycle](#npm-scripts-for-development-lifecycle)\n  - [CI with GitHub Actions](#ci-with-github-actions)\n  - [Renovate](#renovate)\n  - [Yarn 2](#yarn-2)\n  - [CJS+ESM Hybrid package build](#cjsesm-hybrid-package-build)\n  - [VSCode Settings](#vscode-settings)\n  - [Readme Table of Contents](#readme-table-of-contents)\n  - [Useful TypeScript Libraries](#useful-typescript-libraries)\n\n<!-- tocstop -->\n\nProject template for TypeScript libraries\n\n### Quick Start\n\n1. Setup a clone of this repo, enable [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature), and install dependencies:\n\n   ```\n   gh repo clone jasonkuhrt/alge <your package name> && \\\n   cd <your package name> && \\\n   corepack enable && \\\n   yarn\n   ```\n\n1. Run the bootstrap script. You will be prompted to answer some questions:\n\n   ```\n   yarn ts-node scripts/bootstrap\n   ```\n\n1. [Setup a repo secret ](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) called `NPM_TOKEN` containing an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) for CI package publishing.\n\nExample:\n\n```\ngh repo clone jasonkuhrt/alge foobar \\\n   && cd foobar \\\n   && yarn \\\n   && yarn bootstrap \\\n      --orgAndRepo 'jasonkuhrt/foobar' \\\n      --developerName 'Jason Kuhrt' \\\n      --packageName 'foobar' \\\n      --createGithubRepo\n```\n\n### Features\n\n#### [TypeScript](https://www.typescriptlang.org/) for Type Safety & Productivity\n\n1.  Optimal settings for the safety of your implementation\n\n    1. [`strict`](https://www.typescriptlang.org/tsconfig#strict) mode enabled.\n    1. All lint flags enabled:\n       - [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig#noImplicitReturns)\n       - [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig#noFallthroughCasesInSwitch)\n       - [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)\n       - [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature)\n       - [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride)\n\n1.  `.tsbuildinfo` cache setup, output discretely into `node_modules/.cache`\n\n1.  Inherit settings from [`@tsconfig/recommended` (Node 14 flavour)](https://github.com/tsconfig/bases#node-16-tsconfigjson)\n\n1.  Base `tsconfig.json` shared across `tests`, `src`, and `ts-node`.\n\n1.  [`ts-patch`](https://github.com/nonara/ts-patch) setup for enhanced language features:\n\n    1. [`typescript-transform-paths`](https://github.com/LeDDGroup/typescript-transform-paths) for a **_working_** [tsconfig `paths` config](https://www.typescriptlang.org/tsconfig#paths)!\n    1. Intentional avoidance of [`ttypescript`](https://github.com/microsoft/TypeScript/issues/38365#issuecomment-921889655)\n\n1.  Optimal output setup for your users\n\n    1. Target ES2020 which Node as low as version 14 has good support for ([Kangax compatibility table](https://node.green/#ES2019)).\n    1. [`declaration`](https://www.typescriptlang.org/tsconfig#declaration) so your users can power their intellisense with your packages typings.\n    1. [`declarationMap`](https://www.typescriptlang.org/tsconfig#declarationMap) enabled to make your published source code be navigated to when your users use \"go to definition\".\n    1. `package.json` [`typeVersions`](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions) used to emit only **one** set of declaration files shared by both CJS and ESM builds.\n    1. [`sourceMap`](https://www.typescriptlang.org/tsconfig#sourceMap) enabled to allow your users' tools to base off the source for e.g. stack traces instead of the less informative derived built JS.\n    1. [`importHelpers`](https://www.typescriptlang.org/tsconfig#importHelpers) enabled to minimize build size.\n    1. Publish `src` with dist files so that jump-to-definition tools work optimally for users.\n\n1.  `ts-node` for running TypeScript scripts/modules.\n\n    1. [Setup to use SWC](https://typestrong.org/ts-node/docs/transpilers/#swc) for maximum speed.\n\n#### [ESLint](https://eslint.org/) For Linting\n\n1.  TypeScript integration\n1.  TS type-checker powered eslint checks enabled\n1.  Prettier integration using just [`eslint-config-prettier`](https://github.com/prettier/eslint-config-prettier). [`eslint-plugin-prettier`](https://github.com/prettier/eslint-plugin-prettier) is _not_ used to avoid lint noise and slower run time. Prettier is expected to be run by your IDE and your CI and if really needed _you manually_ via `yarn format`.\n1.  Setup as a CI check for PRs\n1.  Always display as warning to keep IDE error feedback for TypeScript (CI enforces warnings).\n1.  Auto-fixable import sorting\n\n#### [Jest](https://jestjs.io/) for Testing\n\n1. Transpile TypeScript tests with [`@swc/jest`](https://github.com/swc-project/jest)\n1. Useful watch mode plugins\n   1. [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead)\n   1. [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend)\n   1. [`jest-watch-select-projects`](https://github.com/jest-community/jest-watch-select-projects)\n1. `jest.config.ts` (TypeScript file) for type safe & intellisense configuration.\n1. [`typescript-snapshots-plugin`](https://github.com/asvetliakov/typescript-snapshots-plugin) for viewing snapshots on hover of `.toMatchSnapshot` method.\n1. [`konn`](https://github.com/prisma-labs/konn) for type safe test context creation.\n1. Strongly typed Jest configuration via use of `@jest/types`\n\n#### [Dripip](https://github.com/prisma-labs/dripip) for Releasing\n\n#### Simple succinct friendly low-barrier issue templates\n\n1.  Emojis ✈️\n1.  Feature / bug / docs / something-else\n1.  Config to display discussions link right in new issue type listing UI\n\n#### [Prettier](https://prettier.io/) for code formatting\n\n1.  Prisma Labs config preset, 110 line width\n1.  Setup as a CI check for PRs\n1.  [VSCode extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) in recommended extensions list so that when collaborators open the project they'll get prompted to install it if they haven't already.\n1.  npm script\n\n#### npm scripts for development lifecycle\n\n1.  `clean` to remove cache and dist files\n1.  `build` that runs `clean` beforehand\n1.  `prepublishOnly` that runs `build` beforehand\n1.  `format` to quickly run `prettier` over whole codebase\n1.  `lint` to quickly run `eslint` over whole codebase\n\n#### CI with GitHub Actions\n\n1.  Separate trunk and pull-request (PR) workflows.\n1.  [Dependency install cache](https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies) enabled.\n1.  On PR:\n    1.  Prettier Check\n    1.  Lint Check\n    1.  Tests across matrix of mac/linux/windows for Node 14/16\n1.  On trunk:\n    1. Tests across matrix of mac/linux/windows for Node 14/16\n    1. Automated canary release\n\n#### [Renovate](https://github.com/renovatebot/renovate) configuration\n\n1.  JSON Schema setup for optimal intellisense\n1.  Group all non-major devDependency updates into single PR (which \"chore\" conventional commit type)\n1.  Group all major devDependency updates into single PR (with \"chore\" conventional commit type)\n1.  Group all non-major dependency updates into single PR (with \"deps\" conventional commit type)\n1.  Each major dependency update in own PR (with \"deps\" conventional commit type)\n\n#### [Yarn 2](https://classic.yarnpkg.com/lang/en/) for package management\n\n1.  Painless/familiar workflow via `node_modules` for `nodeLinker`\n1.  Using [Corepack](https://nodejs.org/api/corepack.html#enabling-the-feature). This means the Yarn specified in `package.json` will be used. And note this is a Yarn binary shipped with Node now. In a future version of Node you will not need to even opt-in into Corepack. Make sure you've done `corepack enable` at least once.\n1.  Plugins:\n    1.  [`plugin-outdated`](https://github.com/mskelton/yarn-plugin-outdated) Bring back `outdated` command from Yarn 1.\n    1.  [`plugin-typescript`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript) for painless `@types` consumptions (e.g. You probably forget how to pull down `@types` packages for already-scoped npm packages, doesn't matter now).\n    1.  [`plugin-interactive-tools`](https://github.com/yarnpkg/berry/tree/master/packages/plugin-interactive-tools) for some slick in-terminal project maintenance.\n\n#### CJS+ESM Hybrid package build\n\nSee [Dr. Axel's article about this](https://2ality.com/2019/10/hybrid-npm-packages.html))\n\n1.  Use `exports` field to give support to both modern `import` and legacy `require` consumers using Node 12.x and up. For details about the `exports` field refer to the [Official Node.js Docs](https://nodejs.org/api/packages.html#packages_package_entry_points) about it.\n1.  Use `main` field for legacy versions of Node (before `12.x`) requiring the CJS build.\n1.  Use `module` field for legacy bundlers importing the ESM build.\n\n#### VSCode Settings\n\n1.  Optimize project search by ignoring `dist-*/`, `.yarn/`, snapshots, lock files, and more.\n1.  On-Save actions for optimal editing experience (e.g. ESLint auto-fix to [organize imports automatically](https://github.com/lydell/eslint-plugin-simple-import-sort#can-i-use-this-without-autofix))\n1.  List of VSCode extensions that users who open the project will be prompted to install if they don't already.\n1.  Enable `typescript.enablePromptUseWorkspaceTsdk` so that oneself and collaborators will get prompted to use the workspace version of TypeScript instead of the one in the editor.\n\n#### Readme Table of Contents\n\n1. Using [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc)\n\n#### Useful TypeScript Libraries\n\nHere are some TypeScript libraries you might want to use for your new project:\n\nhttps://github.com/stars/jasonkuhrt/lists/typescript\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"559b91db890640ed17c3fa09782fd492972054ac","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.8","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-kRj4Z7VnOBO74bXpMUUXn7LUGcpIFtV9iH3cBLL8oQUwHzDeIO9uEb7o7IeFyJ3aByvODyxH4ZxPtcq8rZR5wA==","shasum":"02a819f0ddbd7f320a17d7c671cd40987579d599","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.8.tgz","fileCount":121,"unpackedSize":88443,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDPOcKEqWJsP3+wrQf3LTPPJCZ7y9rSKVswvzu/5sTVqAIhAOwg6i/Fdw7CCSTJx0V31HIwQovURofIu1A0ftPSqCQ2"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiSHVBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrUOxAApEIcRaBDHX0R9US8qMdE6Dogp7VpM34vEa/j2t3dGLl2z1+m\r\n0RtvFDB1X/aWt2V4TNAguAfjfHQpjjU+b64Chu1ZGht0HKYTqC7AI1v9H4Af\r\nPTvzflc9DFy41m3wg4bh77EGUJL9VRWfYId+SslEpMUXme3yN2r5gcw+Igl9\r\nPI4W89grU+efD4nFbORjbTY9EvUD/EIQbIxDGJEw+cgW7hYHN46+BC9YmjrW\r\nyyFshvQjpzsoPWRDiF+D9ieJQbge19WfUETgelkIo4iD1pHz/NVfNaOmvg2B\r\nj9kELlUjnFLInf2N0a4BpCKZ/4ton7E95SzQeV0+KN2I/a70z65YFWUze40o\r\nuh0i73Zb7xLAlda7iHlRBt2QLneTQE7t6mpyQVB7PGv+UJGAfV3K8KpbT1R4\r\ns9Ia7PYLbXG16mbvvGDSQRHlCd7bQtBJreu8yK8clnM+pNYxUVrf1EV2cf95\r\nLbVlZvZTUytW5Y37RHdN4cXCKmS3peRJfPpLxOTk2Lo7yOcvE/qeCWlF/WN2\r\nW1DaQ8ytQy075uT9nIjo52XmY9RbFqroSed/i+7EgK4n0lLE1R51Gp/TRo1N\r\nRRIKiGUsFFhOXtGA9AfTaxDu6B9eGt5uVZP9j23MimY88fMzQrsdoqHKWVjM\r\n4oEP/Mxve2vsdhN5ZHjtzS12xvIVLndje5Y=\r\n=ZBCC\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.8_1648915777650_0.19066255740357074"},"_hasShrinkwrap":false},"0.1.0-next.9":{"name":"alge","version":"0.1.0-next.9","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.163","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"^0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"ba43ff22b2076df7657cf6b6e0d24f559353f5fd","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.9","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-h7UQUskpmCgFYVoxg0ouwse87ogEhAp16xRX5iKYyXApEKlycgjSj0aWc0O1ZdkGFh7J/9jK3cc/d0gvE4TuUA==","shasum":"d5b48d1626365128341b9fbbba7606c341928d97","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.9.tgz","fileCount":130,"unpackedSize":86913,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG6cJgaCvTHLzRQzbVQygpcAFEqXKlVucDLoQy8YI1DlAiEA1WcHDue2otHyfgWPYh7sTMSBGIooZ/DamjoMDPFn/9Y="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiV3XUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpNTA/+M2GC5BJIDU32meKkdppZSlZcs4svIjbiBlaUaoeiJ3m/JrKp\r\nvEnITHZxnOeXHkd42xE5ekxCFVcSYlPwK8QPH9Lks/UVarPrvGcD9UPcoXBa\r\n3MX7nCQvTZ/wyi4LNVqiQnWoGSEiLJ/s9M8rVkIuLSAjK7I5T/4cGhB3ezGs\r\n234w1Ap0hXLWyIp2xywoculah692RvoAC6pucVtxVEoP6oNIwKeNzklg26nA\r\nwMqlY1ljrjq9om1tlinrjMCKvMOsmnoDM6fowMeE6LJL9mv2JSRzKsuFHOkq\r\ngpHDIYchL4APy0HtSwPoq6zDvXAXZxUKc2tHVjubiQJTNufbb9JrPI6cwSWb\r\nGiddwVhNlgxzMco4wnhPnnXPtM11I8GKEa14jhzrc2nAYqcYXCV80vpqnclc\r\n9AE0o7oMkm/DVIZ1NUapvkvCE8UASIBATV/qtAaAH1Sa65/8y0nYvjtNs817\r\nmRVqQI8gDWT8Tj062EUWu7p3HORg6WXpLWuv39EH9fjkmKzzUU/dQEf6pQ2J\r\nVO7wqBK1hxAQWE5N5zh7Gdf6C4mi9HwzLnxIwx7s2TrQGkcwrwVZytITBhb1\r\nTPuvryL1DGmfrVnkEmUHODdo5ukznY5jniYNKeemFfGdiTieisMf7t2dB5yg\r\nG5QcThkahoCAeAIspLEcaksGhiSmAoKU5Io=\r\n=A2ab\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.9_1649898964488_0.4097837983190893"},"_hasShrinkwrap":false},"0.1.0-next.10":{"name":"alge","version":"0.1.0-next.10","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.163","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"150a25a3e665ce82bfa8fef208bcedc9713b72c8","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.10","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-h1G+Zm/9QBxGqQ2xVQvKKE07RypUeh+Y5NDQ0+6xud3dnrav49nGZqy9N/EcBAIAhTI1HmIhvpSBIfMD5pYcqw==","shasum":"aeca8dd179702b34725bf62e6fbf663efd62de90","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.10.tgz","fileCount":130,"unpackedSize":86913,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCGf3z+4ExbUl5WGrRY+yN0u6FN62sABOVysA2abTJV+AIhALFp0Ia320kawpElcl+l69saDxnz8ovWLPkQbkk0dkMc"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWBbjACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpa9RAAod+TWchZ5uZh4awN1gyuOFczSDBA4tNvUhMx08ElNO9u37kH\r\n+iE6wyVHpUliz+YCudBg+uN/rkv0kuXQQNkbf01UemqOTMYsy7nwPlBaq4G3\r\npPEf1gZ3YZIsXdGUmZZFsRfMDPdX70O8QIsf4NSBsxswK80GWBCqtx6QEdcK\r\nCNXBttu2ypbwFClwX8Ew9ndZdcawNU1Fva0C8LwyujReCTOa6D6Q01u8Twti\r\nMzRhHlAAcin05W2wMq3+hjwpp7ZdeXwZYV8N3QbqQuhlgsHCVxidQYyYxX/Z\r\nKILVTlT5/5iBRkXuq6FKffL+FHR4iegY4j6cxUrjNXs2W6G4ogJjOdoMeJOh\r\nytiXcl1T8rqJVGQo8sB4cgdxpVVmCr8FXhJskqoebE+PlaVcQp/9TFYdBCbJ\r\nOEZyOjTQc78f59s6hwryC0PDutZAf8qescENtmk6PaJQ4mNOIjRLmCGAyNDr\r\n7EFLDL/eQ/Abnead83/LyDvZjLqJU1yQyQlAj7lxgTeTjVaMwTYqPQ3Pe4Oo\r\nCaJSOwVXrKXnhXUcVLSi2YZrJ3UupSbzlhEwE7BJ56rroUDfj+hLC3YwMKGj\r\nxTx4lhrw6QZHeAAckEMPCHQVKUoZbjYXc/ZvXmcA99VgK+S5x3A+8uZxoVi3\r\nfegff/KuwRTGTw36h20cix9/tdUEo5wt5xQ=\r\n=7+Xq\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.10_1649940195152_0.16346796843959077"},"_hasShrinkwrap":false},"0.1.0-next.11":{"name":"alge","version":"0.1.0-next.11","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.163","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"af0081d56edf5f499c1cf4b5945cb1464e9f5c83","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.11","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-zctkRwUX8OfavWws5CiZ7oiI7ik/FYbzqFTcIqj9Dbuu5Tr78bILfxgZyty3uUkb8ZecffUw07wtyVOA7xnaeA==","shasum":"61388950825cfaef777a547fe7805d2690c63dac","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.11.tgz","fileCount":130,"unpackedSize":87934,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCy2T8TuT5mXxFhWF0yfqs5TR7IXLeyob3YyWv4W7CoxgIgZZ/QMWlls2ZlyzFqEalPb3tJ5uCmPbwFKDVXUp37ji8="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWJXMACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo5eBAAhqzH78RTwwZhQhjhDACP1wfL3PxmTqzWuy18osjp/xYu3grA\r\nuT0j820ZU7eO652H3XmYkmJV+eFMwkLHfYxUaVa1wSPv9Pin23hHg3iJY/iU\r\njpmKFSewJn5ae13BVz9aQlB/qnmEgO943DlmC0cS0o5YgtORD7IzsC3At4rL\r\n0Yz8iiRqNrP4ASZyYGzMMsWz5YokLEC84+jeliFqqXDExco7ceITC27sBOqp\r\notiTjm0WmZmiZN4mPBXhvOtoAso51X4lmIvihHevJK6pDlVl1bvImb8dCOWk\r\nmVLvMtncNaDLaM+hNO+Me+etxfpDTsv6YEBUp7/KnK/MPc6dkdQYzNgFJCdp\r\n9NPQ1ZM4I/C6mWZnZk/qfRoYJEsEaX3t1yZd2pFfL2IlgOgy+KEbXQFh1thB\r\npYzWaCxroi+4pT2hlPfOV9ILppMgnAW1w8CG7q/eBGNxmL4og12xOOafCOmZ\r\nGb8HsCERtbjvjZCKPVsto2m0fZDSUIql/UXMcnbuVU3BDmt8CYgkvbc8LIAQ\r\ndoOh0uRVBFrxOPWQq6RyA32Ba0FIZdVnnUtGx4+XrJu81moWfoy9/C+vwxc2\r\nslrIlwCDYIKPDWQBMbNlnVgHGl6KudxDU/zhqjqryHCX7AIkSBQuTTVMUjm5\r\nID7JiAFH5CvjH/+gXfjozg8obejJ2AOeJC0=\r\n=Qkz/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.11_1649972684176_0.18057534558351152"},"_hasShrinkwrap":false},"0.1.0-next.12":{"name":"alge","version":"0.1.0-next.12","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.163","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"11571eb6a861f8d640fc1e5ef9f0be4e279a868f","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.12","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-mPVfq4BWxJdHNDSkIJTv6ad8XrCwTUxIGbZEpCE3X0tUldvcRB/w7cRMC+QqP77G/4z2ss4QsUTn1Tv8kpKFhA==","shasum":"ad995ae7734313d10169aedb09f8157579570fa6","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.12.tgz","fileCount":130,"unpackedSize":90545,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEtwDqPMBqUB7BzKsHxLvCjEqak5JCVFtVHmAQNy1thlAiEA2uv6A5SmIUouaOmzj8FKVtg4vn0RE8xZ5Etzdid4BQk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWJ3UACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrpNw/9GsxYruMFrVN8J20jfkgv7wYQQE0g8OpqRt8MWWmuP8xUewbV\r\n8SxiWAO/LSTpzChxcpTal7WiRFjGp6ezjKH0gL1tulf48xsjwFwghKfJWhUT\r\npLdYJHBp074Fqdz1wqvnMaS+8/yj3vrHkr2Qzulqu38BAhiZ/b7THO/7bs1g\r\nlQjMj2jihIO7QdfcL4CzoqkCZLfgqWvPG3Zd+petKaWIneK2SPOK93w58aAo\r\noMZM7mdkJCbIB/3GaI2bkrY/nFdKJknfAcZglH88MEUy7qEGw9/Q8AJN2K/4\r\npFofedGsSKBAgE/5kuXCpWfrKVVu6sAfwGPqHr1bOGkfGCeBUJZ9BRzvhwQH\r\nQl+cddatI/0/bcM/OZUFruev9Bli2iu0hG11KEGbWh+oHMAkHJNB0PLIJu6z\r\nuN3YjXx9+Euox80YZ5S9hdQAyPTRWX6TVtkcFDCqZY8xfmm0XR/W2IRNbbIC\r\nStvtXDK0MkmgOPHEjcptyqjOZTbweKtjxLU3M1asRw/YgJkQUp+N5cNY3Rr6\r\n4I3UE+NADlG3muzf47+/9bfv5TX4fXNZgrZ84MXDv5sF/lXtWUcLIoTh3pbp\r\nJKzq8vxYiu/BQz/EGW2gQCrXZ5uqNjI3I+whcpf4NzxjiiLZsQbV+A1H0M52\r\nKZK1eoQ1oCGrzMFBf+mjYSZEuBKh4prn6zs=\r\n=dqYe\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.12_1649974740720_0.7309556342432368"},"_hasShrinkwrap":false},"0.1.0-next.13":{"name":"alge","version":"0.1.0-next.13","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.164","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"1afc828d6b0df60d16b0286664ba38982f617e02","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.13","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-AkkJwpKYC8EoVnsK5w4hebyHXdImlkE9MRjekTXuxG9HLWdKfkuNfoq5td1iPNYed7DK788HUQF1EONvyu1pCQ==","shasum":"896533c7ad0d87236b01e70a840ed397641c9e5c","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.13.tgz","fileCount":130,"unpackedSize":97206,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFdWNsb4JhZYxXDqoTWBf3DdSkCFA4jpFAAGbyYM2ZF/AiEAntpWEC7D9DD3CHlGbWQRekD0eKy2zBvVDGvSmrVhNTQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWtT1ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrIFQ/8CZ1cLL8TBHfT40dXCo93pfcNo4C+wYiCgO8uCcMy7MaDyw7j\r\ns0Zbud7qORR9J9chetPRIY5oAoZ5sJJZ4CxnFc+6ceBDz5RFJCK63JCWOWty\r\n9tAUbChVOzEvgQkuMhkHeMCdFgASpLE9gheGXveknNywsA/JBziKbNaAKSfQ\r\nDJRj8SVmRf/VKIdy8EpGvNUcILZgZZc7Y1fD4GLbCGS/RsbPKeCs8l+ZV4Q1\r\nlA1F83TQBCDUTyWuIY0LjiG7v7cvoxS5P0NPLCiwmm3QYwM1ARin1romGmT5\r\nJ9izjPhbv5RP2/lzprVanmkgwiZHVliHuutVanfp+LCyllSK5c7/BvBR4GXr\r\ntRUY2K9DrI/Byl3ft8sCPgUgImIGA1HE1wPW0dJostjnwytiQfj5X5oVmvnQ\r\nFuM5bjuj9K5hYU+OCSHQjV/JheGcz4w2xBXY3MHyLlMU8LvLKikW/1/abFuC\r\nqF114zYPzW2CT06Uyx7tul+xJPhq0mzV/ftaFUuY2nYyAD4zAQafaCAWpfLW\r\n+rHaUj3SGxguxydxVaO0J/fg9pZ3pqKAABZejyO8uvqcRRFjzsylbf37Lvgr\r\n0REXwGYRbLP9jYbidrEn6wUAnWtK8tNPFTPh5ISUaamNDZAeThICK5RcY4eX\r\nPa62Zgs8YDAgTAk7zhQN0nzwOkU5W3ZHOjg=\r\n=1+KK\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.13_1650119925723_0.9129749877639275"},"_hasShrinkwrap":false},"0.1.0-next.14":{"name":"alge","version":"0.1.0-next.14","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.164","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"8.12.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"47d1d6d537daeaee73ca3b797523e51cd6c969cc","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.14","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-3mPliA6jOZRqsNoU1YW48dAf/+CUChLaS++J+llO0CYUq6VesmMJ0kZFxuFT9WEFb4Z1z7Zv66giyw4Oy8mT1g==","shasum":"16a6f2f9c2ce45cac917f01db96d5d45c251925d","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.14.tgz","fileCount":130,"unpackedSize":100221,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFpRJxVR/ZR3jq2nTr9VS7fXCi8dTh9STBnLBWtSC6t8AiEArLeVT1oXq/pBVemZy/xlrMkwwdFB2q1sGIP68Hny/UM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXLYfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoLhw//UfsRUPbGJC9N5Rlzf1Xe5lPcLrl2ahc3qL+nW2YrVDcibtWm\r\n3YYNdRBZ68fEmHK9O7AGytoUB6sAPvmWeBWrNZJKEM43oZi8jpUIe42zgmtL\r\nIFiY6MXdlDjgRmqMzOYYsfAiuXgnjky2AS8oBPFmRUWnH4bB1mt9r7OoZ78g\r\ny1vvaBl1igtichwzcJ8n91BKUNFJR7I1P+3puFvdQxZLTNykzUt+UksOMneE\r\nuvvOJYej0CpTdAHXDNkMNC4iRkbXFIufOr1ohcrmH2yZEuAw2o4J+/2uoiDq\r\nEIlRItwVY8t+F7HNU+fpqs3VyE8EOpxPJD9F+2IGla0UIY/YO8xyqqzbE/RA\r\nE8FjNTX4huv7Nx1jIX2ghHz7VyBSF1cmbmyxruYiO20kkuTcE5ZvZltb4N3B\r\nbZ2eGWAu0KSRFzEQv6BS1r5UXkq0Ym5oFuzUTq440GyzUv2wZXeM+M8xXA+E\r\nX/xeHje2koUxmSSfl0HTFY0r3lonziAmpxB4JTCDhiBE+831cK5OZbS7fvS2\r\njBHe/aiJ2f2NFptEPs6asF/KujchAvnaSSkXD6cS5BeZFF1SjXR9nlORx7AU\r\nn9037qDFt2O9bhbH6vv2gFVPNh//dh89nSqVoFdcoKa0aJBt0l8IX1BbEX3G\r\n34wQJFGl9fm2EWnTDMcjgUDrhPrnU0LjtFk=\r\n=GhiQ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.14_1650243103629_0.059879546805574835"},"_hasShrinkwrap":false},"0.1.0-next.15":{"name":"alge","version":"0.1.0-next.15","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.164","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"^8.13.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the tests.\n2. Just install it and try it out.\n3. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"c009aea989711527e7a62b4ba580abf6f7f967cc","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.15","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-0AOxOAIfWv6a2qA2ziuvaAHTi78OfcNBsaSUUD12Mz8wdmrgr3Qn/Bwv2HaJIOpHPdWBnVG/75JZMfKh9FUdOA==","shasum":"dd3c8aaf5363fa445bb66fe8f3543a3d9dea9031","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.15.tgz","fileCount":130,"unpackedSize":101406,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDIphEkRRbQRK/LoZibCXbZnyq9AuaO0gV8l0fwQ7onSwIhALucxt6Q8/Ikp6UqROmLz9c9hPW/Ng236t2tQXfeQyL1"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXMLJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqdfg//d0JpginOT/MXB6FULQhkbf8Slxb4MeC3LvvUR+xW+pPjRGXl\r\nL/acAVsFrttTcSFQcjb5KpK0rjO3UQQJp/LLfOZmHV5Q1fEt6Zq2H2Z6nUos\r\nKHwn5eXqLa4oxW2zbCrWg8//WIkwfV/eEzkSc1l68H6l1RgbyjTTNR+UrFU7\r\nuhd6M5xuJm7piXnCNrbT5Si/u+fLSIuXt2Xb2fEEPTuXlUh2cUTLDvXbP3WP\r\nSizE/7psIcDRpsv2eW8Q6XM/rUARGU8rFDAARQSh7EIe7hJJGpHn2Og5YePD\r\n0jttafrdi+S+N/vWOICkUWJhMq+URs6/2x4LwwWtCbxci1kPrVK9X/3Aw6KF\r\nMKcEvRJNCWssJDTsdZtv20Mjev0vtrGlHMJwQLlE5SM7tb3V1/jPtKOo7R27\r\nAu4GdneDG7S+3+FlybySGTn1nGPCy8mG+wEgcmLAcxwNM6fRAOMyuDdAUq9g\r\na6bYg1khDvM0RQls33AAWzGqFwzOS78kVWixQ7vmMTl9aMK+VU9vsAm7jcR7\r\nkjmS+KURgrdTNvWTjcWr9fh0nE5YEQbbcobwrABTTS93AS5fCscnNUcPsRO2\r\nFQS6lAJ1nC+hQE1Vud9khA045Z+71o+R+mOreifCDs+j7FpbHb4dtokjQcjd\r\nQLrskNWrNL/DrAjanWw9K8RhCWh4TUv6yA4=\r\n=QK0h\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.15_1650246345641_0.5194784285311942"},"_hasShrinkwrap":false},"0.1.0-next.16":{"name":"alge","version":"0.1.0-next.16","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.164","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.18.0","@typescript-eslint/parser":"5.18.0","dripip":"0.10.0","eslint":"^8.13.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.14","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"501a3152ef2826597fe9aaf8cb4c25266567de53","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.16","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-DYsucRxgSxAl3MbQyyM5nqQ+Uy9iWLoXS4OEl1cvaVdqWcf0fufpcDOxqFxWph0CsxjAi7ul+QwoCzh95CpSow==","shasum":"1cc43aa0cc9f5b83e7dbca2b7ed5f3856f60c6ee","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.16.tgz","fileCount":130,"unpackedSize":107320,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMyM65gMt7GmD5BEb9OLb4EpoCaKoWIptvcR0kPDQxHQIgOVhhh/ltSaVGHuwzhhCCjjEp9oN2UxpKvQU+OWpoo0w="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXcWQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrNLw/+ILYoM4ei4AGjdMYvfoMUQnwr6kVt8Ub0OGtauOb46g2ZbnHm\r\nQy0h6tz8tQ5H274jXwzfaZ0shh8S5R02X9eSOVryP+EFYfQjvZxQsSPoOz8b\r\n+R7OgI2XI7lhzP1OIC8a/pQvGOGC0ym4D+sYdqj4itKACfDasQNEt3pDdvrZ\r\ntsj7lvlTYp1SGPJ8y+qiVFq7ljEYD7U6PxBWwMVh3V/GifSz9OBxGeF7ysjS\r\nl5YVwNnMHJ72UVgBZausIYTs7n8A6VPg9OEBzSXKuGCBMR0QmefSgdpWbtf6\r\na/pyc3qLePxrcBuuQqyvsysctD4R8W0DFrLqHwZOX43W+F4nQ1chgeOgm8L8\r\nwwREjbLbQkGapxNNJ8WSCqrMSfoFw1d+UN2KTaWvqcoedZrTIS5WqRuI9pAL\r\nOME1XYOYACUeSrNXgnMhQk5nuTwFxfiPyRoEfehkp3bnhdOMCg4IKelOO0cE\r\ng8Y9wkF+qVuHuvn/C3+uiBEIC0adDQpta5igwPJ6BNTAyyvYOD2FyvtwC471\r\nas8gvW7PJ8tp3Ohuj/VClrVt68WsTU99ZpHRupPQEjtAytfQBYsS3x9T83dr\r\nCuUOLoKvwnJdPHHxgez2/B77T0WoRFghpzWcw6JsRWN0FvfJ3DrCET6mxMPr\r\nqesgoToIQq5TWgFr0GR7WjOjq3X6yXwlaBc=\r\n=n3bk\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.16_1650312592698_0.7919291465837541"},"_hasShrinkwrap":false},"0.1.0-next.17":{"name":"alge","version":"0.1.0-next.17","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.165","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.19.0","@typescript-eslint/parser":"5.19.0","dripip":"0.10.0","eslint":"8.13.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"e7d6aa1614deea4baf82013f891573d8e60ae789","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.17","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-y/rQryvpEYocoTztOh+Ir1ofWcOczl9tJxNVd2vo21EmX1KOJXgCtq3iUfA2MmoYDe4baCAiJ8Go9ndDMPDFSA==","shasum":"7143e3f9c69ecda4ac4536a39dad81570e066f52","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.17.tgz","fileCount":130,"unpackedSize":119762,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCbBj9LejjtyARw0Y+sqgPyqNkDNf1hYi+JGLxLbjSv6AIgOwK91mTazHIhR0DGS5X2ojK5ny1sK3etrfehBxjExEk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiY+wLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpARg//cc1TbGI36Med7OkZ2fWqcHFDl0IPAkd2LWX8Qi/D1/RJuPAr\r\nZj+Sqi9r7RSpphT2XXXPsrd6LL9+vEfPU1AHB6mPgPoWlU03MnFycd4gdJFu\r\n965ON0wyAUqxOAJILsRa/xHoXsbkSLNMEMAVNHilPq0CeHtbHixsKyB2ACNz\r\ntC20TE+A8FnWPkbkCerhVvNFc3VEjkBjfBUiTIfcXloIwCjFBXFymnVYl3nn\r\nk5gItPnQWkJXwCWsWQF6+HaA+4SoGJqc+sORYKJgGEHa1GmJE3N3rpmC4dF0\r\nlZESHSj47bBA0qzwDQwfuA8NHAq2h3JdRSsvM0qR8bW7Ue7oGgbbs/+Z6DsL\r\nucignDpv0X7alwN8UvqnySFMblmGBwtrsK8wu7szmGPTUbACaLcOu3H4HI0a\r\nbEnrMcrWyjXYkb8bmhvKgHzpLhJzbzH4/1pVm0r3i4b6tA7EOaHDkaTd31C7\r\nxjMbpyPxoley9l9xp19yZj9isdmMKoH3ZycfdVAGZsBg2RSKvSvT6MgzXCSm\r\nB2PFNJXUqQOQQ3KlSXJ1jKkKo2Rt85Whu46z5h9xIbXX7jVOGMkiNP/uiSx8\r\nYThpZ6V1xcmvj5bqZ6B1w7Gox/WkXNKpYLCCDx4kSJXgYPf8KBYoTtGxvDRh\r\nYbwOKtYUTybc/zTijv8XRrA4R4VWOu/jZXc=\r\n=iz4k\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.17_1650715659286_0.09052735257525168"},"_hasShrinkwrap":false},"0.1.0-next.18":{"name":"alge","version":"0.1.0-next.18","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.171","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@typescript-eslint/eslint-plugin":"5.20.0","@typescript-eslint/parser":"5.20.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"73f728e6efd425ba440aee87c66dd964ff6c4d26","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.18","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-/XV2DJn3kUUlOy4vchqwRA+2bY1QSWxYDQrb7C7qhVGCCNquu+Z885zxHMMe5xWFmN11VoihNi99n5cEpZg+xg==","shasum":"4548e1d58729cab305e5903f5cb659cc4edfe02e","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.18.tgz","fileCount":130,"unpackedSize":125976,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrm/xBE/WYxia3SQjdh8ztvj4hlDbDJPPZif8/GIqbaQIgFVaHJ0iHh5EFk/0NeL0THAhs6y2Z8e1pKNL1UGOPd0M="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZIKtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoERA//TJ5bRoMexwmdcvEkRWToGkUvP14LJzi0JArWmV6R8Rj6aaTr\r\n1FN+lxs1yceNs50/qAp6MPu9nIT3rn0da2DyJg5QlpBxYigbZFAWQtiDKnta\r\nWR+N2SGLYhisNyySutB3qHcgQRl4wBZmAABKxJGO52AJm8WD7AK5NUQkldDE\r\ntohisd1z2BM1fxeYZPZpDitvwXJs2xss7SqE+6N3lAIMvrUuZWu10ZbO+QqE\r\nCRY7y0HcP+WrvDOFt1yg2VJqAvbEq9Kn80Q86yUXstEmPa4SRKPrNBvLi27l\r\nVMP92eD/ABB32LEbtFOSiiIQQGIS/rvuuDpfc3g8Vtx2Bzmmg66Uto4J8GI8\r\n4u8R6zX+2uLtyQW5JfT51BF5EczfvEeds66Yr8CAUYhIoKhx8DToJ5uFvIUW\r\n9LEIYab6mkMPlLO8egaQUvfB9YywuuxasSJGn8AOgHG+CI/0OjfCBHdt+wfE\r\nl7IF9IvtYyHzEPCfq+JsNR3vVcTIlGHuwj15LC2CSYZ02ZQNJRyP9DUfqpUM\r\nfEqaRo/dl0fSLPmU+RZdpSEUDGLbkl/n1feRRxVsXLaBC7VOHqYblazSine6\r\n0sypAf1CK70JUmLMx8XhqNi1Mil3NIyGxYfsDFwLT51eE2u23vl3vPsWVXfG\r\nrzxdRiWES0BUnu6DX85ceOKSHkv2V+VeGm8=\r\n=Q7Ld\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.18_1650754221379_0.7067255979195202"},"_hasShrinkwrap":false},"0.1.0-next.19":{"name":"alge","version":"0.1.0-next.19","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.171","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@types/semver":"7","@types/semver-utils":"1","@typescript-eslint/eslint-plugin":"5.20.0","@typescript-eslint/parser":"5.20.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"ff6c694c71721abf0857599e5b97f08d0af00217","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.19","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-c83uZwVJbc1m/10jUS6taq3EXz8NQHKC5eSZFiAAKan5aPalYLdXXo3GrOZDxb15+hYhL/tNxRmmG9bBDHnn6Q==","shasum":"7991d4f5447359532fc6d10d7cc5f5b56da237c2","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.19.tgz","fileCount":130,"unpackedSize":131581,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDYIwJpVsRXxbV5gS+S3sZhQBw/wNxaGfMiEtseUMhysQIhAKjKxt3fzRgAq+9cAQ0IF66nVxUtierc2v0YTGBh+G5X"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZLNHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp/Pw//elRdl/FKmYqK3IO+LXM0OFVCopov9KFB3TD4yjsQkfP23j8o\r\nMllYoiehwm/cZK4OVqKhwslCMRR/XL0EzVElK6eZ5A2eijZH7G+bMFzoDXvW\r\nUJXDoox06z5CHzM5v++OpLqkuXc7KPac04a22l0HHbzKi/v2tdPEterxY+2X\r\nTJLMaw0LnJq/VVcHrcTrrXuTjJXmCuph6z76D8Bu+mUAGyvMyTFgSo06hX10\r\nzAT31jmABZUl3/dvs799LLO7EtJakVxvh1Pr7wAUBAkzxuDJVO4ZU8jhcLHG\r\nY8kAZJdZZCTBlxxeWTzp2sBnMr6v4IK9r5mYgvhxtZZuin9tkXgVxH18I/wJ\r\nvbCvI/NCNMHuQc3lEwLPhBrsxwRiXBNpFr9GZZXnQb0ZqPBnSWLSDarZ4EM5\r\n1UTblOTVN8A6id4zfjQAueBqmGb8mHWuTSgXH6WXmQwwrfsLjE8leZCKPC+1\r\nMtEbF1IBSlCMtzpR1X+cPg03tDffO7dDDuE5kGBvQSjZDsdpaj8N7wGOEgCN\r\nGkhWnUZ2v7Cm617vg2aQXQHAMvj9t/d7LXLK7TbWteL4qNKs0qVxqKnOEf1a\r\n++iXyTROT3TJouWETro+6LgJYYfesnOcBo2dtxAxcgZ/Li514guMO4ooOklE\r\nUTzNftO8cGux/AkZUFGsHQl9rfbpRCsiJ/c=\r\n=3sQH\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.19_1650766663515_0.2958366697980934"},"_hasShrinkwrap":false},"0.1.0-next.20":{"name":"alge","version":"0.1.0-next.20","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.171","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@types/semver":"7","@types/semver-utils":"1","@typescript-eslint/eslint-plugin":"5.20.0","@typescript-eslint/parser":"5.20.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"a3b8267c8946af499c373442e5ca7ec7fb51e84b","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.20","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-hE9+Vd0E+a8tT0789lAbRHvOfQ1CAiFK/bgVDZsaTrvcEwlxY7O2YU53DMkXSqppnQGbwYHuDJ4rSUuG6Y+awA==","shasum":"48e67f9f2136c654a6fb758995441f2e960adb91","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.20.tgz","fileCount":130,"unpackedSize":120859,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBtWNYCYd/Q4DspjS1R5ueY3rRApf4MTDf3Kbebm8SVJAiEAxuG0j4rY+xqGufESOWn7TdE3SJyuqPfVczHNoi6vO4U="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZLYBACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqgSQ/+IkYH+gtMVRwPP6uFkf6rgyJrfmS6T3C89h+g6/hkvW/vNbHB\r\n4RAQB9h5iyf5JMzpu3Gl823nRAl7FMNzQsRAFjPlELaP51cWhebqT79kTiIu\r\nPid/n3pD+CKwxoWxja/prsZ+JUecqkoEQNQK7aarlXdyaWWRrOUHm0CGeb69\r\ne0lYiqkIHGqWJHTGpRChiPixUb6mSmthBT0BWgbllrQuEUZxTsQeakaDJMnD\r\noNatcApitjykKeAnr9/WY6SxNmtKm2A7CybelPMO9dPZrlnajMVk9De5NeCr\r\nIH2eKYBwNX/LbXYvDwJgltdyAiMs7k/6Rdd4CmqvNSsPbsZbDGFsOM2/+P8E\r\nkw3Ki6FkgvIjqiGL17/SPFlacF6zUoxwVVS4Yf52hkeQ55PRoD6tnBsviPLm\r\nAkzvgv1oG/IcBCckUNyEw4DbACzT5VcnuUJWwM1gZkXs59Nhw37OaqXCxHmr\r\nrAs1dX9yaTNZ1Gm5TRrVivtISuYNk5N9G1M3ErLUo/u4qX4BAmQr6iDSn+vw\r\npl2IU8PCLtaAk5PAgyxl2eQ9TUlKSfIJRxzicChCjBBQKKwFRTKNkbUpIsGJ\r\nUYyV/VnNMeTPYfPtQTNtfQ3a4RnCKWJw7bRzvBw6jiHul9Vdx1EsDEv5tJKX\r\nIUC+iK15UCyBjmZyuFsR9WRsO5iCrDYT4/I=\r\n=+v5x\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.20_1650767360884_0.9996164533929253"},"_hasShrinkwrap":false},"0.1.0-next.21":{"name":"alge","version":"0.1.0-next.21","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.171","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@types/semver":"7","@types/semver-utils":"1","@typescript-eslint/eslint-plugin":"5.20.0","@typescript-eslint/parser":"5.20.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"97b22b48038f3b4f478cc18243b89f5803c8079c","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.21","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-NbxmS0qNNpWXczDS+oLWY1Z50iQuCIM/bUSIXKEwV+axnAGK/kGvweYeOH42VUCVPmbDcJI7YG+bBHix82cHEw==","shasum":"3263f3799a2464e3fa7b028f885287b01353c08a","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.21.tgz","fileCount":130,"unpackedSize":124020,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFrQZMNWGK76GVk7e8LaL/IsT4SjQv8UXkm0GH3LNQltAiEAllLHvjPaM8u0yqXsCr92qjeW38LMSN3BkuQ3fM72MNs="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZMVqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp12w/7BsdvBOV/Iijhw5TlHemVE10JbEnkM2s32Z85ePiRlmBWb7zk\r\n/T+hh6nky+tRZ7EgtK01fOf84lApbr6c62CzBr8pB2vbUJ7NUL5Y5ZUgvKeQ\r\nuZrFegCPbsxUYaRGR0F9WW1H42N6QfrrM+cXC4429QCal1LKxtVuOlESVsqu\r\n3Jsnk4ZRuZMWGFcBHf89srW/z4I0cg8BJrs2r4hDliQMW6dRA4McawBWZNb/\r\n5gL8DeLCGZvtLpqBM68GP2zbvsMBoeBtFETHcZdmOTDeoTQreUNKngtl9bbS\r\nblaAG5TzEW7CZBSmAiDQ3dOs0uDqbFIXJEwyrESSgWje61U28KxJF1l+9hxI\r\nJkkzFrJMf48OK4OYEAIja7ZDsD+zIgc85ThivE9CczsMRJ/YYEXV2U0vzw3X\r\ncFkvMRa9jm+VRZh0c9iNQOXkYqbvqpy30pAXBvCQCkhYFOwkuBhIV7Lo4PYn\r\ngfc1njTbkL6YyhuytxNTncvysGklh1yYafHH/6xcZCh0JRUWZWGDlAstRsaH\r\nginiEKu3FfqJDb1GMTRvBLbfeGWUVCe7yFvCGqxkUamoDa9q6cPaguzi3EJS\r\nCZFlMZMA3qDGqBtwSG80rf9VtnKUituVz7MtwRH3e/3kb9wrKdWlrRTGioVT\r\njDx9gxcZc/uRW0QiWIrDTPifY3OpAf6UBbE=\r\n=8aTZ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.21_1650771306159_0.6815070501280904"},"_hasShrinkwrap":false},"0.1.0-next.22":{"name":"alge","version":"0.1.0-next.22","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.171","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@types/semver":"7.3.9","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.20.0","@typescript-eslint/parser":"5.20.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"5ea597dbe9c262746a1608ef3b27dc4a0ea103a5","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.22","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-mUdX9bi79fLmtm5Vt1GAZH42KqteNHJCDV6pArsT00TdHgdjTWv0Chw1OzhSImVRa8+ukgXxrqAZYwCa7+f+UA==","shasum":"a7c09a3ae82e970339222c6514241fe44bee9b8c","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.22.tgz","fileCount":130,"unpackedSize":124513,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRmQRJh9zXHoUoOotXfkfRVeDj0C12B6LZZDJzC9FAVQIgN8GxFyONBZxjMVO6M0+5tTUHLvXPtOVB0vS2XLWqErA="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJia1u6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpSxA/9GR/aadtkmMeXIiNkqRboKyNsXJQ+CptCsYUy5QV9rMN4Zkm2\r\n/Z5JCVdeCKL1ExVp+gQZCp/P4x75UAU4sYRThtK/xPYnKSRzRh2RMAe3SoRm\r\nztVHY8asRk2EBLsE2LIXPbhRoHcbc92B4MN3GMVKjRxpReGvDFUymfW6l16z\r\niQK6hopQMkqwHG1pPEjLuxB9JsN0ud8bVctZ4Vg4KYdvQ2rj4luRNzGQeOqC\r\nWBSsM0uVIVD9mfbiDB2kGXKKntNRQSexygB8H7hmAcEqdS9mhbNNhNP+9puL\r\neJxzTS1F299yMByzdV/9n9yaVszP5+e5RTIOZQplJ+AkFc+DN9vSShjs+MzW\r\naBdIgmKcAYp9aY4L7eBQX4acui2Pm1uNGa+l94Mg9g69YMpUyrqbQ64+bTaw\r\nQXz5MZ84LrQSe1O9fXa1QkoSxuAtdXwgzTQpIMSb2r4SxNn+RZCi0B/B02Rb\r\nIAh8f7cIraxOEkFmayXAj38vi5tH2D2eGqulVNLdw2fUrtZk+0QybxxCE9BD\r\ntvLBTE6F+0PCoVlnH4gkPP+0Not8v4KVwb2yzF64V2m55K/Vaw9ZtKpNc/rY\r\n1/gdoAvDWRYXziL+ScIW2cjRk6jEdmY9siJDmcnZUYLEPDPbHjNtuAImw9iC\r\ntFKMKbjhPwOlDvBm8DF2hwiZeSM0aUtI3GE=\r\n=GTSp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.22_1651203002077_0.5311018352694652"},"_hasShrinkwrap":false},"0.1.0-next.23":{"name":"alge","version":"0.1.0-next.23","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.3.1","zod":"^3.14.3"},"devDependencies":{"@jest/types":"27.5.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.171","@swc/helpers":"0.3.8","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.23","@types/semver":"7.3.9","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.20.0","@typescript-eslint/parser":"5.20.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"27.5.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.3","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"183f204cb3cb297c163b19a70ff86f7836255e27","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.23","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-HrXYuIpggPmOADYAaWH6kbhKkZo2+aHrR8oh3G/rT7L/z7c7QMmwH+6nAE+g1axfedCGi8oI1fbslQM8JF/mGA==","shasum":"5c1e918fee7b4e50e7dd7428898b17a8c432222c","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.23.tgz","fileCount":167,"unpackedSize":162998,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDhVZLC9SdKQ4C91JHm9C51oJAeZdHb/LZzOjLfZ/jpcAIhAIhIuJFDW2w/cilH5KBk26aXn7MroDypZoxaqsUHenUm"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibMhlACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrKeg/6A5S8DrYsUNmxIeHgLC8WdQEah/HCwzFyxnihBcNo9TfDutY1\r\nAD59cLVJj68A2oCzb4ClPODKBcutI54im44SfPtue/EFx/N/0/VeD7GisRlA\r\nE9ZOD58M6GHgklFEdwDnmcPZqSMa/VejEFeImjA5ZtQNwOAtwQJEM9Su4d5Z\r\nkKCvedc47pBwJCPw4Ngka5BcLb3ulSrf6krbOBtNLv1l9MuXZoQUCJgB6Xy+\r\n8ibGVs3ZFYg1Hf4BeoyScYa+Zm4+WUoYpMKGo+364cJXVbkU/mVMzhVwb8v3\r\n4uVkiiRg25yiI1PRxHhW7Y3hUeHQa7vEZ33a4cJbekqOkGChS3nZHRTA3OJ7\r\njiKT6SCd/+ao5WUYhY8Dq9QQ/tlroIP7xGUbW/lKqvpvPRJSAb0I406OBGcF\r\nkdFP/YkJK/xCGtVdtYb77MfDw9MlZdpCETY6hsQNUBYzLe29n477Xc2rHlw8\r\ns367GsGbIdR85cWcFahAb5H2O9AMizCOVHp0JwKbkuXRYDuXuXvYm6sJWx7w\r\npPETlBaq61yW/VQ74GUjXBeIrUg1CtZZoCB63m1jBB9syPeg78wgDqM1i1dj\r\nwHbdy7M+MoPORyHzACk+djOggx4UndItAes6BqAZL2zt5Xk1vso5UlaVnSGb\r\n6TgYeI1i4O8wfiKXTc4S4SCdw6RnpSDTnSU=\r\n=8UbG\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.23_1651296357263_0.29046722811398284"},"_hasShrinkwrap":false},"0.1.0-next.24":{"name":"alge","version":"0.1.0-next.24","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.4.0","zod":"^3.14.4"},"devDependencies":{"@jest/types":"28.0.2","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.174","@swc/helpers":"0.3.9","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.30","@types/semver":"7.3.9","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.21.0","@typescript-eslint/parser":"5.21.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.0.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.1.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.4","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"a0bb1686d83774acca09fa3a4c4d7cafaefc2c5e","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.24","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-n4KD9tC+3m22alNO/yGzFX2ihV3Yo5dv5lY43ZZWTkoJjMZic7LtMpWmKZ9J7HlVDZj6RniymdvQOw3ULhtzHQ==","shasum":"7a9ed1d026cdf90df5e95df011e788b12db97499","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.24.tgz","fileCount":167,"unpackedSize":162998,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEcD7pzbPeeOsA+mJvHkUFmV2C2aVnZW/EuzJdlQw57oAiAKhzqaT6FQNdrq2p1o2b/jT4vCGIg82OPuVDucEw2QLA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibXELACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrfqA/9Hb0gzsoHq0TGR9Z/3dhFTwx4TZQai5d0rjwP3UfgfcG8Xn8i\r\nhQJ8qW0hN2TJ1id8utgbDW7hTivuwRlWR4T2Zk9ypADmnBFp3DFKE/Z/7YG/\r\n6GuyuC/5t3+PDQH4yG5ZERpK04lPl4cbbLJgGtgBa6xlrT87oMyhFqvFS+p+\r\nS9Wbej+QKEdu8TMzf99gHHVtwpakpRzHAU0TgarEJOQLYgJjf5bTsrjmGizc\r\nAc++H2XrTcxTgH3tvMNW4BD2VgizPOAviPnrID3JNJQ6M9T8hDkVZr/uLX+l\r\nJSpfC8m1hZt+YnIVUaH6rYnoQYx8joxQP5aP3jCeJZ+sjyMKd/H4792cP9eF\r\ncvYJuGzrTiMBEYt1665nTCb/Iqcvg3PMPcqBxaqUEAlnLIBhY+oqJocdoEIx\r\n5i24nRZgzTt3nIME8SBGxY0Rr83VuqfTXsxI80Ra8KJ6G4fbjxQLkAlOXLgb\r\nyG6bdNqWv+QU78VB+BFY1GyV7OJGzshD51veL2gba+km/eLbQF1rQ6i6OefZ\r\nTL5+mqVUUfmyb/+bZ+04y2h0LKkHqhnEvX9juvhmb+auPNyLd9ab4mDnv91G\r\nON910xmDD+hnJHC5Mc961/bCOzPd0fTkfec3C+7HIls4eoGO0zBGIEYo9Rbu\r\ntFudc5mMnU6sBaCQASg34ijAiQOqYTvD/yg=\r\n=7TXW\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.24_1651339531004_0.3616399433375832"},"_hasShrinkwrap":false},"0.1.0-next.25":{"name":"alge","version":"0.1.0-next.25","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.0","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.32","tslib":"^2.4.0","zod":"^3.14.4"},"devDependencies":{"@jest/types":"28.0.2","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.174","@swc/helpers":"0.3.9","@swc/jest":"0.2.20","@tsconfig/recommended":"1.0.1","@types/jest":"27.4.1","@types/node":"17.0.30","@types/semver":"7.3.9","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.21.0","@typescript-eslint/parser":"5.21.0","dripip":"0.10.0","eslint":"8.14.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.0.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.1.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.4","ts-node":"10.7.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.4","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"b110c8633ca22f2fab772d230501585bb2b31065","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.25","_nodeVersion":"16.14.2","_npmVersion":"8.5.0","dist":{"integrity":"sha512-vqZ/DEWDI1I0Fx5mazVEuiqEmoEEyu35OSg6BCZSxBnZed+Bq99u9Y44duYsc5AG1tWw8jWSgGqYFCV3ezJqEA==","shasum":"e997a05e9d214660f435bb829f4b2ee5e69f7fe4","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.25.tgz","fileCount":178,"unpackedSize":173830,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAnfXc92F72C+Gm1s25CZ9ObyMwpzkGl8SPHzvUpOl/JAiAlsK0ixDuJVEHwFQ+cOcpz6WaO+vqOS5C9WOO4fNpngw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJibn/bACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq7NhAApH71A0NnfyQci3r/SFviLg53TvpXQ0dcsyYfUqwrwlbiQzTq\r\n382pojarcYwPwz0Q5RbnG6AIUrI6hyfJeUQuxf+kup/Vq7Lq2VrNEzyctEKk\r\nhahzq7TWVPRWCVfKhlckGNaA5DCWL4ilSO/jDz3ERIlLvM3DJrAW0gIC+EhG\r\n848KQNVl72zqc6yUD41lt4TvdiUSk+Talitd9z2ueGdNpFtaTLgPyTGcq66w\r\nFHWVN/nX+80ynXris1+l1arrnVsfuK2Z/deHNUJDEV4c0wsPZObJbM0F1kLU\r\nPr6+bbq5Bh8bWqQpo7R+FvRpgBH7jNmQMhIQ6AyZnUYEWF3ScBTbcC3apd7T\r\nGRKuKYETTNeeW87+Pwg+CEDD3WLDDODw+TfnIVeg+5sMLb0x33vtURZTdDj3\r\ntKPO+4vJT4/Uylk/aluudPtgkexlhv8pF9urQ/izZaCBYDeGuUnZKh8rf93k\r\n/DI76wjOyF5BgY6GmJ0/+rX1fKnX7q6sh8KvgvZLLWaLK7PZgt7Mvvky4IUt\r\noGg6+9e1hFIMLj/G4tO7r4/m6GM4DW41k9AkKL+m/+4N1gKzUpfn7sL9ZLR/\r\nxbYB/S0Ns4ZlMBYb25eDXp4+tHJ9a4h/yuqO4ZgjXbHQbRWlGQISrIvYX2aU\r\nZKhj8s9CTFUk8mRKVzfTd6QXZQ6IFTulrg0=\r\n=s4tx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.25_1651408859687_0.734463258340394"},"_hasShrinkwrap":false},"0.1.0-next.26":{"name":"alge","version":"0.1.0-next.26","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.1","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.34","tslib":"^2.4.0","zod":"^3.14.4"},"devDependencies":{"@jest/types":"28.1.0","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.189","@swc/helpers":"0.3.13","@swc/jest":"0.2.21","@tsconfig/recommended":"1.0.1","@types/jest":"27.5.1","@types/node":"17.0.30","@types/semver":"7.3.9","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.25.0","@typescript-eslint/parser":"5.25.0","dripip":"0.10.0","eslint":"8.16.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.0","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.1.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"27.1.5","ts-node":"10.8.0","ts-patch":"2.0.1","tsconfig-paths":"3.14.1","tsd":"0.20.0","typescript":"4.6.4","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"13ec351fe998e88a9f812997820a411200de4627","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.26","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-/+bCxrjGcVhwl/r6bBGTk3C23FM12wybSzQCkvYNSpYp7+HjrLfKjtnP73QjNYEjnREHVi+/MawiBS2odYlOpQ==","shasum":"03dc532a458e6cf4d4a0c3925b4c854ea48c88cd","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.26.tgz","fileCount":178,"unpackedSize":173831,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC08Xgpc5yAJMKSmxHILCFkQjAn+QB6/+Fl4ixWAzZilAIhAINLlLh2Dk+ZFUqbp+dfZtyEcbIfI0g+75exH+Aj1lXF"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiiXXKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoobA//Y/aYh6GLq+SE43YZo0GbzsdYuAd+iOu2JbK8aRAIwu403JkN\r\nRBK/coI0/DA/PZzsO4vH07PzF7l7gYLx2hltvmlewYWVn5euGm7MSrXM+qXR\r\nn8XALYjOAncFaLwbeQuf2AgxllqbDp8UtQZAg2XN2E7uMuj4tnmYxCFMLcxj\r\nvTcJlGrOKypNlnFgnP+5J410MpN4yRm/a6chHDNbOp4+usalGj2gV35k39U3\r\nL086HvVN04+TH2wx4eGU93MHczGd2LMoYvsPQBpMbMZIM90owUx+7MPWbQeT\r\nEzYNnPZZdIHrvBpq0g9LHP7cwY1g8PF4VrPVRAT6c8G/NA9ARBtVo3N8/UOU\r\nt7NiVwUpukf00F39fc4l5/GiqRFaktmKqBZTyl4oeI8sUCIae4FXooAUfXGz\r\nvsDXq2AOr19Lq6Y4Sr3YVT/nK7HqiYxmreC5e6LaKVNnJZXSTfQIWZBxrF9r\r\nRRwVRSUi9L/VaXPx7Ht3UoWeA2ALkUx/X5Vi9uXy9Lz8lgPU6CXXeqLG5bSb\r\nGT/LrZ0Iu4X8WhvoWsUbndSOAR0hQsOFJKEPq5dXd3uGMAkiCU0ZmzLWeL6D\r\nmhhnB19sIisTWo6DFWIDzSTfm4kf9rivnZB6n3R4TDakPXA9lN1PooqU9Ohf\r\nPfm0ASY+6o2+xZ9YBjk+8oWiue/rheYyxww=\r\n=uHZU\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.26_1653175754663_0.9305425412898036"},"_hasShrinkwrap":false},"0.1.0-next.27":{"name":"alge","version":"0.1.0-next.27","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"yarn@3.2.1","license":"MIT","exports":{".":{"require":"./dist-cjs/index.js","import":"./dist-esm/index.js"}},"main":"./dist-cjs/index.js","module":"./dist-esm/index.js","scripts":{"reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"yarn format:prettier","format:prettier":"prettier --write .","format:check":"yarn format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"yarn tsc --noEmit","dev":"yarn clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"yarn clean && yarn build:cjs && yarn build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"yarn build","prepare":"ts-patch install -s"},"dependencies":{"endent":"^2.1.0","remeda":"^0.0.35","tslib":"^2.4.0","zod":"^3.14.4"},"devDependencies":{"@jest/types":"28.1.0","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.189","@swc/helpers":"0.3.14","@swc/jest":"0.2.21","@tsconfig/recommended":"1.0.1","@types/jest":"27.5.1","@types/node":"17.0.30","@types/semver":"7.3.9","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.26.0","@typescript-eslint/parser":"5.26.0","dripip":"0.10.0","eslint":"8.16.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.0","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"1.1.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.6.2","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.2","ts-node":"10.8.0","ts-patch":"2.0.1","tsconfig-paths":"4.0.0","tsd":"0.20.0","typescript":"4.6.4","typescript-snapshots-plugin":"1.7.0","typescript-transform-paths":"3.3.1"},"types":"./dist-cjs/index.d.ts","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript.\n\nPronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae).\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Guide\n\nThis library is at alpha stage. For current examples of usage you can try the following:\n\n1. Look at the examples under [`/examples`](/examples).\n2. Just install it and try it out.\n3. Look at the tests.\n4. Read the source code.\n\nCheers! <3\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"6bca6add734951f82f975a40b246689bb8d608aa","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.27","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-dMWeNDMEIyo281YkSVuc9JPZfly372ReYn1s79Y3LI7mRDjKfRorML8Pcj5SHytwETQHdvVvRCtiU9O/i2a+9w==","shasum":"b55ef0cb9998ac7d33d196e535e68cdfe6806145","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.27.tgz","fileCount":178,"unpackedSize":173830,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBvLU2uzh3Hrsee+y7OczBi9ozlIJhIxuPrjawncEOdAIgY7MAwwPlfVdzNPe5NtdburD4WQvYHj1R4H4swdwv3as="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJijsoaACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo0Hw/+Ku2KB+qGJCCtH7L4lFfa3B+ZmexLFYTOTc+EbJR7NJlPVXSZ\r\njpXlDf3eocJWZCSW4zGcWpP7WZoNMv+uSf4xlYOvXfARJx6NTKD5MLfqZhW3\r\n0aAgPbPGIr5wDBBl81pkzmDdF+xL8AjujUDTY8epYeGJj9Kt/YPknGUDvsdo\r\npJs4v9QDQZvmM5qYrgq/LeM84O1zkBLeaKgCG/MNYrdXRLf5GVIcYGUb4K+I\r\n9CHrpKt3TNb3SnMyk4ikEfxW1YVsJ9E6L6mkMYbG956hDF5v91tSPisIsVC8\r\nSepz9Db1DCIaVUyYouDgfKbmjeqOY8jTmrZz0IQhMFjndoLx+MSlN+VA+Avs\r\npSi3uVD7PfX+5djDTYwlKSZ92/ib76Ep2gPDeN5poYgt41wFolTyWHYy23fK\r\nM++2bI/0Si7SMm3D4HAARoAAqnMhest8c/f9DehWZ0EUg1oaA18AxPzsdrPV\r\ndXNv2D0A55/rBoS3yr7jQGPDzr7Yt1y0dD21xK1hLLRO2/elltPSjChIuZiS\r\nfPQLtm7FZOa4dbSpbKHJ0+KjkfwYTdTt49BPxoo1EyLUA8Mq95KI6GS+7qXr\r\npW6maZlR2kCqkorRCZPzyax/xVyeHIh9Cl8TDcBfP5ZrQPHQWOjvHslb1CEh\r\nh007gQPaeniNp5NCJxPYFV9W/+WPhDM2S4k=\r\n=vDLR\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.27_1653525018221_0.6467980398987874"},"_hasShrinkwrap":false},"0.1.0-next.28":{"name":"alge","version":"0.1.0-next.28","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.7.1","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.220","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.31.0","@typescript-eslint/parser":"5.31.0","dripip":"0.10.0","eslint":"8.20.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\nType safe fluent API for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)).\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n- [Features](#features)\n  - [Variants](#variants)\n  - [Lone Variant](#lone-variant)\n  - [Lone Variant Composition](#lone-variant-composition)\n  - [Schema](#schema)\n    - [Properties](#properties)\n    - [Optional Properties](#optional-properties)\n    - [Property Defaults](#property-defaults)\n  - [Static Types](#static-types)\n  - [Identity](#identity)\n  - [Codecs](#codecs)\n    - [JSON](#json)\n    - [Custom](#custom)\n    - [Throw Decoding](#throw-decoding)\n    - [ADT Level](#adt-level)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/invariants/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about import data relationships. When `isExact` is `true` then `range` is undefined but other fields are guaranteed, well, except `release` and `build` which are always optional actually. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n## Features\n\n### Variants\n\nWe can define our ADT with one or more variants using the _ADT Builder_:\n\n```ts\nimport { Alge } from 'alge'\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .variant(`Square`)\n  .variant('Rectangle')\n  .variant(`Triangle`)\n  .done()\n```\n\nIn return we get back an _ADT Controller_. We can use it to create data:\n\n```ts\nconst circle = Shape.Circle.create()\n// { _tag: 'Circle' }\nconst square = Shape.Square.create()\n// { _tag: 'Square' }\n```\n\n### Lone Variant\n\nIt is possible to define a lone variant instead of a whole ADT using the root `.datum` method.\n\n```ts\nconst Circle = Alge.datum('Circle').done()\nconst Square = Alge.datum('Square').done()\n```\n\n### Lone Variant Composition\n\nYou can compose lone variants into an ADT. Doing so can be useful for code reuse and modularity or also just as an alternative style to the chaining API.\n\n```ts\nconst Shape = Alge.data('Shape').variant(Square).variant(Circle).done()\n```\n\n### Schema\n\n#### Properties\n\nWe can define what properties each variant has. We use `zod` to express our schema.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nThe defined schema is used by the _ADT Controller_ constructors to type check your code and give you autocomplete:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 10 })\n// { _tag: 'Circle', radius: 10 }\n\nconst square = Shape.Square.create({ size: 20 })\n// { _tag: 'Square', size: 20 }\n```\n\n#### Optional Properties\n\nProperties can be defined as optional via the schema and then constructors will not require them in the input.\n\n```ts\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1).optional(),\n  })\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n})\n// { _tag: 'Circle', size: 50 }\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  opacity: 0.4,\n})\n// { _tag: 'Circle', size: 50, opacity: 0.4 }\n```\n\n#### Property Defaults\n\nWe can define defaults for properties. We just return values in the `defaults` method.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1),\n  })\n  .defaults((input) => ({\n    opacity: 1,\n    ...input,\n  }))\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nNow we can create circles with opacity implicitly specified via the default\n\n```ts\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n  // ^-- default:  1\n})\n// { _tag: 'Circle', radius: 50, opacity: 1 }\n```\n\n### Identity\n\n`.is` is a variant method that is a TypeScript type guard. It checks if the given ADT value is that variant or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes any input. It is less type safe than `.is` so prefer `.is` when you can use it:\n\n```ts\nconst onlyScoped = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\n#### JSON\n\nSometimes there are other representations you want for your data. JSON is a very common one for transferring data between processes, over the network, etc. You can define your own codecs with Alge but JSON comes built in:\n\n```ts\nconst circleJson = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\nconst circle2 = Shape.Circle.From.json(circleJson)\n// { \"_tag\": \"Circle\", \"radius\": 50 }\n```\n\n#### Custom\n\nImagine you want a way to transform your Shape data to/from a custom string representation.\n\n```\nADT Variant             String Representation\n-----------             ---------------------\n\nShape Circle            (<space equal to radius>)\nShape Square            [<space equal to size>]\n```\n\nLet's define a string codec to achieve just this!\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (circleString) => {\n      const match = circlePattern.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (squareString) => {\n      const match = squarePattern.exec(squareString)\n      return match ? { size: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see below).\n2. When returning the parsed data for our variant we do _not_ need to deal with the `_tag` property.\n\nThe `string` codec that we have defined can now be used in the _ADT Controller_ under the `to` and `from` namespaces.\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'Circle', radius: 3 }\nconst circleString = Shape.Circle.to.string(circle)\n// '(   )'\nconst circle2 = Shape.Circle.From.string(circleString)\n// { _tag: 'Circle', radius: 3 }\n```\n\nDecoding could fail if the input is malformed. When that happens `null` is returned.\n\n```ts\nconst circle = Shape.Circle.From.string('(]')\n// null\n```\n\n#### Throw Decoding\n\nWhen `null` is not convenient we can use the `*orThrow` method instead:\n\n```ts\n// throws\nconst circle = Shape.Circle.From.jsonOrThrow('bad')\n```\n\n#### ADT Level\n\nWhen all variants share a codec definition then a generalized ADT level codec is automatically made available as well. Decoders return a union of the variants while encoders always return a string. Each variant decoder is run until one matches or none do. The decoder run order respects the order in which you defined your variants.\n\nExample (based on the `string` codec defined above):\n\n```ts\nconst shape1 = Shape.from.string('()')\n// type: Circle | Square | null\n// value: { _tag: 'Circle', radius: 0 }\nconst shape2 = Shape.from.string('[]')\n// type: Circle | Square | null\n// value: { _tag: 'Square', size: 0 }\nconst shape3 = Shape.from.string('!')\n// type: Circle | Square | null\n// value: null\nconst shape4 = Shape.from.stringOrThrow('!')\n// type: Circle | Square\n// value: throws\n```\n\n### Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your ADT. Alge has \"type functions\" for this which leverages TypeScript inference.\n\nFor ADTs there is `Alge.Infer`. It return an object with a property _per_ variant of the ADT _as well as_ a special property `*` which is _a union of all variants_.\n\n```ts\ntype Shape = Alge.Infer<typeof Shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['Circle'] => {\n  // TODO\n}\n```\n\nFor lone variants there is `Alge.InferDatum`.\n\n```ts\ntype Circle = Alge.InferDatum<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // TODO\n}\n```\n\n#### Namespaces\n\nWhen working with inferred ADT types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // TODO\n}\n```\n\n</br>\n</br>\n</br>\n</br>\n</br>\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"68fdaca0e434a53f22b4bc674f42d92be6b19fd8","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0-next.28","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-Z4l0BbXFmLUgxs47gLlVppHjP/HXlnvtC1TA6EI3CSzufeQiludozqfT4U+y3jY7Aw12utbh+leJbw5s87ItiA==","shasum":"3a8997451835f591405e4eb45d3835e01fa5ba6b","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0-next.28.tgz","fileCount":183,"unpackedSize":219148,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBiAyldNs8GysbutYOotQHHh0rT7KGkStY1/jZ3nLA4ZAiEA6pG++2rZ+KJPbr0JZAPAGVpXfx8jOo2uOTPUO0BXQDI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi5dKUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqIOw/8CyJlOPWLWhglAkEYtU3OwlhgNM3svcb0G/k/enKsaFRPdu+A\r\nbkjGmUdlHj2DaxzeXhV36kLxLQXng2Zj1eJOMRztVsg3EjskWf3ukfvEZQa0\r\nFpgIlo0RXdQ/8uLYqAkw7mIo1S1kXa7PLsK0kkof/ThwJLHGIs3S8ftC61bZ\r\ns4w+3smLUeogfOiCaC25VsY3JDL7vCaWZG17qF6zJf2LXb4t8MVeR0F1I2Es\r\nVE0ZvYuQQv3dZGfaszC8ZutvoKfFNwMEGF0rZrkKREO6wUEnGwfsxHVgr4Yf\r\n6ZhXbwVERnoz/dgTsyMex45HjY7plqLwgjAFvsuA3NnDi9NLAsMva+HWOE2e\r\niRD7vQg+/XsnZsnKGYhaVqHN+l2zPIBt22fROr6wKIOJ4crXbl5F/sTy9GFO\r\nze7naKk9qV7i96gu6kOX7Z5KHmKRsy6A65tLfU27BaQ+0bNNUaKJQMAku7lx\r\niPTJiiz3fyCeAv+DmxCvewtNCY8rcrHIWHMGXvQZvKoRrYWDdhMpDDFYlTPI\r\nfR498DtmZ3MC6R0LDIyKZW2HsrFhnRCrwwt+mLjByYgQDpJg1uTWoTY6xLHh\r\n0e+BhUzL7ZcaYT/SwYxrXLcBpX5jjASul8y0Zg2puEopWOrUFPd9S2dC0jfC\r\nm44rbQH9GyxocQcCF63MIIXpzy37atoqdOk=\r\n=fbCt\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0-next.28_1659228820338_0.355858738083604"},"_hasShrinkwrap":false},"0.1.0":{"name":"alge","version":"0.1.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.7.1","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.220","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.31.0","@typescript-eslint/parser":"5.31.0","dripip":"0.10.0","eslint":"8.20.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"ceece12b6a7aab999ac3ecd60990f0afa9043c5b","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.0","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-//OgoyYXCftg6lDK59Kt8ld2b7TopUP7ehisCGJO20V0bwFb1Om+WyTWSCUIKKTq6DHfuch3Y6zEi90je4ZDrQ==","shasum":"97a342fc39a6446c21bb861b1318ac40e85e3604","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.0.tgz","fileCount":183,"unpackedSize":219140,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAgvs1yeneWE4oOzxPfvNVLCMt8a8FCXJM5qyhspB6RkAiBhYU7bIUz6Ip+IwzNJUaGd0XNnl4OTPuksDyUiw2Oipw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi5dQDACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpGyQ//WjtbipxDuVFqL/5Z5YF5qshXbWIKQ13Xxe8iTZtkCOs8CG9+\r\nZk6/UsFGBGALCxT+OTEjgQhASTArui+8skq5YVrJPOwh8OvZ5QKstBpqK0Aq\r\ng+odOgmoyRjFcl2/A4idR16hUOZ5zDxLdhpJMbgbs8t4tUOQy6N5gsAJPBaU\r\n7gw68ls1wgVBqtEuoyXTOUKUsD2Jk2HMBXLG7B3o7agMb6Dx/gP9f8JwV8bH\r\nvutNBZNSdGPtb8+mZ/jd3uNT2iU8SnxxUwu1QvO91FuSEc62fUhtgyqTzI/e\r\nGIzXN1lsbEWUmLOrCZ131NWIN13Cgj7u5z0OjxxQB0hk/hbiOJ3zdIcjVxLh\r\niRiYaP/yZ5NWXCHAqwOe3Oite6KQ/8lnXhEopTReKQRRUvlgCjVolnXOyGJi\r\nd15e5e/l85Hkt5OtovRAQzJryBspJG5BuIbr/twCztTpk1JJTtCQqYS/N4MH\r\niwlldsIbKQUR24OHpwg4pNKCruVpkm0OyQQOzgWVqFEz21OxiEC/8EZddKNo\r\nue5+Rkf/QYrBZxmpQgpb5To7arPAOtoqnlSeoayRU4gVCtluYs63iGNJOMl4\r\n0CYb9dEfLpDZg6pHaeEs9tUfj2dghT5ZOqXMJg+MRJbJG4De2e8mp4XvYjkv\r\n+2KWvBPhw0eGirqD6isVxTJ6cso0djiNR+o=\r\n=E/Uz\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.0_1659229187164_0.39260527652161237"},"_hasShrinkwrap":false},"0.1.1-next.1":{"name":"alge","version":"0.1.1-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.220","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.31.0","@typescript-eslint/parser":"5.31.0","dripip":"0.10.0","eslint":"8.20.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n\n## TL;DR\n\nType safe fluent API library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. Controller\n//           |            v--------- 1. Builder\nexport const Shape = Alge.data(`Shape`)\n  .variant(`Rectangle`)\n  .schema({\n    width: Length,\n    height: Length,\n  })\n  .variant(`Circle`)\n  .schema({\n    radius: Length,\n  })\n  .variant(`Square`)\n  .schema({\n    size: Length,\n  })\n  .done()\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Datum Controller (not ADT)\n//    |             v--------- 1. Datum Builder (not ADT)\nconst Circle = Alge.datum(`Circle`).schema({ radius: Length }).done()\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Datum Controller (not ADT)\n//    |             v--------- 1. Datum Builder (not ADT)\nconst Circle = Alge.datum(`Circle`).schema({ radius: Length }).done()\nconst Square = Alge.datum(`Square`).schema({ size: Length }).done()\n//    v---------- 2. Controller (is ADT)\n//    |            v--------- 1. Builder (is ADT)\nconst Shape = Alge.data(`Shape`).variant(Circle).variant(Square).done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n- [Features](#features)\n  - [Variants](#variants)\n  - [Lone Variant](#lone-variant)\n  - [Lone Variant Composition](#lone-variant-composition)\n  - [Schema](#schema)\n    - [Properties](#properties)\n    - [Optional Properties](#optional-properties)\n    - [Property Defaults](#property-defaults)\n  - [Identity](#identity)\n  - [Codecs](#codecs)\n    - [JSON](#json)\n    - [Custom](#custom)\n    - [Throw Decoding](#throw-decoding)\n    - [ADT Level](#adt-level)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/invariants/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about import data relationships. When `isExact` is `true` then `range` is undefined but other fields are guaranteed, well, except `release` and `build` which are always optional actually. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br></br></br></br>\n\n## Features\n\n### Variants\n\nWe can define our ADT with one or more variants using the _ADT Builder_:\n\n```ts\nimport { Alge } from 'alge'\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .variant(`Square`)\n  .variant('Rectangle')\n  .variant(`Triangle`)\n  .done()\n```\n\nIn return we get back an _ADT Controller_. We can use it to create data:\n\n```ts\nconst circle = Shape.Circle.create()\n// { _tag: 'Circle' }\nconst square = Shape.Square.create()\n// { _tag: 'Square' }\n```\n\n### Lone Variant\n\nIt is possible to define a lone variant instead of a whole ADT using the root `.datum` method.\n\n```ts\nconst Circle = Alge.datum('Circle').done()\nconst Square = Alge.datum('Square').done()\n```\n\n### Lone Variant Composition\n\nYou can compose lone variants into an ADT. Doing so can be useful for code reuse and modularity or also just as an alternative style to the chaining API.\n\n```ts\nconst Shape = Alge.data('Shape').variant(Square).variant(Circle).done()\n```\n\n### Schema\n\n#### Properties\n\nWe can define what properties each variant has. We use `zod` to express our schema.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nThe defined schema is used by the _ADT Controller_ constructors to type check your code and give you autocomplete:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 10 })\n// { _tag: 'Circle', radius: 10 }\n\nconst square = Shape.Square.create({ size: 20 })\n// { _tag: 'Square', size: 20 }\n```\n\n#### Optional Properties\n\nProperties can be defined as optional via the schema and then constructors will not require them in the input.\n\n```ts\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1).optional(),\n  })\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n})\n// { _tag: 'Circle', size: 50 }\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  opacity: 0.4,\n})\n// { _tag: 'Circle', size: 50, opacity: 0.4 }\n```\n\n#### Property Defaults\n\nWe can define defaults for properties. We just return values in the `defaults` method.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1),\n  })\n  .defaults((input) => ({\n    opacity: 1,\n    ...input,\n  }))\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nNow we can create circles with opacity implicitly specified via the default\n\n```ts\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n  // ^-- default:  1\n})\n// { _tag: 'Circle', radius: 50, opacity: 1 }\n```\n\n### Identity\n\n`.is` is a variant method that is a TypeScript type guard. It checks if the given ADT value is that variant or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes any input. It is less type safe than `.is` so prefer `.is` when you can use it:\n\n```ts\nconst onlyScoped = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\n#### JSON\n\nSometimes there are other representations you want for your data. JSON is a very common one for transferring data between processes, over the network, etc. You can define your own codecs with Alge but JSON comes built in:\n\n```ts\nconst circleJson = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\nconst circle2 = Shape.Circle.From.json(circleJson)\n// { \"_tag\": \"Circle\", \"radius\": 50 }\n```\n\n#### Custom\n\nImagine you want a way to transform your Shape data to/from a custom string representation.\n\n```\nADT Variant             String Representation\n-----------             ---------------------\n\nShape Circle            (<space equal to radius>)\nShape Square            [<space equal to size>]\n```\n\nLet's define a string codec to achieve just this!\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst Shape = Alge.data('Shape')\n  .variant(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (circleString) => {\n      const match = circlePattern.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .variant(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (squareString) => {\n      const match = squarePattern.exec(squareString)\n      return match ? { size: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see below).\n2. When returning the parsed data for our variant we do _not_ need to deal with the `_tag` property.\n\nThe `string` codec that we have defined can now be used in the _ADT Controller_ under the `to` and `from` namespaces.\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'Circle', radius: 3 }\nconst circleString = Shape.Circle.to.string(circle)\n// '(   )'\nconst circle2 = Shape.Circle.From.string(circleString)\n// { _tag: 'Circle', radius: 3 }\n```\n\nDecoding could fail if the input is malformed. When that happens `null` is returned.\n\n```ts\nconst circle = Shape.Circle.From.string('(]')\n// null\n```\n\n#### Throw Decoding\n\nWhen `null` is not convenient we can use the `*orThrow` method instead:\n\n```ts\n// throws\nconst circle = Shape.Circle.From.jsonOrThrow('bad')\n```\n\n#### ADT Level\n\nWhen all variants share a codec definition then a generalized ADT level codec is automatically made available as well. Decoders return a union of the variants while encoders always return a string. Each variant decoder is run until one matches or none do. The decoder run order respects the order in which you defined your variants.\n\nExample (based on the `string` codec defined above):\n\n```ts\nconst shape1 = Shape.from.string('()')\n// type: Circle | Square | null\n// value: { _tag: 'Circle', radius: 0 }\nconst shape2 = Shape.from.string('[]')\n// type: Circle | Square | null\n// value: { _tag: 'Square', size: 0 }\nconst shape3 = Shape.from.string('!')\n// type: Circle | Square | null\n// value: null\nconst shape4 = Shape.from.stringOrThrow('!')\n// type: Circle | Square\n// value: throws\n```\n\n### Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your ADT. Alge has \"type functions\" for this which leverages TypeScript inference.\n\nFor ADTs there is `Alge.Infer`. It return an object with a property _per_ variant of the ADT _as well as_ a special property `*` which is _a union of all variants_.\n\n```ts\ntype Shape = Alge.Infer<typeof Shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['Circle'] => {\n  // TODO\n}\n```\n\nFor lone variants there is `Alge.InferDatum`.\n\n```ts\ntype Circle = Alge.InferDatum<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // TODO\n}\n```\n\n#### Namespaces\n\nWhen working with inferred ADT types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // TODO\n}\n```\n\n</br></br></br></br></br>\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"55769fa2f65040a82da204ed75d4978724f18017","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.1-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-JvwB6P4juIBu06Aq00lV9lXAuqeP0F+9MfDF77RLRuwPt1j4DXEVaB0CTsjvpvRnNwTEtMnlKR8prpBIWQ/WVg==","shasum":"5dc97748d01301834126a90ef8090f69b76afe24","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.1-next.1.tgz","fileCount":183,"unpackedSize":222233,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB4qWXeaikaBZi7YUkHIuptXi8SRX5jAe9WVjPnTYnDPAiBa76MZvw+co5GjtZzX1slWqUNWir9fDwFqAVxv6f128Q=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi5zXJACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmojdg//W9ceCR42nsYHGGoq+NUCrBgDMLhzllFguoyCD1BO/sTD1OIs\r\nRgKuxp/C0tmViZU48eff/fYlL9THaHHknUmDiKozs3Zymn08PM1tsz8P+fxf\r\ntCyiWwnuwM8hxLSvF73rEJ4wrqAQSaHvpuZAtS6+C7Gm6bPaed/7iI7UxnXP\r\nFcv7wA9txyaCpd9Ap96gTgIEJSnD5Ivtq4Ebp7Vxb/9dS9u7gkVk9QWAZUMF\r\nfZ3BItT8E4S2fFAGeCpTznqYvNtQ/mwPrxuEXt9u/1AmHcefJItPbOdeJBm3\r\njPcQbnr0BgX3JARSiC1NnNb+pKckGrMfe2x6AmDQMro8+v0Dzpsnj+wpuKu4\r\nlQZ8YQFCua1L7kfGWWXwq54G3xPhka3zVkxJVz/J65/Zptluv3GBbiv68uZu\r\ngau6bk+va6CC/L7GREWCw3JzwA1ROC2AfQai6rXBBwRYjYSEFoFTI1ip+cFm\r\nSHOrVRSuzkYzo5HqOz9kvVhnYn8NsL1FQ2MXu9ImHiZz8RXNDM3ev7+1CSdF\r\ntT2lRzZ29ceOU6q+PKRnKZ3SM7vlEHZDdGqSaufxcXBpUOUZ0fAZVJk64TSR\r\nT0iWVtv7NrdMp5vrHzfVq9gIL/ACweQNRBZzwFT9RF2cMPRrf0I/9H7pA7ly\r\nvpe+u17pfyNnXGQG7xRSP+rZ0J5roXiyvgs=\r\n=qByq\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.1-next.1_1659319752955_0.6163066861459157"},"_hasShrinkwrap":false},"0.1.1":{"name":"alge","version":"0.1.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.220","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.31.0","@typescript-eslint/parser":"5.31.0","dripip":"0.10.0","eslint":"8.20.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"55769fa2f65040a82da204ed75d4978724f18017","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.1.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-sRIQjqS5cM3isORwCKX+UnBur5AwTDm6FCOHJ6MTFWsBQjthQzNeL/Si9vK8CjWYbivAvYx+/MQ36CgmKjPmIQ==","shasum":"9317ec0d22e16fb0fabec338ada8dd14727c844f","tarball":"https://registry.npmjs.org/alge/-/alge-0.1.1.tgz","fileCount":183,"unpackedSize":222226,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCuFUaj8QZnth6kt1E3TJwkVEU+H7xhpnmCHEap93wgbQIhAI8aW6sePBS3hXYWW8S/TgFoECtx0FZqCHDYAdSjYIGD"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi5zkpACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqRyQ//WHja2j20vzIehrEXtO4dz1XRkyTVwYNL+qIt3GyTUWA1fIaJ\r\nCwSBZHZalKaKRJszAu7W9spGNDbUgV19JkiPKN2TGkmcAwWwlx6LBoqI8MWG\r\nnT8rXTm/c1KEkHfNQxNy/45moqgTgb1Vm48zpp73k4SaK8wgdqvRd+W5goTs\r\nuFWPYKrLurGB/CNZ4fR+fc/v0Gl7gFvQbW24ywDrDQiu8bgmCh9jHjRQNjkg\r\nAQCSx86JyDG6E9sDOI1HCghAeUZNXVxGG3PG6B0LMo9D79P3FeJGBuNFN/iT\r\nzxa8/8UJc/L+I/sBCF83GYJz3FdBjWSIOHxyxfGwNCUbwzqq6mf/GHu+zdAI\r\ncxBt1AukJaZm/3JqtXydvIfmhVtwQ6a0qLVOrdzSziu4GlaEBYRNSJ32+PzH\r\nlOxz2Qph1dHMNX1/fyOVrgy9z98NCN+sD7Pkx+UcCjNWoTcTFSS8eK/fYX6y\r\nLhVnYqasTmGsGu2fw8WVdnolN8sQyC0NymZc7fHDYLRQ719mLFP0jjL6PcZq\r\nVXCwG/sTKZD/MSRd+QXAO7qxRMsD5mX/cE3LMfj80+/mrfgZ4gpD7LQsQW2m\r\nu3g+L8FLbzKjCU+X0aGYVlJY92KyzQajxHjGAJubWEVkSq5i3+egWKfUWb5S\r\nWyxJMTUoIH9gE6Q8N9eXQYzHpUOUXjAhCrc=\r\n=+5wC\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.1.1_1659320617139_0.4004879279902924"},"_hasShrinkwrap":false},"0.2.0-next.1":{"name":"alge","version":"0.2.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.222","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe fluent API library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. Controller\n//           |            v--------- 1. Builder\nexport const Shape = Alge.data(`Shape`)\n  .record(`Rectangle`)\n  .schema({\n    width: Length,\n    height: Length,\n  })\n  .record(`Circle`)\n  .schema({\n    radius: Length,\n  })\n  .record(`Square`)\n  .schema({\n    size: Length,\n  })\n  .done()\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller (not ADT)\n//    |             v--------- 1. Record Builder (not ADT)\nconst Circle = Alge.record(`Circle`).schema({ radius: Length }).done()\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller (not ADT)\n//    |             v--------- 1. Record Builder (not ADT)\nconst Circle = Alge.record(`Circle`).schema({ radius: Length }).done()\nconst Square = Alge.record(`Square`).schema({ size: Length }).done()\n//    v---------- 2. Controller (is ADT)\n//    |            v--------- 1. Builder (is ADT)\nconst Shape = Alge.data(`Shape`).record(Circle).record(Square).done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n- [Features](#features)\n  - [Records](#records)\n  - [Lone Record](#lone-record)\n  - [Lone Record Composition](#lone-record-composition)\n  - [Schema](#schema)\n    - [Properties](#properties)\n    - [Optional Properties](#optional-properties)\n    - [Property Defaults](#property-defaults)\n  - [Identity](#identity)\n  - [Codecs](#codecs)\n    - [JSON](#json)\n    - [Custom](#custom)\n    - [Throw Decoding](#throw-decoding)\n    - [ADT Level](#adt-level)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n## Features\n\n### Records\n\nWe can define our ADT with one or more records using the _ADT Builder_:\n\n```ts\nimport { Alge } from 'alge'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .record(`Square`)\n  .record('Rectangle')\n  .record(`Triangle`)\n  .done()\n```\n\nIn return we get back an _ADT Controller_. We can use it to create data:\n\n```ts\nconst circle = Shape.Circle.create()\n// { _tag: 'Circle' }\nconst square = Shape.Square.create()\n// { _tag: 'Square' }\n```\n\n### Lone Record\n\nIt is possible to define a lone record instead of a whole ADT using the root `.record` method.\n\n```ts\nconst Circle = Alge.record('Circle').done()\nconst Square = Alge.record('Square').done()\n```\n\n### Lone Record Composition\n\nYou can compose lone records into an ADT. Doing so can be useful for code reuse and modularity or also just as an alternative style to the chaining API.\n\n```ts\nconst Shape = Alge.data('Shape').record(Square).record(Circle).done()\n```\n\n### Schema\n\n#### Properties\n\nWe can define what properties each record has. We use `zod` to express our schema.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nThe defined schema is used by the _ADT Controller_ constructors to type check your code and give you autocomplete:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 10 })\n// { _tag: 'Circle', radius: 10 }\n\nconst square = Shape.Square.create({ size: 20 })\n// { _tag: 'Square', size: 20 }\n```\n\n#### Optional Properties\n\nProperties can be defined as optional via the schema and then constructors will not require them in the input.\n\n```ts\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1).optional(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n})\n// { _tag: 'Circle', size: 50 }\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  opacity: 0.4,\n})\n// { _tag: 'Circle', size: 50, opacity: 0.4 }\n```\n\n#### Property Defaults\n\nWe can define defaults for properties. We just return values in the `defaults` method.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1),\n  })\n  .defaults((input) => ({\n    opacity: 1,\n    ...input,\n  }))\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nNow we can create circles with opacity implicitly specified via the default\n\n```ts\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n  // ^-- default:  1\n})\n// { _tag: 'Circle', radius: 50, opacity: 1 }\n```\n\n### Identity\n\n`.is` is a record method that is a TypeScript type guard. It checks if the given ADT value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes any input. It is less type safe than `.is` so prefer `.is` when you can use it:\n\n```ts\nconst onlyScoped = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\n#### JSON\n\nSometimes there are other representations you want for your data. JSON is a very common one for transferring data between processes, over the network, etc. You can define your own codecs with Alge but JSON comes built in:\n\n```ts\nconst circleJson = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\nconst circle2 = Shape.Circle.From.json(circleJson)\n// { \"_tag\": \"Circle\", \"radius\": 50 }\n```\n\n#### Custom\n\nImagine you want a way to transform your Shape data to/from a custom string representation.\n\n```\nADT Record             String Representation\n----------             ---------------------\n\nShape Circle           (<space equal to radius>)\nShape Square           [<space equal to size>]\n```\n\nLet's define a string codec to achieve just this!\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (circleString) => {\n      const match = circlePattern.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (squareString) => {\n      const match = squarePattern.exec(squareString)\n      return match ? { size: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\nThe `string` codec that we have defined can now be used in the _ADT Controller_ under the `to` and `from` namespaces.\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'Circle', radius: 3 }\nconst circleString = Shape.Circle.to.string(circle)\n// '(   )'\nconst circle2 = Shape.Circle.From.string(circleString)\n// { _tag: 'Circle', radius: 3 }\n```\n\nDecoding could fail if the input is malformed. When that happens `null` is returned.\n\n```ts\nconst circle = Shape.Circle.From.string('(]')\n// null\n```\n\n#### Throw Decoding\n\nWhen `null` is not convenient we can use the `*orThrow` method instead:\n\n```ts\n// throws\nconst circle = Shape.Circle.From.jsonOrThrow('bad')\n```\n\n#### ADT Level\n\nWhen all records share a codec definition then a generalized ADT level codec is automatically made available as well. Decoders return a union of the records while encoders always return a string. Each record decoder is run until one matches or none do. The decoder run order respects the order in which you defined your records.\n\nExample (based on the `string` codec defined above):\n\n```ts\nconst shape1 = Shape.from.string('()')\n// type: Circle | Square | null\n// value: { _tag: 'Circle', radius: 0 }\nconst shape2 = Shape.from.string('[]')\n// type: Circle | Square | null\n// value: { _tag: 'Square', size: 0 }\nconst shape3 = Shape.from.string('!')\n// type: Circle | Square | null\n// value: null\nconst shape4 = Shape.from.stringOrThrow('!')\n// type: Circle | Square\n// value: throws\n```\n\n### Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your ADT. Alge has \"type functions\" for this which leverages TypeScript inference.\n\nFor ADTs there is `Alge.Infer`. It return an object with a property _per_ record of the ADT _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof Shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['Circle'] => {\n  // TODO\n}\n```\n\nFor lone records there is `Alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // TODO\n}\n```\n\n#### Namespaces\n\nWhen working with inferred ADT types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // TODO\n}\n```\n\n</br></br>\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"923a13921d956d7d14785f32f7d6293058f56eb3","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-rtTsQb1xSD9vH3dEecn3dvAWz95gYxIJ+2UU78uLu0taxvuvTcYDN6JmMqQkHryZe6g/DIZp8CSxUohUze9j3g==","shasum":"c1201cbef5512628dab238f6d35452038ef4c321","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0-next.1.tgz","fileCount":192,"unpackedSize":223359,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEg+s+QhRINbDOCIZ2r5xfxIM1TmIySf12p7/CUHF8nsAiB4XYT5NQy1It94RjN1WpC7VGOgniZOaKd8ohfrDRxOKA=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6aL8ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqa3w//Re6TAf7yKSTezTQH6fr+K7PgsFX06T0AsOdXF5i6eiC0Hjc/\r\nTr+Nwxp9DRWu0LNpDp1JqQ2y1KSwpnY3EYTgenWLPlgBFx+dj7jforwnScjT\r\nXpDcAxuolPk1Nh8Lq378MKKWpunOsrr1nwD2Hd0eWvKP5eR9C1DE8DBUOUOb\r\nPnGkpRvfd9AshAg7NDrP7dMbWCjwCxZjDGkr2b78czvRLdRO1ddT7e9hg++X\r\nus/3/dSgWAYaW1YUTFn9VGKPB+Hg0F5IXE/dkXMMYhsJjjSySo+3Rxe7d01R\r\nnfymVvgSaWKCMFl3HBkV0FA4f9Jt65SQMNhX4zq1UjwpZMFhh4aypZ15bT6s\r\nJTb73Y78BWYSJ0PEbIubotcQKDmmaK3q5whEqqISpS7VagevdprCuFgDkTH4\r\n7JFuAjsOEVG7ygdg1izycsJeZt6OY7XUxxdUKyicd8E7Hl/RvEzwbf+VaaZv\r\njF7mUttaTchVViMOs2puG+5yqZ6tYINnx7Ii/CINLT57xzXyLykrRMAZOF5r\r\n/0AuSTjqNttFCNL3H5jfS37hZEoTnsGacP4GhqOsxxZNab2vlEr4wD2vPhxi\r\n2IKkzk9HSDEwg6xbW9oKm/sDOh0Wln91X5fjwDUxWJwJK7vgnVHbI9fWrV3q\r\nXwGZJKbDYzwgHVu1vhr5q/BJxi2cixrW+LY=\r\n=IIKl\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0-next.1_1659478780320_0.5920927682192014"},"_hasShrinkwrap":false},"0.2.0-next.2":{"name":"alge","version":"0.2.0-next.2","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.222","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe fluent API library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`)\n  .record(`Rectangle`)\n  .schema({\n    width: Length,\n    height: Length,\n  })\n  .record(`Circle`)\n  .schema({\n    radius: Length,\n  })\n  .record(`Square`)\n  .schema({\n    size: Length,\n  })\n  .done()\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\nconst Square = Alge.record(`Square`, { size: Length })\n//    v---------- 2. ADT Controller\n//    |            v--------- 1. ADT Builder\nconst Shape = Alge.data(`Shape`, {\n  Circle,\n  Square,\n})\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n- [Features](#features)\n  - [Records](#records)\n  - [Lone Record](#lone-record)\n  - [Lone Record Composition](#lone-record-composition)\n  - [Schema](#schema)\n    - [Properties](#properties)\n    - [Optional Properties](#optional-properties)\n    - [Property Defaults](#property-defaults)\n  - [Identity](#identity)\n  - [Codecs](#codecs)\n    - [JSON](#json)\n    - [Custom](#custom)\n    - [Throw Decoding](#throw-decoding)\n    - [ADT Level](#adt-level)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n## Features\n\n### Records\n\nWe can define our ADT with one or more records using the _ADT Builder_:\n\n```ts\nimport { Alge } from 'alge'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .record(`Square`)\n  .record('Rectangle')\n  .record(`Triangle`)\n  .done()\n```\n\nIn return we get back an _ADT Controller_. We can use it to create data:\n\n```ts\nconst circle = Shape.Circle.create()\n// { _tag: 'Circle' }\nconst square = Shape.Square.create()\n// { _tag: 'Square' }\n```\n\n### Lone Record\n\nIt is possible to define a lone record instead of a whole ADT using the root `.record` method.\n\n```ts\nconst Circle = Alge.record('Circle').done()\nconst Square = Alge.record('Square').done()\n```\n\n### Lone Record Composition\n\nYou can compose lone records into an ADT. Doing so can be useful for code reuse and modularity or also just as an alternative style to the chaining API.\n\n```ts\nconst Shape = Alge.data('Shape').record(Square).record(Circle).done()\n```\n\n### Schema\n\n#### Properties\n\nWe can define what properties each record has. We use `zod` to express our schema.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nThe defined schema is used by the _ADT Controller_ constructors to type check your code and give you autocomplete:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 10 })\n// { _tag: 'Circle', radius: 10 }\n\nconst square = Shape.Square.create({ size: 20 })\n// { _tag: 'Square', size: 20 }\n```\n\n#### Optional Properties\n\nProperties can be defined as optional via the schema and then constructors will not require them in the input.\n\n```ts\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1).optional(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n})\n// { _tag: 'Circle', size: 50 }\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  opacity: 0.4,\n})\n// { _tag: 'Circle', size: 50, opacity: 0.4 }\n```\n\n#### Property Defaults\n\nWe can define defaults for properties. We just return values in the `defaults` method.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1),\n  })\n  .defaults((input) => ({\n    opacity: 1,\n    ...input,\n  }))\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nNow we can create circles with opacity implicitly specified via the default\n\n```ts\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n  // ^-- default:  1\n})\n// { _tag: 'Circle', radius: 50, opacity: 1 }\n```\n\n### Identity\n\n`.is` is a record method that is a TypeScript type guard. It checks if the given ADT value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes any input. It is less type safe than `.is` so prefer `.is` when you can use it:\n\n```ts\nconst onlyScoped = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\n#### JSON\n\nSometimes there are other representations you want for your data. JSON is a very common one for transferring data between processes, over the network, etc. You can define your own codecs with Alge but JSON comes built in:\n\n```ts\nconst circleJson = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\nconst circle2 = Shape.Circle.From.json(circleJson)\n// { \"_tag\": \"Circle\", \"radius\": 50 }\n```\n\n#### Custom\n\nImagine you want a way to transform your Shape data to/from a custom string representation.\n\n```\nADT Record             String Representation\n----------             ---------------------\n\nShape Circle           (<space equal to radius>)\nShape Square           [<space equal to size>]\n```\n\nLet's define a string codec to achieve just this!\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (circleString) => {\n      const match = circlePattern.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (squareString) => {\n      const match = squarePattern.exec(squareString)\n      return match ? { size: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\nThe `string` codec that we have defined can now be used in the _ADT Controller_ under the `to` and `from` namespaces.\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'Circle', radius: 3 }\nconst circleString = Shape.Circle.to.string(circle)\n// '(   )'\nconst circle2 = Shape.Circle.From.string(circleString)\n// { _tag: 'Circle', radius: 3 }\n```\n\nDecoding could fail if the input is malformed. When that happens `null` is returned.\n\n```ts\nconst circle = Shape.Circle.From.string('(]')\n// null\n```\n\n#### Throw Decoding\n\nWhen `null` is not convenient we can use the `*orThrow` method instead:\n\n```ts\n// throws\nconst circle = Shape.Circle.From.jsonOrThrow('bad')\n```\n\n#### ADT Level\n\nWhen all records share a codec definition then a generalized ADT level codec is automatically made available as well. Decoders return a union of the records while encoders always return a string. Each record decoder is run until one matches or none do. The decoder run order respects the order in which you defined your records.\n\nExample (based on the `string` codec defined above):\n\n```ts\nconst shape1 = Shape.from.string('()')\n// type: Circle | Square | null\n// value: { _tag: 'Circle', radius: 0 }\nconst shape2 = Shape.from.string('[]')\n// type: Circle | Square | null\n// value: { _tag: 'Square', size: 0 }\nconst shape3 = Shape.from.string('!')\n// type: Circle | Square | null\n// value: null\nconst shape4 = Shape.from.stringOrThrow('!')\n// type: Circle | Square\n// value: throws\n```\n\n### Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your ADT. Alge has \"type functions\" for this which leverages TypeScript inference.\n\nFor ADTs there is `Alge.Infer`. It return an object with a property _per_ record of the ADT _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof Shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['Circle'] => {\n  // TODO\n}\n```\n\nFor lone records there is `Alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // TODO\n}\n```\n\n#### Namespaces\n\nWhen working with inferred ADT types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // TODO\n}\n```\n\n</br></br>\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"9f0449204837ce12ec5bb15a17e5e486abea6331","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0-next.2","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-/p4yIkw8KwvptmlGVIhcLDU8nSuFfZdlpuhbQJ1m4DsCadij/0oLfKvhCH8IaBVIXzRVi0h+q3U7ykKOnqbO/w==","shasum":"60ea8f19ad17bebabaf48a106b319881d537addb","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0-next.2.tgz","fileCount":201,"unpackedSize":227473,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBbD3n4ZNxR+GSJ9aXnYrfLCAnQV26FOv9DuiTPW+BR5AiEAor40z0P7jyTy87SgaCfvAg+MN6L+lheZlzMgQeeUdbM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6bVwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp3WQ//YFzba4PoZPzfXJugG2OkVrW1COF11NQN39hJQVemoQPL6HQt\r\nu6b28OwFKbyG3jx/mW6+VuPlCJysTTpLNXne/1bU63ZFnNT1l75XVCDM52RK\r\n7WWa9Z8Muz0v4H/v1vTzPx/dmFZg7dh1eBoYijWTBIFs1ys6CN/chwYh2m0U\r\ntjgO+Z86xWfe3HIuXn5arfpH4+KmCE34V2B5MkKXN9CwLRKDitZttgWU5hPc\r\nLj7cDIrpxj+6JOt7S2BAKfB6mJ8mFO2BiWTPuzTgFtpssZTM7cJ3IGVnTJaY\r\nEFPnd9UejqmKch4L9SxbGAWTs+xwWlRzJ/3+oVTOpYuFkplMUeTlcFsPLvRH\r\nzHKAkfLjeE6BZUprTmz5Ts1tjIMkfuk9XQ6smD0SJEK2cksYogqP/VUveLVK\r\nK8zSQSdNLA8Bl+3Zr5TEj+jtDaTEfVfwcWnzxx7dbtKBkda54kwsEolH9j20\r\nbFjS6l287GrJ+xksIm/ed8f6KWYv/hq88nBEs8Q5a6ZVx21o9vBuReEvxAzs\r\nYT7ATY5Tt6VSpHb3RhTAelxYlrHAG9imQfXaoVoP/4sSVEXxH0l7h8s1TtYb\r\n4cM49eZ61M/iBHhT1YFTMch9Uwt3HV4i/cvCAevHJLQtTQVfguujOj7Y0Wlf\r\nsbM3zkQHpvgcfGsSlm+Bp+GUVMr1Dsz/yEY=\r\n=9Q8r\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0-next.2_1659483503772_0.8627871013278532"},"_hasShrinkwrap":false},"0.2.0-next.3":{"name":"alge","version":"0.2.0-next.3","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.222","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe fluent API library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Rectangle: {\n    size: Length,\n  },\n})\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\nconst Square = Alge.record(`Square`, { size: Length })\n//    v---------- 2. ADT Controller\n//    |            v--------- 1. ADT Builder\nconst Shape = Alge.data(`Shape`, {\n  Circle,\n  Square,\n})\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n- [Features](#features)\n  - [Records](#records)\n  - [Lone Record](#lone-record)\n  - [Lone Record Composition](#lone-record-composition)\n  - [Schema](#schema)\n    - [Properties](#properties)\n    - [Optional Properties](#optional-properties)\n    - [Property Defaults](#property-defaults)\n  - [Identity](#identity)\n  - [Codecs](#codecs)\n    - [JSON](#json)\n    - [Custom](#custom)\n    - [Throw Decoding](#throw-decoding)\n    - [ADT Level](#adt-level)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n## Features\n\n### Records\n\nWe can define our ADT with one or more records using the _ADT Builder_:\n\n```ts\nimport { Alge } from 'alge'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .record(`Square`)\n  .record('Rectangle')\n  .record(`Triangle`)\n  .done()\n```\n\nIn return we get back an _ADT Controller_. We can use it to create data:\n\n```ts\nconst circle = Shape.Circle.create()\n// { _tag: 'Circle' }\nconst square = Shape.Square.create()\n// { _tag: 'Square' }\n```\n\n### Lone Record\n\nIt is possible to define a lone record instead of a whole ADT using the root `.record` method.\n\n```ts\nconst Circle = Alge.record('Circle').done()\nconst Square = Alge.record('Square').done()\n```\n\n### Lone Record Composition\n\nYou can compose lone records into an ADT. Doing so can be useful for code reuse and modularity or also just as an alternative style to the chaining API.\n\n```ts\nconst Shape = Alge.data('Shape').record(Square).record(Circle).done()\n```\n\n### Schema\n\n#### Properties\n\nWe can define what properties each record has. We use `zod` to express our schema.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nThe defined schema is used by the _ADT Controller_ constructors to type check your code and give you autocomplete:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 10 })\n// { _tag: 'Circle', radius: 10 }\n\nconst square = Shape.Square.create({ size: 20 })\n// { _tag: 'Square', size: 20 }\n```\n\n#### Optional Properties\n\nProperties can be defined as optional via the schema and then constructors will not require them in the input.\n\n```ts\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1).optional(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n})\n// { _tag: 'Circle', size: 50 }\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  opacity: 0.4,\n})\n// { _tag: 'Circle', size: 50, opacity: 0.4 }\n```\n\n#### Property Defaults\n\nWe can define defaults for properties. We just return values in the `defaults` method.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1),\n  })\n  .defaults((input) => ({\n    opacity: 1,\n    ...input,\n  }))\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nNow we can create circles with opacity implicitly specified via the default\n\n```ts\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n  // ^-- default:  1\n})\n// { _tag: 'Circle', radius: 50, opacity: 1 }\n```\n\n### Identity\n\n`.is` is a record method that is a TypeScript type guard. It checks if the given ADT value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes any input. It is less type safe than `.is` so prefer `.is` when you can use it:\n\n```ts\nconst onlyScoped = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\n#### JSON\n\nSometimes there are other representations you want for your data. JSON is a very common one for transferring data between processes, over the network, etc. You can define your own codecs with Alge but JSON comes built in:\n\n```ts\nconst circleJson = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\nconst circle2 = Shape.Circle.From.json(circleJson)\n// { \"_tag\": \"Circle\", \"radius\": 50 }\n```\n\n#### Custom\n\nImagine you want a way to transform your Shape data to/from a custom string representation.\n\n```\nADT Record             String Representation\n----------             ---------------------\n\nShape Circle           (<space equal to radius>)\nShape Square           [<space equal to size>]\n```\n\nLet's define a string codec to achieve just this!\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (circleString) => {\n      const match = circlePattern.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (squareString) => {\n      const match = squarePattern.exec(squareString)\n      return match ? { size: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\nThe `string` codec that we have defined can now be used in the _ADT Controller_ under the `to` and `from` namespaces.\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'Circle', radius: 3 }\nconst circleString = Shape.Circle.to.string(circle)\n// '(   )'\nconst circle2 = Shape.Circle.From.string(circleString)\n// { _tag: 'Circle', radius: 3 }\n```\n\nDecoding could fail if the input is malformed. When that happens `null` is returned.\n\n```ts\nconst circle = Shape.Circle.From.string('(]')\n// null\n```\n\n#### Throw Decoding\n\nWhen `null` is not convenient we can use the `*orThrow` method instead:\n\n```ts\n// throws\nconst circle = Shape.Circle.From.jsonOrThrow('bad')\n```\n\n#### ADT Level\n\nWhen all records share a codec definition then a generalized ADT level codec is automatically made available as well. Decoders return a union of the records while encoders always return a string. Each record decoder is run until one matches or none do. The decoder run order respects the order in which you defined your records.\n\nExample (based on the `string` codec defined above):\n\n```ts\nconst shape1 = Shape.from.string('()')\n// type: Circle | Square | null\n// value: { _tag: 'Circle', radius: 0 }\nconst shape2 = Shape.from.string('[]')\n// type: Circle | Square | null\n// value: { _tag: 'Square', size: 0 }\nconst shape3 = Shape.from.string('!')\n// type: Circle | Square | null\n// value: null\nconst shape4 = Shape.from.stringOrThrow('!')\n// type: Circle | Square\n// value: throws\n```\n\n### Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your ADT. Alge has \"type functions\" for this which leverages TypeScript inference.\n\nFor ADTs there is `Alge.Infer`. It return an object with a property _per_ record of the ADT _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof Shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['Circle'] => {\n  // TODO\n}\n```\n\nFor lone records there is `Alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // TODO\n}\n```\n\n#### Namespaces\n\nWhen working with inferred ADT types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // TODO\n}\n```\n\n</br></br>\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"a2fd165f32ac07350960c00b7f5876354b86bca2","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0-next.3","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-3uBAZZTIFtgPs4s/x7rjHCkHRmBlSvqdSiQ2vIH9w5w32/zIZfDTf3SD7tMR2rpJFaLFyeLP67vxiQGTd7NB7w==","shasum":"4d4627dc8717fe7a0dbe494a134a425f6d4c32fc","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0-next.3.tgz","fileCount":201,"unpackedSize":238001,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCvWeGjDYfV2+OoBxwShSLg/29ADHEGxNXRBTrwOnWjwIgMilYJOVyktm4qeAzGCkogkY8bMQ8tm6UnZIUVwYSRbA="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6ewbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoBgQ/9H6vJHrJBIQV6RwYcU5vI+sCnyhgdnV3q0oOJSzCwWigghxYL\r\n4jesWmrumNi+gV0uzegW+xQ9w90wHWsT8phSjtPf2O1VPK7EcfzqoKFK0d8v\r\nebmTWlAu9Xxf2KWnjAeOCiweOS7IhZXH3uIa1mPw1kLJRGbgviSAZnVlCxH5\r\nbtYu3Gz0jegqZrGgZdL68tbfOrKbiWhZfGheAFdTX0QIJlCMfw+XVnjiDCF3\r\nKneErlld5RM3bdr6bSjijjreSxa67EZWsYV8BhUj37oN4xVgTWMONDY4jwa3\r\nTmN4w6PfBQFQsu9CYyNhXI7EBWPX0MxYBZYeKKNo9OjC8Ic+Ra20M3cssGxM\r\ngfO0tF5rpZNbFhDY88XYtOfWhRV6ilL8VRtgwNZKL0rVE2V3pdTfjQRICIF1\r\n0SfZLIQSvU1YLg7NP/AATio2uYuh/CIGJxzGHjumgaUS64sQliAY+cLJsndf\r\nzrRSsS3nhSjvcsxq0c74ul0+cFKOYyUnighKS1MLH4or5xcwNQwh8wvtysxO\r\nCPELB85Jqq8OXDbLVrvd+Mj/NjuWVhiuLWPgj3xDOlpfcOT8dzGcRi8CDlHe\r\nLArgYhmMtmBkQA/4z4wN8gcz2AL2HXIwouw478rSiZs9375lpl3a5Zr7KxG6\r\ngZ+DoFQmme/34HsRYQkSdTUoDk0+HceoC/w=\r\n=tt9D\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0-next.3_1659497499356_0.902990505172877"},"_hasShrinkwrap":false},"0.2.0-next.4":{"name":"alge","version":"0.2.0-next.4","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.223","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe fluent API library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Rectangle: {\n    size: Length,\n  },\n})\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\nconst Square = Alge.record(`Square`, { size: Length })\n//    v---------- 2. ADT Controller\n//    |            v--------- 1. ADT Builder\nconst Shape = Alge.data(`Shape`, {\n  Circle,\n  Square,\n})\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n- [Features](#features)\n  - [Records](#records)\n  - [Lone Record](#lone-record)\n  - [Lone Record Composition](#lone-record-composition)\n  - [Schema](#schema)\n    - [Properties](#properties)\n    - [Optional Properties](#optional-properties)\n    - [Property Defaults](#property-defaults)\n  - [Identity](#identity)\n  - [Codecs](#codecs)\n    - [JSON](#json)\n    - [Custom](#custom)\n    - [Throw Decoding](#throw-decoding)\n    - [ADT Level](#adt-level)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n## Features\n\n### Records\n\nWe can define our ADT with one or more records using the _ADT Builder_:\n\n```ts\nimport { Alge } from 'alge'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .record(`Square`)\n  .record('Rectangle')\n  .record(`Triangle`)\n  .done()\n```\n\nIn return we get back an _ADT Controller_. We can use it to create data:\n\n```ts\nconst circle = Shape.Circle.create()\n// { _tag: 'Circle' }\nconst square = Shape.Square.create()\n// { _tag: 'Square' }\n```\n\n### Lone Record\n\nIt is possible to define a lone record instead of a whole ADT using the root `.record` method.\n\n```ts\nconst Circle = Alge.record('Circle').done()\nconst Square = Alge.record('Square').done()\n```\n\n### Lone Record Composition\n\nYou can compose lone records into an ADT. Doing so can be useful for code reuse and modularity or also just as an alternative style to the chaining API.\n\n```ts\nconst Shape = Alge.data('Shape').record(Square).record(Circle).done()\n```\n\n### Schema\n\n#### Properties\n\nWe can define what properties each record has. We use `zod` to express our schema.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nThe defined schema is used by the _ADT Controller_ constructors to type check your code and give you autocomplete:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 10 })\n// { _tag: 'Circle', radius: 10 }\n\nconst square = Shape.Square.create({ size: 20 })\n// { _tag: 'Square', size: 20 }\n```\n\n#### Optional Properties\n\nProperties can be defined as optional via the schema and then constructors will not require them in the input.\n\n```ts\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1).optional(),\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n})\n// { _tag: 'Circle', size: 50 }\n\nconst circle = Shape.Circle.create({\n  radius: 50,\n  opacity: 0.4,\n})\n// { _tag: 'Circle', size: 50, opacity: 0.4 }\n```\n\n#### Property Defaults\n\nWe can define defaults for properties. We just return values in the `defaults` method.\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n    opacity: z.number().min(0).max(1),\n  })\n  .defaults((input) => ({\n    opacity: 1,\n    ...input,\n  }))\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .done()\n```\n\nNow we can create circles with opacity implicitly specified via the default\n\n```ts\nconst circle = Shape.Circle.create({\n  radius: 50,\n  // opacity: ...\n  //\n  // ^-- type:     opacity?: number\n  // ^-- default:  1\n})\n// { _tag: 'Circle', radius: 50, opacity: 1 }\n```\n\n### Identity\n\n`.is` is a record method that is a TypeScript type guard. It checks if the given ADT value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes any input. It is less type safe than `.is` so prefer `.is` when you can use it:\n\n```ts\nconst onlyScoped = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\n#### JSON\n\nSometimes there are other representations you want for your data. JSON is a very common one for transferring data between processes, over the network, etc. You can define your own codecs with Alge but JSON comes built in:\n\n```ts\nconst circleJson = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\nconst circle2 = Shape.Circle.From.json(circleJson)\n// { \"_tag\": \"Circle\", \"radius\": 50 }\n```\n\n#### Custom\n\nImagine you want a way to transform your Shape data to/from a custom string representation.\n\n```\nADT Record             String Representation\n----------             ---------------------\n\nShape Circle           (<space equal to radius>)\nShape Square           [<space equal to size>]\n```\n\nLet's define a string codec to achieve just this!\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst Shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (circleString) => {\n      const match = circlePattern.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .record(`Square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('string', {\n    //   ^[1]\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (squareString) => {\n      const match = squarePattern.exec(squareString)\n      return match ? { size: match[1]!.length } : null\n      //             ^[2]\n    },\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\nThe `string` codec that we have defined can now be used in the _ADT Controller_ under the `to` and `from` namespaces.\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'Circle', radius: 3 }\nconst circleString = Shape.Circle.to.string(circle)\n// '(   )'\nconst circle2 = Shape.Circle.From.string(circleString)\n// { _tag: 'Circle', radius: 3 }\n```\n\nDecoding could fail if the input is malformed. When that happens `null` is returned.\n\n```ts\nconst circle = Shape.Circle.From.string('(]')\n// null\n```\n\n#### Throw Decoding\n\nWhen `null` is not convenient we can use the `*orThrow` method instead:\n\n```ts\n// throws\nconst circle = Shape.Circle.From.jsonOrThrow('bad')\n```\n\n#### ADT Level\n\nWhen all records share a codec definition then a generalized ADT level codec is automatically made available as well. Decoders return a union of the records while encoders always return a string. Each record decoder is run until one matches or none do. The decoder run order respects the order in which you defined your records.\n\nExample (based on the `string` codec defined above):\n\n```ts\nconst shape1 = Shape.from.string('()')\n// type: Circle | Square | null\n// value: { _tag: 'Circle', radius: 0 }\nconst shape2 = Shape.from.string('[]')\n// type: Circle | Square | null\n// value: { _tag: 'Square', size: 0 }\nconst shape3 = Shape.from.string('!')\n// type: Circle | Square | null\n// value: null\nconst shape4 = Shape.from.stringOrThrow('!')\n// type: Circle | Square\n// value: throws\n```\n\n### Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your ADT. Alge has \"type functions\" for this which leverages TypeScript inference.\n\nFor ADTs there is `Alge.Infer`. It return an object with a property _per_ record of the ADT _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof Shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['Circle'] => {\n  // TODO\n}\n```\n\nFor lone records there is `Alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // TODO\n}\n```\n\n#### Namespaces\n\nWhen working with inferred ADT types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // TODO\n}\n```\n\n</br></br>\n\n![Alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'Repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"8e416bf9ddfb8a6bb7d981a108fcd7d10f32e88c","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0-next.4","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-bYocX4huK91k1T0hzKvcC7IJreMT3h0ALgzFey1i+8dapi0rCPL1/GXngJpm9sZ2nxFB4jx+dqXrROsGqcv8RA==","shasum":"c862c4eefb3b42b839cebef689612b36da2cc1be","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0-next.4.tgz","fileCount":201,"unpackedSize":238698,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBxzb3grnShDfa83LEig5eXUniv+7QFhQ1auF2fdqJeiAiEAy7BVH90ESAPBzvvJyEnVo5F4sObECMAQwoHKpDjIrzE="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi6zn2ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq/KxAAk9wdBUukiVHIMi8AFXLcr5f6ajhId6oyL6q6WlKIr5CTt077\r\n/gg+dOncU3BexyrJN+Veaejt9SBiC2R3LxR+vo0laa/OkMVF2FipOlTMsLaM\r\nWN9l3fG4NF1/Euq1M1BfF2n6dZ1CREzgK4mIRF6QrO/dgb0db/kHGgy3TERw\r\nBwvd2a1Aap2ILcYpQltb8SZgG0V2u4Z+Mmo8amSYUbiSosX08vo6qyPZeZDY\r\nzeX0ecXqqj3tx1s9aRPPHvBxai4f9pGgutNlyNM19BXFeUNV1aaGZyDRmqvx\r\nJUBIwaUEQ8jVDfP1MhuI1lLFG8GtT9B48TSiKQe31KBuYeQtlNHEeMhx/iv5\r\nkY3icnzsB3ce+RLhhl0B45xbDeWkiyCGqbrW7WmK2E4MzpYZqVqmCDWHMbQB\r\nWNmJlIDHjCWV+4YesfuwQdweH9TaUYXmRu4wcnDRMQtuBoB1ADIPnsA+zNMO\r\npL4iA1VBEnJJdBlpsiCyvvlftLINNdpDTxzUCNDnszeHEjmO+Yncl2ybED1H\r\nah+DPuVIkd6dTkK2MKN22RatwlI8+xOeokF/A7pj/uOCvnQuIovFf9SU4RVX\r\ntzVK+o9tYorp0mXCdqIL4VH4PXUmKFmjMTJ6UhJ6oWX2MEYIJgPnyG3S7hH3\r\nM4Ul1VYFwS4hqSj4Pcv3IxrRTh97/u10RwU=\r\n=6Qa3\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0-next.4_1659582966131_0.3835958651812452"},"_hasShrinkwrap":false},"0.2.0-next.5":{"name":"alge","version":"0.2.0-next.5","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.223","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Rectangle: {\n    size: Length,\n  },\n})\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\nconst Square = Alge.record(`Square`, { size: Length })\n//    v---------- 2. ADT Controller\n//    |            v--------- 1. ADT Builder\nconst Shape = Alge.data(`Shape`, {\n  Circle,\n  Square,\n})\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"14784f6ea243966925bc91502069a6cf0ad836f9","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0-next.5","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-7q3omwZfJLQB7L/AAqLVJ7qChljOJAOp8kXid7z+cj4d7V3tT2OEPYcuh3WY9CbYz6bzIOV1O9B453O2Z8YdsA==","shasum":"c7be8c2f77c6ffbfaeb4d93c0156d591b0cb7a3d","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0-next.5.tgz","fileCount":201,"unpackedSize":244023,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHHXCNk1mrYxJeiYz9nkUzF86IhCziXT6oQ/jTRTQymrAiA5scSoEZZezWq4qbu3Fa3jP1aLOI0bPHtKATXvRzevng=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi7GfxACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpP4g/8CBPCit8Dx6+dwUKtLPNQmG/iyialwmEQnb3v5C7o4Sg3e5Mc\r\n0/cxwbKQxVQzNeNWGpxoQTVxLqNkggJm/Pa69mR61aTtZFE0WFu4FMFG5wj0\r\nOnvy0m8N0PQdbkMbZzzTXQaLI0OhRX80wnGxGfm8jV9VdGyufdDrin/GanwZ\r\n32VrJh73xLpzDfIuRlEm3L0Cf5BckiYZ9c587HIMUbbyPOiiqWggYLL5ii8+\r\nmvD4Ccf6bgsj25wjLP6PrbElKDLxDEIk+S6ujKiWWmtO/KaWJ0G5g8sZSe9Q\r\nu2jq1EgmP7bqXcnyKNPomiqZWeGG5eXn4KSfVaA3U0bt72vddrfoXU/OTunW\r\n6ToyQcMPtrAjsqIVcYztGKxnD6n9Y0xoHYpB4l1FgN+GE4BacARF+mAhZRYQ\r\nV9BeEo1jOa726iyiIEZ5BIvMe0G8Y56jKPr0eW01hNy7p1fBYznSEmysuyIm\r\nTTHQedJIo5BfYPxKB1WsLAtbuwgKV3BbquFNh3m+i0yOe6aKWU4draL4zVmG\r\nTzanCQeSafqKP/RtyQZkPaGlwOMRkSOer6UEZeS6i/dqlPaIdJ8B4JHXz05V\r\nb+3g6fFES99rBfwxMQdkKlBeo31H/2D/pAq0HBOwGKWJB9H22J3zkmp2ssG3\r\ndUqBEJBDGrVh7+I7+jgwf6gcbsEW0a5yCHU=\r\n=QNZ6\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0-next.5_1659660273699_0.10741843788819927"},"_hasShrinkwrap":false},"0.2.0-next.6":{"name":"alge","version":"0.2.0-next.6","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.223","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# Alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code. First the Builder which outputs a Controller.\n\n```ts\nimport { Alge } from '../src/index.js'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Rectangle: {\n    size: Length,\n  },\n})\n```\n\nNow the Controller:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nFinally are the instances which you can see above are created by the controller. Instances are just data. They are _not_ like class instances that couple logic and data. All logic related to an ADT, like type guards, resides in the controller.\n\nIf you don't need a full blown ADT but just one record, Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\n```\n\nIf you want to compose your ADT incrementally Alge can do that:\n\n```ts\n//    v---------- 2. Record Controller\n//    |             v--------- 1. Record Builder\nconst Circle = Alge.record(`Circle`, { radius: Length })\nconst Square = Alge.record(`Square`, { size: Length })\n//    v---------- 2. ADT Controller\n//    |            v--------- 1. ADT Builder\nconst Shape = Alge.data(`Shape`, {\n  Circle,\n  Square,\n})\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"91809c79e5fa0be84c011a2edc8f44ce30d71cb0","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0-next.6","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-vKGvWLoAshvKp69qXGBe8oHLslDeRtVeQL9sexxvNqLgos3vfQN/J1kUwrzea/+Z7dKJDzwpCqNr7OuGgOM2yg==","shasum":"1558dc780cc3af64d34fee4bd595862c7d2ca752","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0-next.6.tgz","fileCount":201,"unpackedSize":249610,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8RGaD8apgwqUzAPiMKBWbKrDLEmy1E+gu6/aYzE7N6QIhAOUPIey9/P3MI3F2s61iM3I4m/DHY0B0QPu6df9vdwxK"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi7G36ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqS3A//cLKZY1KGxoh38qcpJOA7MS6NfGPS1MTpcOhRU9F3GgjfBR/r\r\nuPzHKJeXxh/Du4KogkrwwpSyX+cfGFSqQv4D6K5N5eXuYaHCyS5Lh39JQlxs\r\nbXb7p1u4HPPHazl8yN0VwbrZ7HMlBOayAs+kyIjC8gXJd5BkuWHrKrFbLktN\r\nHpbJcOe6ifMfFarmps4ER0dFVUldvDGGjyW/Itffg8/W+F38aqsukdGXy9e2\r\nLLwffop3bf+FadshsvQGClPFk0s55hhUJQCG67+XFNPVef2MPM10zD5/fw95\r\ngxUuSPiV+Ay0cdHoRvZ7SNKehesozWnm/H3hjRgpV180QKD5Fzl0U67WR++O\r\nJ6PJitgRvZs3N/qQL1eo8JQJYDD8sGRmZ5UgqWb5uWNPo83JC5WUC1s3RBKS\r\njFTMZJTAdCHHYYTy0d3SiAheWA3EOdOHlMWiVnvMvFcKoxgaC8agPDE5grSp\r\nyaikmCsqNIS6QaweKAGE6CGYEns8zyrEsBdyD0jbC2jfIFospaoYxus3hCsO\r\nxtC92oXidddF1p4NlX0N09dFkcGMSw6oD9gatyPdG8AOJrCfHf+6Ix/rS0Rq\r\nCFl8zn4e+CCQzY0zLJv1VNBgaZ3fFu1UI8cu/ezKm8lu2EOk3Pgd6mflHnNH\r\nWUpFLoZLMC14wuk6bz1J4+h9xNu4/WdAozE=\r\n=Fzqg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0-next.6_1659661818381_0.610525487770951"},"_hasShrinkwrap":false},"0.2.0":{"name":"alge","version":"0.2.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"endent":"^2.1.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.223","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"91809c79e5fa0be84c011a2edc8f44ce30d71cb0","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.2.0","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-3A4ks8XeXluksNfzkJpVpJvMN49ItSEeYzvyUM5d1RsmCHCuBUwJP4IiVA/lER95GxwIjfX9RUrBHVFWZAl20A==","shasum":"d7b4e64fa130c7fd304a5920cec1e748610c9b3f","tarball":"https://registry.npmjs.org/alge/-/alge-0.2.0.tgz","fileCount":201,"unpackedSize":249603,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCs3yIV1rNMqipf4PHAH+Zy3fyAEu0MX3DHjn0vpEBkkQIgImArXDxFKrKX5/t4K2LhVvwL8umwMoIlLGWkNkeBGZM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi7HhfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrD2A//fRSGTaaJGbuZ1eVX+TwOHiDVQ4VtfoceQFRgYTSoY7DiwTjO\r\nZT7l9Ovv0U3Kvn2MYd4PIon29vlEEtfsdyKFCOpAgacI31ffcNNXKg0nctEs\r\n2VY0XxvkT+y9WGAfl96hJmr89rNhyW/21EICo+z0NZ/G7HFoPRgcHUonmZdj\r\n41xxwlDKZVrQVqCeZ+R8JHgqeRhPV1zUKD3coFy886m7tlVROXaoJT/2KLsU\r\ncS0B+CIiQS4ZJfiF098VWPU1d3dK8PSRB7FAgdLxZ/9sJep6KH9ydvfAzZJg\r\n9iCpYuyzQuU6wPsNUZLWR4hCgigp7nc3v2hOBUoC/5d0As6lpjjIFz5k/0bb\r\nxsHGP2Wxu5TydbqCoqaJF8hNf3gvm6Y7/VXnOgSnUCgPZhGQLJ//iYdvrAdG\r\nulHqL3GoLXYOzWGSBZe4+/2yULGIzeh8BqdN0lggX0elpwsooywnAhmqFzmX\r\nGs5Uuaz9TQ7vI0cWvffg1zn2okkJhuFJiLLtixvH5xhCduX4UTvIJRPAQHjN\r\nE9oO0VDMueYfGY6MTQ/PcmrC8PufXv/scE8XmW/SEBl4PHLZjKPlVxwuBT0J\r\nJud4y2zMEihp2Gw7xkDQibw5+eVtH0PtDd+kqNZ7IaS3koSAXzMpAllVOSy2\r\n6okZd82MJK6zhZ6kRLtlYFi8KbPbTCa2OPI=\r\n=xjLb\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.2.0_1659664479651_0.11273409231183029"},"_hasShrinkwrap":false},"0.3.0-next.1":{"name":"alge","version":"0.3.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.8.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.223","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/lodash.ismatch":"^4.4.7","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code.\n\nAt its simplest you can create one off records:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nBut you can as easily make full blown ADTs too:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nYou use your built Controller to create your data:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n```\n\nThere are other utility functions on the controller:\n\n```ts\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can use Alge for pattern matching too:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Data Matchers](#data-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Data Matchers\n\nData Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a data matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"52d97b1f3993515bc21ca3a8c9b398f3ef660c5e","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.3.0-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-qhuH4P3auVK1VNsCResOiU4XGQvZ2j/HiSEIHzVnOarFsEGP2XXJdv4Id2PmMf4NsgDOU0CKxmvgVwwdZN93Dg==","shasum":"2a60a5d6cc2ecb67202d02a31254dcc35c55ff5c","tarball":"https://registry.npmjs.org/alge/-/alge-0.3.0-next.1.tgz","fileCount":210,"unpackedSize":277990,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHtAPb5jCaMMiukEygSArpEUn0QVhN7+O7EEbDoHGHCyAiEAulmM/JHN+5XZlK4of6SerUNcEmGTgMK/AN/brVN/rtk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi7t9pACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmpvrg//YNachrQ4NDlsWVq8+KRsYxFp1yqCX9CUW9rBDLu8jbmkUISU\r\n5Ew0ngM7rgOkuMDnC4L8vbGbUpTE9bc5B6KytXfYM3Wsbh+IQEOEkx+lhcw2\r\ndDIV0WSofy5EZNsOatQ+biO/DMY+UrFSMpXHYYt73duBfvrHA4T1vaKiqlai\r\njAOmEumx7V2rB1YMApvg/w8PkllMLbbOIDjHVehuNJqcThBpbmdZw2MTtvDC\r\nSHq3MoRW0Jubs+Q7dbVYTerHUegE+96hNCCapfAn9YXbtTSmskxHbL/xtVCP\r\n30aEOB8ZEkKh6uZZh2w1tL8dAzjYLsHWSCd7gD4hImMH2UsFvZQpvkpZz8z4\r\nEKUsG+btolhRX43YW/eOC97m1+T1yJ0AHs1OEmgzZftlh+F4Xmn3Wk622D/v\r\niR62LgeKtRUsEV40DrtkZYEprDHE0wackHCYbtM2ZyWjI6nt7MYQ8CdqExwE\r\n9+xHubVzBw1vJfTzMfjQCjVZ12Q/K/OEIv8eYm3Rs9h1hSdzrH3PU5LwLDDx\r\nBkxdyNqK11ek7SZgSURZFIiaq8tJc6Seje0WJoyLiEDNtnW4kJcsfUOCC+ma\r\nTZ3/MjS9Qi4kwoFeRRmDMbENs4DaCeZUJzoXFa7zdzXPLukjuqieNWWhhnZ1\r\nQdAm+Mb4YF/2qvzDXzC0joxZf/IsoSQGOWc=\r\n=/rlU\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.3.0-next.1_1659821928819_0.4014269799388819"},"_hasShrinkwrap":false},"0.3.0":{"name":"alge","version":"0.3.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.224","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"62e990cd2cde178ea29828904bf0f92e7d608667","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.3.0","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-J8SsqcSZ0dTOV0decJuPz8nz0VTHp61ky3hF/hPEll34jzOZ37jB7N71dmmethN8yGSYz5Zl9XsVs1gh4sEmQQ==","shasum":"672b208b78376e90a28ca1a0eebf9b67264438a7","tarball":"https://registry.npmjs.org/alge/-/alge-0.3.0.tgz","fileCount":210,"unpackedSize":277982,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4unLwJlDqzqIhOotIqtHG02X9+Mt6wCYTefjL0RCiPAIgaEzIkoetr6QSheprmEPaXSMTZo9s0qNY/ElG1rgYdsI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi7uGyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpeeBAAitru4QzG7B4z1y9R9BzDL9gjjzky1kSTYyTq+Oza639kxB7J\r\n7WU0o8HAZLSoiOcYArVfD9xBWwVpbtAMd9lAY1Zv+dPd18E+7tx1/CnRb1rh\r\nhwfo0HWFCLEvHwVwgDu8HFuCPDEe/+F6072yqjUAgHzuuHDKEJRqSgI26CZJ\r\nFN7M6ZHIaKbl7W5cDpcfzauMSdHqXW/G0kiHbvvecrgEUDF/aYqGtCjV0zQh\r\nL4+7Rsdru7Bxhqsp8y99dGRgfK4Y563Tv5WCWa978RQ3H6VqcBKMUYMI8YKS\r\nVawLsT2NSP38V6FG4rnSehs0Ijrv4SmCE9hZTrz+ExBF+ZvB6hLy6bgkxvBp\r\nuHaDIQuSA/C6L3DW92mZgxG5SJdWkglgfZvmvj9tMsdpCfIWApvN1H0Sl0UB\r\nESHXk4yUv5qaYuV4P4/YuWDtUUj3pMiftjO8PdaazaKffg8rpdQGw4orIelk\r\nMa/HEsoWxGaw6oXbE7mTFx0d+vedTRwEddxAFxe6Ap712rTZAzFKHQsPUbnp\r\n74WCDGqCE9aOPFd8vc93NAkUgdgLxjJVuwRO6dP64/dWj4uTYfYA9hTlAJcM\r\n/yzRWPnHVMq8ImF8nHv+OyoUnCt1aMxTTrgKJbLkX1sdYjVhHMOIbi/Q3FU6\r\nqGMMimaNeQe4fV1w0D9kwB0z050yUXwCpd4=\r\n=2LeR\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.3.0_1659822513820_0.047418134897626585"},"_hasShrinkwrap":false},"0.4.0-next.1":{"name":"alge","version":"0.4.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.224","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code.\n\nAt its simplest you can create one off records:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nBut you can as easily make full blown ADTs too:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nYou use your built Controller to create your data:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n```\n\nThere are other utility functions on the controller:\n\n```ts\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can use Alge for pattern matching too:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Data Matchers](#data-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Data Matchers\n\nData Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a data matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"3449d71814a1eb5263bf18a720e42744d779997f","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.0-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-NIKLw/wda+E0ULCeaGs/PMDu7szlYdDOUE1SYhL1BIx/3qWJVV6Lf4AFogbOdpoiau3G0TfRameC+IxmcYomeg==","shasum":"7da728219ab5c670c496580318856641afc776e9","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.0-next.1.tgz","fileCount":210,"unpackedSize":278593,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD9I7ylw1NfGUrjcCURIwVOh1lwTWFBBU9bVqyKrJX24gIhAJLXEcVTX3dMoEtSGg3K5gL8jbzSVCw+WNNKYoMUogRN"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi75dwACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo2MA//QFFfB8BMvD0MadyEEx9dRw8fMWfXjnaeTZe0T9YRHDLmslum\r\np97vmkDHoW3MiBTst7mwC4/+eAcASN17tlZRHjeKthjC2y1x5q4RM2Os0wI4\r\nn4UcCy7qcxWmWppcMCj2Gw92/HYU8UVMoGioONKq8H74HFhEvvyyRiXb0K/e\r\nJqsH1a/jdAaZuYKiqSmmqoBq98SXnIeexJ3wHHL8E5CxzjX4VWmrPvYGADpC\r\nwBKs/TwO0JFTS+htVX8QUMMeUe4mo4JQM3kdVloAQ9H0Vi+y01V9GuBvxjad\r\n3zrprGsY5gT45c/iM8hr7OTFyVrq6XZsPkNeU3dWjNoUxbt9NbBsMIXML1ck\r\n5cogzI33FxcLEa5mR1H0BXSyrolrNfh7uz85p21lLkkhfj5hGRvetJuOqn4w\r\nC3TfomDaRlyfBb5FekgWZpubQ8TccdUCzQvSa//GnX5NxaxMCfSgO3Zpq9nb\r\nKNfwfSG9dCWyxuUwOD1+Sg1VDD9WBkUksbTNwqgWDylJbEKYqSIsHNX9KUyu\r\n/k8LBm9ui67oOZS1Pgn3mIlLbm4+MZNbDis8ciuqxSp3mymcYleKNKqxLflW\r\nRNnyd2LTSDlaRIo1xgGJYtxP1NkYwOW96vzX5iybkoe8WO1hci0ro31glGGm\r\nlj1vp1AZH8Nxz+Cb/JJdaoOFH/2QOQrWp6s=\r\n=7Dng\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.0-next.1_1659869040151_0.4918552667574583"},"_hasShrinkwrap":false},"0.4.0-next.2":{"name":"alge","version":"0.4.0-next.2","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.224","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code.\n\nAt its simplest you can create one off records:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nBut you can as easily make full blown ADTs too:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nYou use your built Controller to create your data:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n```\n\nThere are other utility functions on the controller:\n\n```ts\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can use Alge for pattern matching too:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Data Matchers](#data-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Data Matchers\n\nData Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a data matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"97853485f5a55d0dca5a2f1ee3de95dd00a7e8ce","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.0-next.2","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-Plz+jHruqhEfgYUDhCcOzmHT2JYsM2Mbqp+uMaKdBvsS460RVNrD9z11H1X3subp7x9AwiFE6uaVO+k5K0Dyow==","shasum":"fc96d32df2322d0e1665f778bd1eeff8001c441b","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.0-next.2.tgz","fileCount":210,"unpackedSize":279007,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDGUQxBejZkOxnFz9xXlhbygQu9AOBeKGEHKJSgRmBeBAiBPkC4yig8K+BWvzUqwYsni2nQ8yRgqRFE2dxr1jIY87Q=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi75sKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr/Tw/7BtEhyGxqx92WCRrmA34xQg7BsHsHB0kuC7x4ya/2b8K813Lh\r\nmccPtuUkl1k6pck6K/rKEpHch569oD+7tqjydyGeh3C0XMLw3i8gjOsQJFvC\r\nqDmdjBaCGqNZ4UPi+bHux/j4V8DXcjqEes4b0XBQuvwnvWjz+qQu0Ahi/rrM\r\ndVqIS0j/QMWmZRsycRFkRd+0BBg9dot/SHz08o3hpjvcvBhCTuKIRquRTgsF\r\ncygJyzhd+vScPm0s8CLG7QXbcZJdDeU59ejqiIJ7ykHM/L1D+6mYAQWgpsFO\r\nzjNBzZ1awTNvMkT2IpCjVGEy3dHx8GRG9pxhF7dJdT0MvOp9Rsf0Pb06Mi0e\r\nzMozeQd1xpoP3Y4QbY2DSnyZ2sR1ZLXzj9J7JnVWmRw82X257hsVHqU6pmzd\r\nd2pC9Xurmzgrh/Yqsk8VzX1ic8ZP+Yd2Zn/Gep+tEwC3G8+TL1wjMl/yi6Qe\r\nqd4cwtUbCnRaA5C9AvLMrxUikQAc24DMaSvglJ5vk4KQRJjk5We2E7zYoGo4\r\n4w4l6RJCO/jvCH//tMwMPVt9Xtm7LkX+7QhiB81rDsMkVXB94x7ex9AHhwp5\r\nYSrhLggwyVbgOguWQiFnotmb7Oz7LcUGWFMzO8/Y4Dg9h9bipz9rpoXGuWM/\r\nCboxwhB41bov3UkH4w8LIgd6fiDL/XRGnSE=\r\n=Izmy\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.0-next.2_1659869962304_0.19944316016507613"},"_hasShrinkwrap":false},"0.4.0":{"name":"alge","version":"0.4.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.224","@swc/helpers":"0.4.3","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.6","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.10","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.32.0","@typescript-eslint/parser":"5.32.0","dripip":"0.10.0","eslint":"8.21.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.7","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"97853485f5a55d0dca5a2f1ee3de95dd00a7e8ce","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.0","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-eMVrusTUAO5MLwmkv8e7KEqb1ZumyyYHn2aDN+C4XKKaGmUoa3rekiwukfi7ljz5Skq2q2cPXFMYAKU+mAlsfw==","shasum":"8c2a4abf12a65c9101aea697093cd9d1f9719e57","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.0.tgz","fileCount":210,"unpackedSize":279000,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSjErIwRMM6HS3vz3Nictd75TVUZUeofX2uPhEmfjEYQIhAK35N4I8R/ZkYV5fbNOi2AMfoMEI9bA60yZxLCOGw6Lz"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi75uCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZug//WhvlDoJu2CYXYFQvX4LrB+iZBczagDoFsMVVpkIP0PYMv5pd\r\nvH9ukrH6CAFEzVOqLv/7DrRtfvGiuMryOlee6SGARBtgUMRqaK9SG/JAFvtz\r\ncwqWnaMdYu4hU+1WvEwGcQ6jEbGIMYLoszgwiLDDnwujk53MIowDpxnPafIF\r\nY274q8PbHk/u+ohCC1fFmjk8BizZhnnFhRAcu9P+tAjGbIbR0xvwmKeSBKyY\r\nSWJg51REW3y2FIWgne8c22WSZ6NErY3+fovc5s8NeIRPI6yAavmeqyNH26Bh\r\nJi3fEwnqerf7MiWyMCdRiZ64Om4XiAuWSnt2Chnmc33K63ZBVnl+lCXpVLnW\r\nokMf8Rj7RYKriP9D74Q9jl9lOB9qNQYvnEYsChTnr1Y83iByhspDzKJSQHDp\r\ndkrR93/nvU6eImiImt6wSyqU1rBqcbrGviR1QNH2qQnL5ROpwwhqB1/lobjj\r\nU/pGlqugyB5TL5EH24gMdgNmUnpV/4mGvozb+bxH03BUeZ/M5I1UGJITemGd\r\nT+RDqBbfcUE8wBMsP+heyy7sUaR0XsIll4u+opmfsxddd9ZifaTHHYp3tYbC\r\nH9gHLqeyLvRq+Xe3qIgWhrNr2t4PskvF02FDv89B9LoRjZjQZn9tmIGxsEDe\r\nIOzY601djPNcIcA8BqU86XCs7POO9PMhpas=\r\n=Hx1o\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.0_1659870081837_0.3991712050950247"},"_hasShrinkwrap":false},"0.4.1-next.1":{"name":"alge","version":"0.4.1-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.3","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.241","@swc/helpers":"0.4.7","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.7","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.33.1","@typescript-eslint/parser":"5.33.1","dripip":"0.10.0","eslint":"8.22.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code.\n\nAt its simplest you can create one off records:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nBut you can as easily make full blown ADTs too:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nYou use your built Controller to create your data:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n```\n\nThere are other utility functions on the controller:\n\n```ts\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can use Alge for pattern matching too:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Data Matchers](#data-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Data Matchers\n\nData Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a data matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"bf544515612f400e72f568dffbe99e5a60144289","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.1-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-a6SZypazOBahGiaNfCwSMY19HMavcD9wzLWLji/5qbEdDkOSpxiFkaBgYDLiw7nB4Uc8feUnKZXY7Uuf0y4iJA==","shasum":"0ef7ac33f4b08e9f66b9e16abd54525a1511d354","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.1-next.1.tgz","fileCount":210,"unpackedSize":279007,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAU1esHOydARrd1uPSr5UbRYHaLO5YrlIXuEorrdzqpAAiEAxfxFPS3rvKKWCFplGlrDLSzIp0NDoTXL+Wh63ZeAmG8="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjAVorACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqIzA//eIiIP2XV+FUx7zOk+1GvETNaj1SpxvOdXGWKvg/I2KpBz6gs\r\nspRk7CmCck24ITzMlXT0iLd0Mus8xS/g6O4NhAJ3m69zc54qhqNSPU3g22Lh\r\nPIcFfsIJFlu8CgvGJv1ToS3Gw0X5Dj8ALiIgssrWz2f5rK4+788Y5uAfOSrb\r\nBlYgvhIVaG7UHoqFmbAGqM+QP6BsY32uKoWzvcRLRb9J1M3IOb47aA/HiN30\r\n6AVnDXjeihevzFDOyl+oliWHGvYMYHn4an+aAAuBOwv9eW/VXt0C8yvU2Vu2\r\nF1X8wY00tRtw54/kR5/5Hrx5RxzNSpRYvp6Vc1HhnJeqwe6f5eAOnDXCT9wV\r\nq2vQ1xjMRGE6+1GcCtyGSPFkVjEF/rsH3c93mLrP4YdkKYMBC0plhpakip63\r\nasScaUZ0K2jC6ofYQ3dVX+O5POc7nJRTxhS8QI95ziLKXgR2EKJUa1ubCBF3\r\npRz1/y4B2hsZM35kxRUuLY2hR4e4+HTcxpzGWEYnMVrwQDGtz9h29hPwYMzC\r\njBEvkaq3ZexxZSuJJerrkoTRPhjgFxta25SPtiIGvNmICOhxbzc9m2MI6aeh\r\nRKojhzwcTvH+1syF1k9Brya70kQhEcIqDqRcz0h5whMZLjc8fRKJDtL95obk\r\nIabj/bN3o4DsmSiP4/0J/5+zqcDeH0FGwMM=\r\n=PpF7\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.1-next.1_1661033003435_0.43043465767454947"},"_hasShrinkwrap":false},"0.4.1":{"name":"alge","version":"0.4.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.3","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.241","@swc/helpers":"0.4.7","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.7","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.33.1","@typescript-eslint/parser":"5.33.1","dripip":"0.10.0","eslint":"8.22.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"bf544515612f400e72f568dffbe99e5a60144289","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-NDd3cMg1bo1jdVz1jeAA5UyBUCnOSl3/wVMv08/bDyZ4mW10XRivMUu2GA1AAiKz8UJqvXhRsog/YCEBER3oIA==","shasum":"76ca83e4cba3b9a69b8b3a5a3d9ec1a6a9dcf95e","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.1.tgz","fileCount":210,"unpackedSize":279000,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQ0HSbBo54fE1boqO7O+QMC2EF/mE1JMDY1/Tn1Qgf5QIhAKBGKladaOq9v/EqTBtW1bBmeNs1/C3rWTv1MGMf20Ex"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjAV1DACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr1yRAAnFffOA8l57StcdIY/Ql3wTHptHm/24V0II2ipApf+nzLJOZE\r\nRGscYXcXQRLQjtBuDbJtWM/HB3oNaPY/yOJrUHApMvk2vER7cB6Vg90+iUdF\r\nddIAOanDugQP2683UQvHpjnwANEAq3mwz/m3sDugrtxPoT0u5GXj3tVx8PHz\r\nywKWfHdhffecvl2sb4JbzaHhAmG42gFW97ohU7ir8RZSJO7clV6gH3Ls2Am1\r\nFKCtL2ZijQ1uDlS5Lat3/5gEz4JYTsxBVWvEeGAjP/x7N0qQ76QpSXXcwET/\r\nGCbGH2Fb8sowWf7usL8AbQRp7TEzav57SEJ8UwJfzjB1X6C9TP/j5TMSWjpq\r\nzIOMKeLKj7ToZw97YGkYWU1hv6badRHG03BvhLrP+qgXpBURb6qihRpdwY5t\r\nhK3GkFPrE+Bn6V2vrR6yN8RFxmH6ilk1ZBYlU9m0BHThZYVD84fhWDvHTCCz\r\nZY2uZGJpv8oTtEGppDhMN019ZlLbglclu03CNmNkag8dw/VQgEtRlS+qKdE6\r\nEYOsbAtixhcIGE/9H2b+gM/vUVLqtS5fNPI3mlkrJdhAECi1JWwpfQG1qHBv\r\nAuuqORfUQl4jojpqYvMImA93xGPyCp18zWAeANoZ3FVHF7LsPqGMMG125Nab\r\nM6Q2Iofdsc2NX2iYNOevdz4HmP15VegNGVI=\r\n=urkT\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.1_1661033795351_0.6943369253107781"},"_hasShrinkwrap":false},"0.4.2-next.1":{"name":"alge","version":"0.4.2-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.4","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.241","@swc/helpers":"0.4.7","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.7","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.33.1","@typescript-eslint/parser":"5.33.1","dripip":"0.10.0","eslint":"8.22.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code.\n\nAt its simplest you can create one off records:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nBut you can as easily make full blown ADTs too:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nYou use your built Controller to create your data:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n```\n\nThere are other utility functions on the controller:\n\n```ts\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can use Alge for pattern matching too:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Data Matchers](#data-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Data Matchers\n\nData Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a data matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"550efeffc4e92a8c63432c2f061f7a7d8df06cfa","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.2-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-FFnoNmg8Q0IzQ/LobMaps10jacZSE/cNw8qRzkg9dA1AP0TlkjGNZ9oP6V+jIDEtnxsJvziJopK/RJFLD96DEg==","shasum":"e3a2bf4855f01cde3320a03d0d16385fe452deeb","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.2-next.1.tgz","fileCount":210,"unpackedSize":278942,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbuJsvOmv1S1ojscDhZLSpkQQgRrfDrHi2YZodW6OnEQIhAMsxrk45ElQIUMzI+kT3uh8cix5YQ1VJnn2MIKWhT8ac"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBZaEACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCEA//SdaAj+7T6jEn4MLU/Khhpp+T5bE6WoQB/NTm7BtMae8Cvbou\r\n6a9O9yJxvfBkylcxIpNccCWi2/tEhZyMznOgbTbOCFfu1RolDNoDJsjN1E4i\r\nAqvUicv/PkgVEYCiCr9/n9rD8n1QZH4xFoWCuq2IujKFxzzmTEPR35p49Fu0\r\n5pvO7F6tRfg9PTp75Qh/IQsn/U28xxeUmXagZEo30CAEzdAt10e7RoWeQult\r\noNy141L+uC0d0zG9LNnWy59pbd8tkI/uNCCj5tDJS6HhYNaakcN0dBERydkB\r\nfj7UAF5syVTUEd97DGkZat5nfYnKZTWkKsKqXSQuU1iM5yEFLb6niAAmUiYh\r\nduwqv/GgtR8j4fbBwruhoXn2PMH7j4RrFj8tUvTeWesg9ZbhtHhv5XYrCcOc\r\nWL75Puq/H2dPG95w/rXDGb+OdbnHT3OPSKmsuxUtvOlsyXPfuijB3RLm0cXZ\r\nRJrJxFkWDcEnaOtNGGdXqIJojGT9bpnllqfhXj6FZbyGPcnPcYEX9AfM2VNT\r\nCKWCMWVkqphwkAKg5Mm9Djxx5sv3jAixF2pweCN4EtjCCjAtq8BNtIVhp8xW\r\no6ZxWUSpqgQ/M9xiRNI1t5q9w/5XE4CYKnJZUxrC/CqakqNpKGLoK7ZqAZmm\r\ndXLII3xxpKGPj6ZMlw+6cxRO8JXewaZ+D8w=\r\n=Y3OS\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.2-next.1_1661310596106_0.24764999049076053"},"_hasShrinkwrap":false},"0.4.2-next.2":{"name":"alge","version":"0.4.2-next.2","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.4","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.241","@swc/helpers":"0.4.7","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.7","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.33.1","@typescript-eslint/parser":"5.33.1","dripip":"0.10.0","eslint":"8.22.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nType safe library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nThere are three distinct conceptual levels in Alge. Firstly there is a builder for defining your ADT in the first place. Secondly there is a controller for working with your defined ADT such as construction, type guards, and codecs. Finally there are the actual pure-data instances of your ADT.\n\n![Alge Anatomy](docs/assets/Alge%20Anatomy.png)\n\nHere's how it looks like in code.\n\nAt its simplest you can create one off records:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nBut you can as easily make full blown ADTs too:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           v---------- 2. ADT Controller\n//           |            v--------- 1. ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nYou use your built Controller to create your data:\n\n```ts\n//    v--------- 3. Instance\n//    |        v--------- 2. ADT Controller\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n```\n\nThere are other utility functions on the controller:\n\n```ts\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can use Alge for pattern matching too:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Data Matchers](#data-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Data Matchers\n\nData Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a data matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"68ccb52d1d3a08fcdf6f5e895125606b14fc53f1","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.2-next.2","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-R+suSHERMI+rn9AD0VSQu0BAhhauhBsYJOHhlhpxzX47lbDorFFKhe+OKSYZ/NWV1LEda9flHigudIUMHjMzGg==","shasum":"6bc1a58647842416df7591b787584e583b3c2312","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.2-next.2.tgz","fileCount":210,"unpackedSize":278942,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDg0eRKyUa3XgvU+Psc5lI9x9WIqVA+IOcrEs/lj4Et7AiBapUt5Y8rf/PJhFR4OqogBgzrDoVu61n0D8ouLdwRNkg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBZbLACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq4SA/9G75u6F1BeTcV2/E25A6xCpL7Bz9vv0jF4VnUnGKKKe7+YHmG\r\n/oIbexqyube/1KAXNbszJD8lGrbm+ULHDBhzullrhGgLAU/ysLzPlvSBKoFX\r\n+dxVVmMnk/iZLAqfPDbdTUtABXQDQFOS19Iu3o6u9ZBAIhNCfEVF90bZi8A/\r\nQvYdhlkK6q/Znz1lnEOedx70WygC/I9U7F0f2tJTsgAH5Phj6yF7suUuTNrv\r\n0vilILVlp5YOOLU4L9LTSqaTs5NPu9moOfJ1liMWik9JYDcxANK/Bybiq/MQ\r\nTfq2koZPP2hs+0iAqEb4szcr/Jj6jjECRgyIiUL/B15wkeKLN0BozDpt3yth\r\n4D5pHlCgDR2+fAST2voS0sZXdkV1zJy0FM5aibIpFFsQbosVOR63LGBVq+LO\r\nreT32ZMwxd45tyeWhEyYLuhUzlHp4OxJ+LaBIL5KDWbnNfFnfYvkVJhz5+fK\r\nw/yPD7fNhjFcFZ07LVdaSNBeXZDpo/eSW6sX0fOs55NCwmbQl8Vsn+4HmyXU\r\nOIM2QRC1y1UvQHxgksLXJrbeAia/k9cbU19iXyE9tv2fIORIpUT6rlWtBKvX\r\nThWhCZMCKvv9R4MtFSYi3mp8fwK7HK4o4bNBGLlLxP4nINtKiHjfSh7igxlY\r\nReUTL2S7oKyW6Z0aEbGvC8UgrAsUXGQ0XTs=\r\n=mey/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.2-next.2_1661310667260_0.587027026116959"},"_hasShrinkwrap":false},"0.4.2":{"name":"alge","version":"0.4.2","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.4","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","reflect:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"28.1.3","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.242","@swc/helpers":"0.4.9","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"28.1.7","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.34.0","@typescript-eslint/parser":"5.34.0","dripip":"0.10.0","eslint":"8.22.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"28.1.3","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.0.0","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.22.0","typescript":"4.7.4"},"gitHead":"c6f28629b4b4a4c6e9b9bfc12eda58f7840d411f","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.2","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-ngfW6bAvOT5SPBOACp/Tvbv3D1k+EmLvqfM8jNk47Rjuv8oNDT0psqF+1JJmjPMStVsjNLj1QcL0+Dy21jkrVw==","shasum":"2b3613ed543b906844dc0d22b4417dbe4f3b54aa","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.2.tgz","fileCount":210,"unpackedSize":278935,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAmf4JzUJ9TP4CvM9gyBS+LY/lO8Ux5ec3olYFgQsbahAiAZe7bBwR288S4Pghzwmx0MhW62N9LaKNpgjbCSuZs9NQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjBZcyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqxRw//fp8QM0dUy+8gQ8F5KpLsevm0eQHBKBc5AO2CqQLAb7dvkgcv\r\n9ZaB0OwVvGJXZn01aI4Zm/lmDDwYDlB2TVKBk80JXNrp3CrMmQ+IP3rgj20V\r\n23xwYD4L0G0oe8SV+gUduXdE5CfiATI8k03bCAGgY9tuP5Q8IJRt/vSXPAHZ\r\nyxwTo6dZg1/h05lSzl8p+FPjg2fbKvUxKn3MnLhkQWGOyW/dXWnoqgzMdf+z\r\nJrWOF3jM1fZt+03F55EjdyXmh5ttvF+5Jj/qt5qM7DjZp4po5KgCau2kBJQH\r\npmpFIJZVo9d1WUgStZXJpYABPcjJpmzWeUgfKxDjRIct9sIcxawaameTzBss\r\nAfByAEshslklv5l0GujBzXTcOhPVcql3wl5jCEOEkFHaH+WMKrB6jtMwZrLr\r\n41aKJSBhpXo0H3/y+JM5ZMgzPBwQnKVBbfZ4BKPueJhBgcIRbX6XUsPNwFiC\r\nsizFMvE1aC0f1nbU0XhQ5GDTNuFOx3lMPVhrFhNeXq8pZsP6H7ku9muhuRev\r\nOAabgSOQflOMB7Qp+Lz+FF/Mdcgnvwe8MknFQFr9jYcxJxuut7kmCwSYZpNo\r\nQHJBZotEr5XSqsLe+cQKKUvkNLS2nqNA0hfH831M+4hy8gK4h4YkfxQCrXiy\r\nwbxKpQcNkD/L+j3U5BliZNyNvApKwXvTFS4=\r\n=VLZp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.2_1661310770790_0.09686446484864697"},"_hasShrinkwrap":false},"0.4.3-next.1":{"name":"alge","version":"0.4.3-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.5","license":"MIT","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"29.0.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.244","@swc/helpers":"0.4.11","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"29.0.0","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.0","@typescript-eslint/parser":"5.36.0","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"29.0.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.1.1","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"257a94676c5f94805da66cdb40b97ccf9502f198","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.3-next.1","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-FNw1001ntWaSWy9Fip6QrLGks8icZVytTutUZ34PAoXQeBy22KaVCZ0rCnHAqEwOKy1fKSFP1H2m2SfOduV6Nw==","shasum":"a589b95e05c8d59db420ac6fc81b2ecb4a2f0a1e","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.3-next.1.tgz","fileCount":210,"unpackedSize":289937,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE6TSWzcq+7LDKnW7HpB8IJWj30FuvEk/+q0O3o2X+V/AiAHtfGbhxMYOfU/Bn5q1YseJLEvdsz2REgX0zLtfCDUKw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjDXN5ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqWJA/+MkDJNvNgvjvX9fby3gdaoVk/mPmRgS5h8idBckFQ8MVlOftO\r\nAT47mduzANLpVLHz+BX3hKtcmY/uXdQZjiFFuSK7OaflgIXHjW8/yWT8KgGQ\r\n2bBQYJdZSLBsTIRRqVvvH0BQ9aaeME/otI6cdbjyJ8/KO+YcjZ65YVnOOR1E\r\nifva56hjwZria0ME3IwuSPk94y3A9NHA/mo1gmNlQscalosZqCYUqubSLXxN\r\nQi41rvmKJaWUppuwRf2NCqccGxSVPn7rhzatKAfZnISn2TqB6GL3vtrYwrZ7\r\nBS+5QNh2sfROZGQibt1EyLBP4mdujJVwbMGoNOii1KRrbExm5OcwBAOA5lKZ\r\nxBAF7LotbTWFTlfsfCz4flLN0QvR7mmLkPYIliaHJkzpCltMm7suMZOo2Z4O\r\n65XIEuOiNR1DjroV0bsfmO2qOEr2O2IU+64xXZmkyihBnEabNuPrHVbOumOo\r\n3AVY5t/HuFG9qvH8g/qRLTm7po+df7SDrLMnyadzZ8lUIvV+RAJ0qpqnZAOV\r\nkcmmyoHjzNjNLQWvZC9rtYGVcQA1NMORIH0d8gn3XfhuBg74XoYmywlOBsJs\r\npDIazkXdNFmDbMKa3Wo1lrHzMTMJ5AucoZYaNqY352HwJlXTWI+0BtzPHJX2\r\nPmNj8ieUpC1GANNt8uMVqZXbSOzL5ExXALM=\r\n=7pto\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.3-next.1_1661825913186_0.835799082542096"},"_hasShrinkwrap":false},"0.4.3":{"name":"alge","version":"0.4.3","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.5","license":"MIT","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"29.0.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.244","@swc/helpers":"0.4.11","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"29.0.0","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.0","@typescript-eslint/parser":"5.36.0","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"29.0.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.1.1","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2"},"gitHead":"257a94676c5f94805da66cdb40b97ccf9502f198","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.4.3","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-viq9yEnuiIqLy+68bDrhe8aC/XuuCIcj9/SrsGX/1hp4vRgw6GqQd3X9uBTVmBjA/zKfciYRPj4lReMXQdYiWg==","shasum":"4adf50a440075e085ce2c10f99e83818aa9cb58e","tarball":"https://registry.npmjs.org/alge/-/alge-0.4.3.tgz","fileCount":210,"unpackedSize":289930,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBG5HjUNyewhUxBnCUY9l9qoD0hIG9wscvBcE1xxzkvMAiEAgBfehz65BD6mDYrVxlNgGJFgNj5BFPHTEedsTi+jLmE="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjDXgHACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmraiQ//TEy50Gox+8c1Z+bcr52o8WTBSlMi+bYvQALInBikNJlZDGvh\r\nUwGdLzi/diEXp7MwqXMmaFUX70LrFM5vsRl5hH9TnvfRJflPYiCrRNhkhh/K\r\nJceW5lD2hbQGWfmIlLAekdOI6ymX7ZfYiZUJvR29x3YZ6AV0ApGgLaUAcOO3\r\nHlo1ESbUGZy3OPBxNHOj7iFf0FhebRLxSNB+MsLqAmicF8QVE9fyR0ZSjKYz\r\ntvlOdlhH4BJmvyJf1n+ATey/4xCqxTSVkXbGDGKG3apKU393ivSq5rI1OOPB\r\nsBdJq6+/QKBZEn20d0gk5KxBvd9W9x4mSJ5xigy5Zjk8lSkls8LKKUhdsRmd\r\niPSLJzhPumSmkBZ2khM0dWdtkJkfF8Kp5FD+OmimQ1q+d6AZqzUf0VSLKxDr\r\n0qTW8hxBr7K+ULTHG3PZ9RrQxCyxWq8s6WTqsOyv5ISjl1mkgQbXwfeRCL6l\r\nHR1VMO2vyNTGcALvF1NvWv8UrE8ktyrP7u0hRTOcHIzm3XDB4dOvwU1dmj5J\r\nlyZKEmd7jYas9IwwQlS0EICC0W0hePTI0qEhHjc+kRIbXICVo5QcgMZL42QR\r\nb1QUtx5Tzr2KBvyZ8rXIUt9Hst8xYYebZjY9EJK2qqBXOpO8AXeB7kqqqIR2\r\ngHux2DYEj6vtW9TAF98Q86BWp4CBM+BWjao=\r\n=2EPR\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.4.3_1661827079653_0.655773357106797"},"_hasShrinkwrap":false},"0.5.0-next.1":{"name":"alge","version":"0.5.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.5","license":"MIT","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"29.0.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.244","@swc/helpers":"0.4.11","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"29.0.0","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.0","@typescript-eslint/parser":"5.36.0","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"29.0.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.1.1","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"a05d0b048bcca356997d81d05daf25e4f46f0ba5","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.0-next.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-irmQ0sknT0+KD/2yrUTC4l7/b3/Qo7Y1NYWTi7JE/tPcsJVprRwPHi0GFyuv9GIDpx315fDuCi28wAUbeeVgsQ==","shasum":"845913402f66236b39c7e073d72ab32f5b443089","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.0-next.1.tgz","fileCount":210,"unpackedSize":292679,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFyLNvjj9hIsn9zJpJJZft/t/ApJJsdfT0W20rm1HdvUAiEAo60TO3WQr+7NogGEgylrm0BbHHzgWCr8RCh4rkysVn4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjDYCdACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpxqQ/9FXXklU9t5wYto3GFCGYZC2HJr6mnIvgjoncJgZS+NOtQOlo6\r\nex3DOhG08cLLwLErjlIDR8PqdOrvP/yRonfqqnhsK1zYkY1a3Pm6e6Iuhln3\r\nox4W2h1+xLmjpGl55vYkzlWAsYHu1dkWhBPCtPSdopLyeVY3whaJFSGzsx+T\r\nHOqQRk7jLxrjG8bu5isxLM26PQEW9fTO4exN0y5XsEg8YH0cWEDwNjwryr1s\r\nppY1hHgUxE58yjHRnetoxOkv3aNiws1CRMJz09ogMRn3UG1z1cd+JcqJoxCs\r\nU8uVw8AJX4kcxuYG1vrcLPUkjCxdbiVAcFi9fL9+2R3lM0V/vFxi2Sm9GsqZ\r\nnZMAbISUG9Jwnuz7GZLMrLW3Z0212M6zn2pNr+zFZ65j5dnr4RFuxyfsQ/fN\r\nH3DrTvkdxVVMEnpETgPwwPur+JNLm/r5aI4PLefD6m8ccNzyyumw7Idttqoh\r\nBdr4ED0WwmJqJcXAFhstMCP1mW9xWomX0adqqpm5dZOSSePlFKAQG4KNbIsV\r\nF0o4kjUVAez1DIpvsMlqkm/Kl/lqoITDaUJoITuJZat+BROq9CIwW7OO0GX4\r\nmmzFYJgsVAIgajIYj+7M2qavPAzuhqO/ZjCUe7lOwcQKd1owLyGf0dSPC9bE\r\niSXRz6Es9VmgQbcS2+WYZk5rROx813QWvDY=\r\n=55zi\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.0-next.1_1661829277413_0.42240265895819173"},"_hasShrinkwrap":false},"0.5.0":{"name":"alge","version":"0.5.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.9.5","license":"MIT","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","type-check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"tsc --project tsconfig.cjs.json","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"29.0.1","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.244","@swc/helpers":"0.4.11","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"29.0.0","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.0","@typescript-eslint/parser":"5.36.0","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"7.0.0","eslint-plugin-tsdoc":"0.2.16","jest":"29.0.1","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.1.1","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2"},"gitHead":"a05d0b048bcca356997d81d05daf25e4f46f0ba5","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.0","_nodeVersion":"16.16.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-4cjrJYe7jRQsJctv41SmFyKN297CV6nFdM3r5v2oI7nQElkNhvmFVl/8ppjOhN3BQzBi0jFwZFMi1W2D2jQ/oQ==","shasum":"93862353998f5bdde9bd7bb422d72d2ec59b8346","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.0.tgz","fileCount":210,"unpackedSize":292672,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBw4PRXjmNQckuqSgsQu8+jKIDXMNKnFS6N+VdoQoXzPAiEAt0GG1iqZz2IhX9i2pcI2o95+m7KpRGtgBuqOPdA3bTQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjDYGtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrCBRAAl1XOoJV4jWYQjriQPsR9WI53voPocGClO6mWve43MdMF9dW4\r\nMiKNY4l6TOA3u8bFKsbmvuri0cT4TuVwselOQhbCfr9hkpyVd+XBV4hl2N4i\r\ngxGUmYc/saSdMqSrmjdraNhj1X9j/iCaIjofsho3Zu6/FG/w8wz9Hgt5/STm\r\ncsIRcOiOFDlu6OPhEMjJm8W2uwDGHu6QYtxPXV9UL2e56MfGSsXGV8RfWuok\r\nYCSaEZHQnFdMs1g0F0Hovpqrf6/l01l7nxibiQPA5w96Ajw3txHQR3Ex1sRa\r\n1VEcP7ODtweaW2dlXeyhFxy4dckAOs993ZShBL6PAiRMb2WT8olsk5E+ULql\r\naypqSXj3X7zK+VHhzTsAESCk2ntTH5i6VhGZijTt/SDj3yrYcLw3DkisNsPo\r\nsBdIyD0oZi8GnKvL0Se/fGQTAR37naz4pob1pddumFW1oiWCmOvG+bC5obNw\r\n90w9VT+NAKx7wViP9gvzXtG6PLU754Gdl89lVG+zNEbU8PwebcWPCoksJXRP\r\nFfRRYqy1bbJDPrquJUiUWP1VONXxwOUhvIIDDC4hz7cLvqRayE3ZM5JB+CEn\r\nNEH6f1nuvNh/etvu94FipUICUubSdnQFc7dwVPfGWstPMbyCO87fUbxNDz7x\r\nWLKHirGWjL9YU5jsOY+qSXJ/U12pGjHv29M=\r\n=S3Wk\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.0_1661829548978_0.16651839608505759"},"_hasShrinkwrap":false},"0.5.1-next.1":{"name":"alge","version":"0.5.1-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"jest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@jest/types":"29.0.2","@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.247","@swc/helpers":"0.4.11","@swc/jest":"0.2.22","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/jest":"29.0.0","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.1","@typescript-eslint/parser":"5.36.1","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"^6.1.0","fast-glob":"^3.2.11","fs-jetpack":"^4.3.1","jest":"29.0.2","jest-watch-select-projects":"2.0.0","jest-watch-suspend":"1.1.2","jest-watch-typeahead":"2.1.1","konn":"0.7.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-jest":"28.0.8","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"c5e19dc46b1f2c6d53f97d125f4d5862d4a803ef","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.1-next.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-5b9rMFSh+QdR9iyy71A6AaSuEuj4ufS3fzIwsSNNVvfl0+arldYmXIFtAjw99lI/ECMcI5bR29Wd8EjACIxIbQ==","shasum":"23b31385a836712ee8181eb3eeecdb9a4a10edbd","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.1-next.1.tgz","fileCount":210,"unpackedSize":283369,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOalMY4usaZjfkBq2nvoPLQL9Px9YvwAnlQzB+b9BtoQIhAK7qy6vvuoYyU3wLKyem+vPYnnEbIaw1c2Ad+UoPiS1J"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFkTtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqqFg/+KqF1dfEs0dteZv/HMRv7he558rOrNPw0yM6FXzDL+BTAc3KZ\r\nFTa5i+kcZL+pRtXgVJncPmMV21wWio+8YtNkuKOq7Bp5Vq8KeGGxD3sD/OB5\r\nmaL0OSps39FPgxFsL5ghslYIkSVQmKwXd52hdi2ziE8yxBq2R8QW7QH3F0dr\r\nJEwzwnT4faU7M54GS466WyQe+fiF1O9lyEbi+tvaHASrmwUbrJwZw7FCqbWm\r\nLAdHwc+kSd5VaINrXdz+ciRuyl/5ZN8/RbvHW1MCkCEGh6KnroFYJRLlYgnN\r\nC3frabz266XTkkU84GAJsFhGGOhvwKpDGLkLd8Z5ST7iizinlklR/SfLYxLn\r\nIYYy23f3q9TWUveHFyDqgv7jro3rWzk31Bu1RLACs1OudVwWdh1/GYMUjLt5\r\ntjw99JagBVLGTinaXs/sRGbrA2DIMnW4ZlsEE/blt4R5TYfUstHoDjbGIiBo\r\nYVbGGu/gZbzKNj6lCppfSxOkebOU7IPZOcRa7MTEoDsoh+v65IH4oL2DbdGE\r\nuECIJ9vH9M5RM6K0hJrY5BXNhL5pxilaYRj36X0sL2ZD4FfMAM7USNAePG1w\r\n86tNQSScc00eOoRAr9TcXc/FqomY0v6lhqrvy85HzY2cbkEYIhXfN8HGFauK\r\n44ueefS59i+eWoU8CQdSaXAxqpA2pzoUL5c=\r\n=mg3+\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.1-next.1_1662403820992_0.14961202557358"},"_hasShrinkwrap":false},"0.5.1-next.2":{"name":"alge","version":"0.5.1-next.2","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"default":"./dist/cjs/index.js"},"import":{"default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist-cjs dist-esm node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.247","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.1","@typescript-eslint/parser":"5.36.1","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"^6.1.0","fast-glob":"^3.2.11","fs-jetpack":"^4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"^0.23.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"89004384e87a8c7253a6245c83419af48ce20b31","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.1-next.2","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-MIhLCMQNaS2Ij3evMi7GCDEC6E3EVa6tE1HhSv0I8WRLik9N3yfyge/jUjMJvlyQ0L1UoJ9K0flHHr/4WhvBQQ==","shasum":"302acf608f90be718e35f825671a97936e2ac2e5","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.1-next.2.tgz","fileCount":210,"unpackedSize":283364,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGUhIRZbMyrAJbfroETfVsLUuM373M8a5rjf4KD1qSqIAiBKo+3giXumC74UD0Y/uuMmkJnPNE/DeL7HftyzJ2qAwQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFnAGACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZyQ//YbV4l6V7kWeSWNPYYVFHhrihiSxRvs+2i1Vis9YcZkqzLDdN\r\n6FizIswltvb7tnV7zGzOxK1uuSJeGYPSXHKfbgjk8iavUvhjKmzgxCsPtU+j\r\nr6pZZNpYqqQZDg/O/5Ucrl7HfxZba0eupMjdQu16qhAXpLlCnwF6Cr8TqiAq\r\nyBcYWLeMYHa01H7i4kwol9k8BhwckDW+LmJW8WQj9+f2lpRTVIGp3GJUNQdr\r\nxWAsTUzXmZ/mnqkwPfYocdga5obcSqThvnqcaNWOy+c8HwVWj3nPu7S6ymWd\r\ndTYkai3udwjpFwAhXmLVWE4eHBnexMdX8ZAqQ4lGGVUc3XQLU7U4gCAekiLk\r\nFaqPPjuOh76wB6qS9KkeBqEDYTy2pY3a9n/5DwvK5A6xfGlZVhFOzUYbiu45\r\nMa1RLyVhB+5W8YPfQSNX3u145cSOKU31g570ltUQ3fWU4M6gk/A4btQo3Fjj\r\nHXmOr6JDPFEczeRek/rJ/EludpwU1xVaef0dwvszy6Z3VU+/vGKA2svdjSXr\r\n19dWBZw1bMvb+KK7JsydoC8tuyC1uGaa9y1CLlaOJaMKm8Xy+SxwQPqNgmXa\r\niJ7cqRwjN3H5Nfi1QEB5JktS3is3DQ8VK1mXA5WWyUgi5xPWt2kA+Xo3nOE/\r\nHiQsJewbKxKOion/LAlzX3A4u5zU5cAR0SY=\r\n=TB8R\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.1-next.2_1662414854109_0.8607467966499753"},"_hasShrinkwrap":false},"0.5.1-next.3":{"name":"alge","version":"0.5.1-next.3","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.247","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.1","@typescript-eslint/parser":"5.36.1","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"5062ed093dbe6694bcdb5330f21aa1e40337d648","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.1-next.3","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-GTLN3o32bS636jCDfFdLhZqmHRbG0wOgrLULGonHCdPe/fkc0f4eZbmvev/Bp8x/LIluXce6gJvJdTxQo622xw==","shasum":"0f9e33a1683a4df06edf8da4d3ac4edd4cc8c67e","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.1-next.3.tgz","fileCount":210,"unpackedSize":283397,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDSwdIjXUFoFrwtNWxxsAcyIPvQ/xrxQWA7w5dH/XunfAIgaFj1fkIC0wXepvONEr0SIzcC8pO5GQTyeVjrX9YiM9I="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFnGWACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrKAhAAiT1fI4MkpDdQtPpsr512+rgO6SQQKbHA/EV2dpKAqkGv/bkB\r\nx380g8KRc5cZHXjh2BLCz6y2EIILGJ9epXsYqIgivoft+S9CIUya8Pt46vA2\r\nRn4L/a1VMCzoHlcmvEOzB43gU+bf6CKIHvG/Dj+7HS1+ZMIpzYRojQLafZXh\r\nNS1vocbRBre+UpI4Q0IvvnTVj0NQWIxLtvN0TDJbkx1LVBwYndbhJt2YaFk5\r\nmTjwpJsc6+9p9/OpbvE9BTdj+MPAkAoC+NN9ALDL5M+V9R0Y+O5TP/3HJXqQ\r\nXnGaQw4i52mzslOpnx0riJq27ayxlvKM5PFvw8UeIsjXNbGa9Ri5AIGvJ57l\r\nYGvtgapNCAwq6Zx3flstteuYYLRGbjtfklFn8bTjQKplgqjkwrJR14WSbJz7\r\nDNdwtOxR6YK52MdP8+nO8Ae0WLUbLM+Gxl+oVJPVxVZTo6ecuIHfOR8mMU2N\r\niSHDeeIDgjhhr4cCfhtxXbHC1YaQPnDAD/cTL/CEf9sjKeoQkmpQKglJ3IdW\r\nI9x+9XvyCgExhzHNDd01BeP/dwJPnuLr6CTi8+ZjnUDqulelH4sULxQl/MO1\r\nvgTm2cp2UJrMVbF0MIBoowLmngMmjugQlQX1p+du3pjEAHsX8I0URXxCjMQh\r\nC9EMS3kSotB/ZTacisPCxKbLRZ3pgE2rmyQ=\r\n=exI7\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.1-next.3_1662415253849_0.056838846832532264"},"_hasShrinkwrap":false},"0.5.1-next.4":{"name":"alge","version":"0.5.1-next.4","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.247","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.2","@typescript-eslint/parser":"5.36.2","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. ~~A admittedly rambling 😃 [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format~~ **Outdated, I need to record a new one.**\n\n   [![Video Cover](docs/assets/cover.png)](https://youtu.be/fLlVQSJx4AU)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"517a968460d7075aae89d8b21a9e692aaea4984c","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.1-next.4","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-YL8KRcNZkFOlEWiFjpaR8Nha1q+pj8fJvwgG02EGgGkJl/CK2rEHir8PqDCaWL3gZFIoAk7hNgjytsdteDXpwQ==","shasum":"353df198bd0f3d20915c673d4c26ab1f35211596","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.1-next.4.tgz","fileCount":210,"unpackedSize":283382,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGQluThzhL2RQQrvcnHtUtTCKaplnp64niOlOx2Ujc6gAiEA0+E2GpOqtKA3RvcYLErZ1C6ur402hEkTnvQmgVOgSw0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFnHuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrjEg/9HCzIlriUgnB27I3SsKOArEJTOlIjcszhv0bJWPgc5Je1w78h\r\nGOLk1F7s3K6urmbMw6A9NM51cPd0XyCDP464klR9XUweKfRQ3Nq255Id6Qdf\r\nHkXbwJk+cyXrY0fXXi6k8r5XjJU76mOF9Wmsfiv4DNWfbZWS8ahG/oNgmCPb\r\nnn2x5Lk7fvh9hC62u5UV8xJuz8U9JnPaEuwhkWbb9PX3bpEaEX7PO5KaTiP7\r\nVX7fLQDt6j1CNfFil/E5UoYy0PMeG9jVG+Miz37bNn7h2lKieaAAsLiXQVHM\r\nUU56kZzvkawQtmSAY1AqmuzxuhRgUQV4Rc1VQodT9w97Wqv1RVZ3QTFJbsJP\r\nTkDDpVBi+YMut3VatonZOaP2OrDmxM2/3NhwBet4PLfOOi8Nussthaa42+Jg\r\nsu3dA6JpazF1vH2xoA034KivLi1YfjOUteossZs9sxJ9yeTtds5buB88ljDT\r\n9sXDecJ+2Gagh4GpK6EoJbtUcoBbmV46AFOsXGOsslog14gFowcrN8xFzlXh\r\n2ApExCtwEEGRIdX/YWaojSoDcNqdFSc/lAcJ+rjsWz2UOnUTkJfHwx3jPoyq\r\nadBVXU901lJikRjsCIO000enQ/76ZOifUGidmUFrPiOFjSzj4+qSskm5sTjr\r\nFKyYEWoGA4iHZ5Np4D/T8leeqwzhtworBTs=\r\n=gTxx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.1-next.4_1662415342285_0.25410613779852476"},"_hasShrinkwrap":false},"0.5.1":{"name":"alge","version":"0.5.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.247","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.2","@typescript-eslint/parser":"5.36.2","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"gitHead":"b9a1bff7599f451235123db3588e362c887d30ee","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.5.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-n3lg0E7AmmG09HhQiMx80UJERHwwY9mxa2hGh6fpgvlaeOncu62G+TC08p+Q8nxguCV0i7/Uk+HTBqkbGczOpA==","shasum":"d2b3387d13f3820de63bc322b4928ddb1abc4cb9","tarball":"https://registry.npmjs.org/alge/-/alge-0.5.1.tgz","fileCount":210,"unpackedSize":283304,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDa0qlx+zczBhYCRbTEBX8hwGyBoxeljb1xzBb9EqJnugIhANi2Orm1E2WermlkHhxkV/EV7TbHCGFK3ZiKiqZgdUdj"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFnagACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqkFA//UdRsXw8bu1ecCzw3yQniWqMljny9ObMVupT23l5w48TheJX8\r\nBjh6KfFcULFD3DrDafLSZ0XEjbzdwmVzpapEP8GTvC5eU7OxPaEZ+GGsGfEw\r\n1DOhsDwcgx7LE8RTzHjRplvm4XWbOH2yyiB4Ug1/BY7hshISMKak6znNS8Qc\r\nRe8tfnFhBk5Lfn3RKvvOI1nU3NLFjwIdfQXCveGGwkVwAjJGXK9CI2K3hZLj\r\ncE8kMil8+zTTJbaXiuWRnLudnxoGk7X4BvqdSibJ+pGZXNjptTbXahS5oxKP\r\nQwYbfjAbXU3G24HesfnU7brhaZOOr0IwMQsspMegKgC2nbzGBMJF/7lULDVC\r\nh/F202xBgazfRPgyMEGiIYb/HXn5v0B46KB5fWo2iDeCPCnOgaxlp6dxeo30\r\nTdJkoIYolY7duYRivwTd3tPyeLpO3vAMCg1KwqVE6WcsdfVxdIMplX+xkv47\r\niR4C08nwqDOZ9g8OjL+DCl7MbfipYdTJYMgH3Bn+bjJo3wurzugk/m9pzOU6\r\n5rNWY+B2DfdZ2JSWlKsmD+rvAgAOlwvCFEIxD5cQRrmagwMqfO8tRcol3UO5\r\nbRbcvVxjq32n/EgARcciLOkqeQQG5MYoTz4L662xCICyjjyIFm7h6Q1n9KTO\r\nUD9BYK+cqawvG6jN72QLyPINbfpAiPkfCSk=\r\n=fhPo\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.5.1_1662416544348_0.018263879735861632"},"_hasShrinkwrap":false},"0.6.0-next.1":{"name":"alge","version":"0.6.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.248","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.2","@typescript-eslint/parser":"5.36.2","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"408eb22c7f82b4e67ac70a659b6da831afc86ae0","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.6.0-next.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-wjAZ0bliP92Geyyl5KOmv7tfouYls4vDNLZV65gUWVWM1sOLws6V85CcFpw5eYUZmx5I+Du1+IEVXiNxJUXAIg==","shasum":"b50e309670b0c0e136b74e6fbe1f2ad1b4e9a605","tarball":"https://registry.npmjs.org/alge/-/alge-0.6.0-next.1.tgz","fileCount":210,"unpackedSize":284975,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDw1+jnuhOm19VO/lgj0VkI+edpsfYLvwHia9OlXPgzRQIhAKK/Ii01blRQS6C821JqPCCwttiZmeCYJKhjGlH3/j4T"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjF8O9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp/9w//Y75qB3MUGGcuqcrQIek/z1QcL7D9flbF898mYvHbA0f40sEw\r\neYjQAKelsvwDItRx5+IVXKpgvAJuboh3sw5bCXeB1kcfcqZiNe9XEUnss5b3\r\nrgb+m43ulZn4IZiynrCrxbbCT0c5kKQWaMYxFURhs386gxGOxRsxeeBKzsPg\r\nFeSgWbnoJPreicyq0cP/qVvuGey5ONrz9z9qBOF+IJuYyr2KsBBpMOIEwExn\r\n9xp5ylkJfTB4uVM9MSUZPwJSxh+karoo7S/J0g7oVjUN/bdG2qape7qJLsAj\r\n3sHgtRF6gzumXbKfGJwGAzyiDejLPfIl46byGL+DmGY55I+2TQpUY8HRxe5V\r\nnChjPYlTM74JsAreILnOBU0g/cnp+LQxV7iAQTjmcYp3fFds+r8tNVaohRbS\r\nWtsanMKV8bNjQwTgAf4cIWGWafSqpRliBQL+tWiJHGBNqMNcv4sodUB+6+aq\r\nr578LGeIPw0z+RW+sCIJbhqInnRIg/swU0Uf9jmRUuEufBNlyB4PQuTHNMSq\r\nLyInKJskqnN6MSVTm6/KxZLLtGJfL62x6s7lLgrDAHYDa784vRqds75+Fu+5\r\nGQwsO789C54hwSou/FUpCDGQFjv4SOJqQViKMb+2wdPGD70iA1kNXI3ftagt\r\nY+Q4WA4VAXnWINRVfhd3va8065jsJybykJE=\r\n=Sd2e\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.6.0-next.1_1662501821431_0.654500332699693"},"_hasShrinkwrap":false},"0.6.0":{"name":"alge","version":"0.6.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.248","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.2","@typescript-eslint/parser":"5.36.2","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"gitHead":"408eb22c7f82b4e67ac70a659b6da831afc86ae0","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.6.0","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-p7aA4q6dHKZPWuMIBJAN/3rVXm7KmhfOfg2MaFVqvVEdCabo4jHxvmcL288fDIR5vLKrqwpN9iznz9tHtCkazQ==","shasum":"f256cdf0a9f97937635ac990034bf44b24584445","tarball":"https://registry.npmjs.org/alge/-/alge-0.6.0.tgz","fileCount":210,"unpackedSize":284968,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSrr5uYdsyredxg8WDczCmJ64chOKXNmj03CVIkmSirQIhAKS1SZ5ml9sx+3Uwkn6JUWGr2lVb5BmKLzwjWCMdT3qX"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjF8QqACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqO6BAAomeGRLqpR89LCCAfVuFDNxSdQVW42WRzLLCYM6GU2YjaOsVJ\r\nNr+8BNWOUcF8b60uhHVoE+AptnvE8kYD3tH2+bhCrtWNXx5YvMKXtwa6b9eb\r\nGFCtILp4ODIzfJkOml7RFb6ZRRZ//TBS4JILcuJy4lnpFmtSG0qCIh2WXhOv\r\nTTVgvVEPunhz6cbv6YEgr62PX+OJLvV0G2Zenjo4YRA/jMCcKiTpyAiauepF\r\nCRf96cWruzraCyK9DpOG+h5s5OweDOzG9Z8uy56XodqtyJ2jNJN4HnVy08qg\r\nMYTFp+qPogY6h7Tu5CaPAhOJqptMJMgMKKo0czyUznl6ztT3OK23L/G2UQX3\r\ncxJoGe72AP80Rx1Fbvakm50pJH0kRQ2/GusTJiBBN//2xNMGRyd5fWsLiPkf\r\n5ciuzvXEZN/KZpPax3iXX2cLJxVlyyVc+wfszlt5OxvBiizLqK8rEAXNz4xT\r\n5NOioPJRyc9gQ0ycr1yWgtKwvyV7jhSu+5vnVqMbgAq6FlrgLxvUZzVXDfGd\r\nWrJrwt3VEvQ51mPxmKsC8f+7diurl+n6XDqTZ/Z0CLbjVTBYKHBkRVBLbEQa\r\negGZ7wOIDKz4QxXG2PNcVzHnwOII9opcSVFgeeBcgxOvzQtXflSF5rlYfpSg\r\nuEvpaL1bVcLC4MI5sqT+Hjz8WsAQiBxayWg=\r\n=+GB/\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.6.0_1662501930624_0.46694575102723124"},"_hasShrinkwrap":false},"0.6.1-next.1":{"name":"alge","version":"0.6.1-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.248","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.2","@typescript-eslint/parser":"5.36.2","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [Pattern Matching](#pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## Pattern Matching\n\nUse `.match` to branch on your ADT's variants or even branch multiple ways among a single variant based on different data patterns.\n\n- Pass your value to `Alge.match(yourValue)` to begin the pattern matching.\n- Use tag or data or both matchers (see below)\n- Finish the chain with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function:\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(() => (Math.random() > 0.5 ? 1 : 2))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"fb253fe440e354844f93de89b26c88d7b23a108b","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.6.1-next.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-9uiYQ2Hw+qwZ+ycwq8IZgH7F2NXgb46MfYgaSDbl7NyRvRuDLNJbHJ2zPImw4CDHcwEbC75GfTdjfeIjUWaP/w==","shasum":"08c5c0e2c7bd0d68fd57b8f74fcd27e9ce279d90","tarball":"https://registry.npmjs.org/alge/-/alge-0.6.1-next.1.tgz","fileCount":210,"unpackedSize":285011,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDyGACxSVCyDQ5GTV+9QolppGysc15ed/9OLRLNFejsjQIgOUNxNJ0zkbyVb+qJkYoUXQXt52NB8JxD1p5ESuE6lAs="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjGhAZACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqGHg//bq5leYbn5vKguR8+oCZoDNR5khBkQpt3SPupkrimHGohOf3p\r\nyCPT6S4qD9pXrT/MHlEe49EbtvJasx8YZ1s4cQKEWqtMxd/YgPySpg8hSCCT\r\nYBq1Ln6J/x05d5m2fFkjYl89sAWLFjuQaJkh8L8GgzdyfH06FIZgyg8CDlAs\r\nuYOU/YFhJaSy3l9/EeWTAGXF2xZpOp8Megn43ITEpTqQQI7jiSvdlnAzXjOI\r\nmuFHg2fmhWiUVmFJa5IgwbbfGN7FTXv0EYiXh0vrLL3YAfWsrqZ83lvRFm6z\r\nCO9ZC3uOye6cZbj2xYJwjI88TXPqUymVProOtkfetQ/TY0LksW585SuzGaAs\r\n3HjgI3tY0JAr4f8uw8pBjf7CchULJbqq5PC2Snwyxjl6r2hmrnfH+j+p7PLf\r\nE1UE/kqTPtMJLNGk2ssqCXefwCMDYhkWBWPQCLliYz4K50D1kZNjwX+SOg8P\r\nav8sySMLbvg/Tw5PZ+aaWmArTY0iu1J4fvJGDRqV5TLtVaDvyyZswL2UzjuI\r\nvxsi586k8L6QlGOoUA7Zg5rCwW065cilNgDPBggw7uYmTIaggnG7CUcXAXBH\r\nbbjJYsseGaOARL4IdcZNIypKazNq98F8KDlZerUAevI3BlA/7pNTrgbdEcl3\r\noOgrjjBvgGOnNmTRfCqgs86bxHrdw00fytw=\r\n=Gy0U\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.6.1-next.1_1662652441524_0.9495665403156586"},"_hasShrinkwrap":false},"0.6.1":{"name":"alge","version":"0.6.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.11.0","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.2.248","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.36.2","@typescript-eslint/parser":"5.36.2","dripip":"0.10.0","eslint":"8.23.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.16","execa":"6.1.0","fast-glob":"3.2.11","fs-jetpack":"4.3.1","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.23.0","typescript":"4.8.2","vitest":"0.23.1"},"gitHead":"fb253fe440e354844f93de89b26c88d7b23a108b","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.6.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-LcKiPDMzWnXLHzhzp8/JPJE9CDcjSsGzDpehflmED4JCGMJG9reYTUZVgcgIZTWlLcOz8Uf8jxDYPO94fm2vVg==","shasum":"985dcb00e9aac69007759e002d9a09218cd10891","tarball":"https://registry.npmjs.org/alge/-/alge-0.6.1.tgz","fileCount":210,"unpackedSize":285004,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB/rsxeEOZJmcAAgIiS1P2ESw+CTOO6FUztBuWIxTEl9AiAI/713viLLLF4/3CI0W28QUIIFUzjf8KHBLb7quHNZrw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjGhEbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp8hA//We0JDVhk+VJVYe4BICpCuI6Q0p7nnGj6wJtWlYJUxCUi/NQt\r\nxZLm6awyTwNQdL0RDRVZsATMbRgwgbzWunuM9/eZV9LmGOPKVUifajL9X6vM\r\nvGGIqIa5aEzK3c3VzFuz4ThJuzLAzh+U7MfCrFuYFgsFQtT0iqe4jXj24gwJ\r\nKlJ3any2TmbPCZCIrH96/ogUMAtxH87wuTDl2WeXSejrGt6TFGz8ph117cUJ\r\nDeL/31AIKsT0LR3FVLG3aillPK2VxYIHS2WHMQ5JxlB2q4GNa7KBmcpaTE2t\r\nF8Mnj3yZo9RxNnooBq/FRZ0C/BLM+8omfBfBTVsP+4Pg2UIxUzivWSE8yqQa\r\noIFH9JwA++m4b3tHRCUgnA4llbqr/h+KYfAQsLAmHuludY84c569foyt2C1H\r\n31v9/AgL7YGfYqc29cyIZHWbCVZJJm29/6AyLjIgFpgM3rtv6LwBk2rQWaQE\r\nBYHN8kuiagv+R+Hr0DwQa4pkbbqCMusobA1sYT9qW2DR0qOjaDZ/dFhTCM0h\r\nu26S7COzaZBxluonyPAYmncQrd+xyNYQLxCwP+qa8BZObtAQXcvY6nUwo0sZ\r\n6trKTprcs0WiM0+alJaYSEmxq4uJLEmUdYc5RMkqj9tSoxKGvT4cgcv2jYUJ\r\nD0fdXb600RuvoIArwgpaBUI5H71TRbqVTKg=\r\n=Zmrl\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.6.1_1662652699615_0.6146935355806196"},"_hasShrinkwrap":false},"0.7.0-next.1":{"name":"alge","version":"0.7.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.12.2","license":"MIT","type":"module","types":"./dist/esm/index.d.ts","exports":{".":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"pnpm format:prettier","format:prettier":"prettier --write .","format:check":"pnpm format:check:prettier","format:check:prettier":"prettier --check .","lint":"eslint . --ext .ts,.tsx --fix","lint:check":"eslint . --ext .ts,.tsx --max-warnings 0","types:check":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"ts-node scripts/build-cjs","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"rm -rf dist node_modules/.cache","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.3","@swc/helpers":"0.4.11","@tsconfig/node16":"1.0.3","@tsconfig/recommended":"1.0.1","@types/lodash.ismatch":"4.4.7","@types/node":"17.0.39","@types/semver":"7.3.12","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.38.0","@typescript-eslint/parser":"5.38.0","dripip":"0.10.0","eslint":"8.24.0","eslint-config-prettier":"8.5.0","eslint-plugin-codegen":"0.16.1","eslint-plugin-deprecation":"1.3.2","eslint-plugin-only-warn":"1.0.3","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"8.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"6.1.0","fast-glob":"3.2.12","fs-jetpack":"5.0.0","markdown-toc":"1.2.0","prettier":"2.7.1","semver":"7.3.7","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.24.1","typescript":"4.8.3","vitest":"0.23.4"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"215caaff09c06a0da849373d5eb350ad1cdb8ca9","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.7.0-next.1","_nodeVersion":"16.17.0","_npmVersion":"8.15.0","dist":{"integrity":"sha512-QGB+1x1LJtVi9VKvzEU4oXXHcOXNmMRvX15Vks12AVltlBmxY0yrthn/+hh1tsU7mlPbXXPNw4aIklVoh628+w==","shasum":"96aa0c3209becd64ba0d0a6acc536a5bec518a10","tarball":"https://registry.npmjs.org/alge/-/alge-0.7.0-next.1.tgz","fileCount":210,"unpackedSize":292158,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBZVl0evz55XEQWU/2BpCloAzflhhHu4RMsNiAU954/EAiBlVpneM5teke1+9bdFWdwkLBgnIj34YKdW7+HBNtjHAQ=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjLwNfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmowdQ/9Gp3RlPYQ7KBSByFDHsrULolzZQSfWX8a3i1t1mEUM64YB+Jq\r\nafC3/+Hs/U1uk7zBsb8wdWVp/Uz5G/UPIuN9imFGJaxv6dN1Cc+uq4Z0baev\r\nZjU95xviZc4S+D8GM2CuXTAY4DAyPB3WjaCZNk8c1nscnvWKZ1ArZ9tjcD1W\r\nxqzsjg0yT6ZJoJt0lYa/O8oN1FIDT47F6njctKNzvs8h7vtj+st+FIV4ezdu\r\nQ1De55L+eMD/Hz/3uJjmNTtxI+VxaS1xTD65S/19RZMIpupfQffRkrLoOh/j\r\nCX6aimrKsZZgAozFpb6+dD9bjskuMaLkvn3mvU7rJGT5IkRWroAFpAFvi0pU\r\nfkB7WVkG3+KOFDnce/rIM5k+3KglAWaIC/gSp0rLs6Cy3W6kIzGDhGexx4m2\r\nXCB8jcEaU4QgfTKXMKoRq8waCd8BIcvP1486IhqEt3fSaZOElIBWjIGzebh/\r\n45fE0wu/6fTy+xeA7skwIBQD/BCe/vJfBMguDnF+Ywn2nkm4HxRy+7TsaZ5N\r\nemNyIGUZdbSdFqJHDwufhEHa/fAfgzzh13zFbHGlO6hxh2BXAOEZuONNb7ZK\r\nGqa/mmFfpPdsF7+AU6IPa25cFWc4/0cgc5QUav+vQ/P0SvPYX6OjtvCG5osE\r\n+E6uZGlV7yue2qmnnLgIcgQfTVP/A9fhYWM=\r\n=9C9m\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.7.0-next.1_1664025439574_0.19902257312831373"},"_hasShrinkwrap":false},"0.7.0-next.2":{"name":"alge","version":"0.7.0-next.2","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.27.1","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.cts","default":"./build/cjs/index.cjs"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.35","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"^1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.0","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.52.0","@typescript-eslint/parser":"5.52.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.28.5"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n> Hey 👋, FYI here are some other TypeScript-first libraries I've created that might interest you:\n>\n> [`@molt/command`](https://github.com/jasonkuhrt/molt/tree/main/packages/@molt/command) for building simple scripts and command lines.\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"83bfa6b662a80efc198da10d3b07e9ab824111da","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.7.0-next.2","_nodeVersion":"16.19.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-LMRNhLlaa/X5PW53aKoJFdq73EDjvzfg2YB6tECEJNSaZLP7Aj53Q6TfA6P8ZgpoqTuHwaL/xeMQr8T+eJb3Mw==","shasum":"a96538fd997ddf918a30cd803cff24bf8929a66a","tarball":"https://registry.npmjs.org/alge/-/alge-0.7.0-next.2.tgz","fileCount":211,"unpackedSize":292765,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCizd3cQFsq/DW1po2C4xDOm52HcOV3mmruRfFvb9czAgIhALa+02wBPUlYsahyApniWGywXgY6RNr0ScZurv95Mc4P"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj8S1HACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrdHA//Ql2sccQIc0q9UIgLcOUV0PrjoF4Uttm8G336s0BSai8FZBjD\r\nj5clFUd68z6TkappjbGs/7majRlh0D/y+nhdabvucOQThfm/rudq0AJH/s3h\r\nnqYa3TzcfV7ZddDJwAs00LFG+5ZxT8808sp/7NGFv3k8BwjM+UWW97CtJptn\r\nSnPE0pwp6U1hhCWB4B/oCCUVh9Hi5U7DJ4yCHi+ml5teiH/n8dlD6F9/uCM9\r\nsJk08yd1DJqliT+CRHZ6bitEzgwhfOZVD9oS3EL8LktfAWiWdjSvYT4tpzZW\r\nj2SXVVCV7wF+16w7TQWkcpntgLwzigDN6nYs0C1FQo2WR1RWdPqjkiouUvcA\r\nS9FtJjlaAN3W/caGH7BP4wXCFMyvuCdcqiExI6NkLaO78E+kmOWLIw2/EQ0b\r\nq2U4Zx6O0j26otkQSOrCJ9dq7BIhosIgBerlnRPjyRxiqLpRCtF7MR9rtsDJ\r\n+W9RkE/P4WmG7kqTm0S87ogLNF4hvi3Tj1H0+b81Gzmnv8WWVOGnJEfJL3ka\r\npED1trOpwjNWKBn4FgazvhsF7TDDXH+ZfecaARSO7hz/bRmZ6kmNzWpRJrva\r\n3EtcmIodzhUE5eYYx1C5kTTXOa0BiHYd5lSB1LrLJumroGj1mOpbHjW5bIDA\r\nuZB/962dpnTseaYNhQHUSwwudCjPWH3OGH0=\r\n=Giyq\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.7.0-next.2_1676750151376_0.23291393506447688"},"_hasShrinkwrap":false},"0.7.0-next.3":{"name":"alge","version":"0.7.0-next.3","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.27.1","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.35","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"^1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.0","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.52.0","@typescript-eslint/parser":"5.52.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.28.5"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n> Hey 👋, FYI here are some other TypeScript-first libraries I've created that might interest you:\n>\n> [`@molt/command`](https://github.com/jasonkuhrt/molt/tree/main/packages/@molt/command) for building simple scripts and command lines.\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"28fcc4cde88f924a88740668146555de50bdbdd9","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.7.0-next.3","_nodeVersion":"16.19.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-Ud4ON0dXKvAmSmqz3PFxxiulzkRddo2A/IFe8kr1258CmVvs3+kuhTHrt4W7ZAhphRLNCx6OaYIN5VpcoPfsnw==","shasum":"8fc249ab96665cbfe34fe0a0efa00714320a53a3","tarball":"https://registry.npmjs.org/alge/-/alge-0.7.0-next.3.tgz","fileCount":220,"unpackedSize":296052,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFoNUU4mhV26GU9o6nwXcPzf63qpBlE/PC9puGEipu9NAiBgA4Sy1TJ+P5srD/UzIioyJ8EFo3XCZx/c9Dk0PUi0Ag=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj8S90ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq9YBAAij0EUYjTBd1me1X10rlGVbWl4v7/yKuia2ET7KO9Lc0u7ZnF\r\n18NCktyQBDJFmBrNl2we3oFnYKspgEosuNHlGxucFFTugWhuuXodJC4lAfEU\r\nmFY+S9hzBUI1bgtQ2kVvG5IQSl4N5LIW0jioNheDbScTEVyG70cz9atBT91D\r\nHmEoqHS/4AN0cGRstAxYng3KyKGi16Z5ohoWQp7yitUTE633lQ59warfjj+O\r\nda5nlYTt3uQJvwqcirEtTmWMCjhaRbClWwP+8Zqxc3H52kLhIKl5MvqoiJ4U\r\nFYgEC/QLjJhPPQS0IOQlYVkN3jC8mcv3XSHFQHZkje+JIlmN/2AEF8snSx7C\r\n1Ptm63PdpGmQoZInVua1VVppnNtjj9fJ/G13NOgIziopLzs9EKYxkfGhFqyS\r\nqemdSaV/ExIV9b7RUDsgRB5bSg7qY6B4qBaFjP2R4Gqu+nSK+7Lokx4hsO3l\r\nfqjnui+adDifsTM+elZokOB7xtuQD6qS9a8uQLVd/XT/aVykxUTW6Kw7amXl\r\nzcRrRpaykClkzLwOXR07E4ManOPx67SyAOj/8dmshmnSCP5tygQ0KZ0p5MRD\r\nd3sIoRoITgM2SliF5ICdZeHPSFwEq1A2/KJEUWk3Wrn/6qulHhmBVSpZ3CHs\r\nTHPEe1ww8mNBPixjVS5CQvIjyoJMvWgNins=\r\n=Wa0x\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.7.0-next.3_1676750708651_0.19420022243371649"},"_hasShrinkwrap":false},"0.7.0-next.4":{"name":"alge","version":"0.7.0-next.4","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.27.1","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.35","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"^1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.0","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.52.0","@typescript-eslint/parser":"5.52.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.28.5"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n> Hey 👋, FYI here are some other TypeScript-first libraries I've created that might interest you:\n>\n> [`@molt/command`](https://github.com/jasonkuhrt/molt/tree/main/packages/@molt/command) for building simple scripts and command lines.\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag (`_tag`). You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"504f4de81e05159a56a2fbf8683c4bbc172bb227","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.7.0-next.4","_nodeVersion":"16.19.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-5CaehyL5FVFcO+TqW8BdVthIsN5MALo8edvDQXW140vp/QE8FM79RbaVaSWxm+oT5gjCse4vxg2611V+/pscKw==","shasum":"8640a82a1d672c7751cffc966e4d0c4bd0319bac","tarball":"https://registry.npmjs.org/alge/-/alge-0.7.0-next.4.tgz","fileCount":220,"unpackedSize":297512,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDI3ck0OvVETF3WrTm+WOCZ2Ej/5F8Vb2upN39G42BunwIhAIKH5KlLtsLD4gW+akv33WYugMQHJOwBx1iUyLjZB8C1"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj8TH/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrxEw//SmvI6GnB3pN4NuZqLB8cFabfcx8u/IOBAtaNmbfI7munGsZv\r\nb5DE1XfXPeYrLdGyF4A6h09EAyL6sPL+kpBlTo9d260tgSZZK0G/L/WiGUGR\r\nYR8YdJS7HMLouII5HjYeTL1o2T4HviGUzm9n2+3rvC595IBc2jU9eVanQ9pj\r\nR7EwK/VsjlqX5Qz3NZDZamHG45sFJZPTgNyNEj6G5FUs3ElaJ/w451XsC3AZ\r\nLLiEMAQBZeqWo4xWPnfyCqJZnCyW8JoTTOl4GllE8cw4j++EZEIFOuTe79Ew\r\nA5oxIGWeVHQtueQi84FCu1tb+rTkAvjAY1YhHfh2MKltGFNP3nyHoXYvCp1l\r\nCFaMxjV0hXpSz5iLrQGz61O+pTu268oAv+cv1CKfuhq89DENUpWAhWwp3m5P\r\nOXiHgHwVao7SU8+9Rl+tJAyf0/X7Bn99aBKo7Nu4iu+OV3AWmE2GMqhbYw9T\r\nzh/ny2OkEblH2KZxjszZ8484X8+dsi+2M30glflB6z4SmkQ0TI1caq+BO7Y9\r\nd9JbIzl/+qTaTEKxEiTKUTK00PZmoAqF+TEvwLArRIlOQ8pCgpFE/Z5B1cIm\r\nhs0WO10UXeGsoRWMMYzYAbNlUHaP84ShEra1QZX/ewfbrw+kInAW7XZgd7VC\r\nu6gMMG6pgULgXXVfcQzcELhG10s+xBabddo=\r\n=FOHT\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.7.0-next.4_1676751358813_0.3511331735488439"},"_hasShrinkwrap":false},"0.7.0":{"name":"alge","version":"0.7.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.27.1","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.35","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.0","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.52.0","@typescript-eslint/parser":"5.52.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.28.5"},"gitHead":"3760e87d2b78ecfeffd04e986bc9c32fd9a32b35","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.7.0","_nodeVersion":"16.19.0","_npmVersion":"8.19.3","dist":{"integrity":"sha512-bpPT9QLXFEYzlyYUaQTNmNlm5ovz0SXak5UCdsY+krb1CP+QhsA1reRWOdpT9EnpOcMSHdySI5VektelhSb6qQ==","shasum":"74ba780309bd92c415553ac2dccd6798dfa16dd6","tarball":"https://registry.npmjs.org/alge/-/alge-0.7.0.tgz","fileCount":220,"unpackedSize":297504,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCG6Lz7jE9sWc/IAL22FcR1vMDvTYfHOqcq6hZ0uPeJsgIhAMe2BbIE1Mt7kEt9tPmrXVQ/Mu9P7Ak/PWdOfyyk/Byx"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj8TLfACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr1zg/9Fn8i15FJvRJnm2MbhTOb9bwfy2yciNtEg9Q7cyKlbi2a627G\r\nqhyg4bRKlEQjwGVw3ySxfDiAW4ATb5YDv2Z3JI8gmb+Mn8bUf3Ecoep9sbf8\r\nkykR61+uZYiwYUvwHJoLl3LJWhNWpJR8rAeMMtySILnQRC6yRD+GprqGvVxr\r\n1DtAlHprgEC4KckozkoM9HzJ3ViNUzxGLlImFr5wFRAV6HPInLPD2afBvutW\r\nZeVScodxf3Asvn3PAANXYkPLAKwGrknkIXNwSRJnbfDB8ufCIEC1jlMcHAI2\r\nn/el3JU/95ZobScD3xa5YT1XVcN7aENl5wmLonEeBEc73Y9F/1oZVGSKxzT9\r\npbEsiu7I9h9SL7YL8qyAB2Xibc1u34aCSAWgpOmH42/u04n7oTtVKmIHZiIP\r\nH/7sQH56Q2givI6FUFOwFiHSL2kqFdJ6fZ7zG33OvlZC4XEpR4QzBmdawd5Q\r\n01S7RuOVUr1Hf4r1N+gX+ray1LJcqqD1cmK0/JaZWm/trMXogm1vi8oAtvPi\r\ntnf5BxrtfJNGtn2pH5FvXYIBihdj7vYs8GBYD1QoOsZIlf5sMz51FSwbXhYl\r\ndLboV5CP3PQTvqYWZknUj+hM4SgNqE11pYl7TdlvqgKwShe+S9aa2reI4fCn\r\nKjYW+TOoMq4O6rP+bEvS6FFMLkZzNF5CVsY=\r\n=HRpn\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.7.0_1676751583380_0.5734806690039207"},"_hasShrinkwrap":false},"0.8.0-next.1":{"name":"alge","version":"0.8.0-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.28.0","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.36","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.1","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.53.0","@typescript-eslint/parser":"5.53.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.29.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n> Hey 👋, FYI here are some other TypeScript-first libraries I've created that might interest you:\n>\n> [`@molt/command`](https://github.com/jasonkuhrt/molt/tree/main/packages/@molt/command) for building simple scripts and command lines.\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\nYou can see some examples in action [here](./examples/Match.ts).\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag property. The tag property can be any of the following. The first one found in the following order is used. so for example if both `_kind` and `type` are present then `_kind` is considered the tag property.\n\n- `__typename` (this is helpful if you're working with GraphQL unions)\n- `_tag`\n- `_type`\n- `_kind`\n- `type`\n- `kind`\n\nYou call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"9f17206cfba0ab7a822b50dd55f7af9f226b7ce5","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.8.0-next.1","_nodeVersion":"16.19.1","_npmVersion":"8.19.3","dist":{"integrity":"sha512-E8pv9S5yJJYK8WbYv3vcpgAhD794W0w061gAGnm4W6moIM7AxKHlIdsDGdU6dy4gav1sSXqZGAcDYmhY5s9Riw==","shasum":"4f60123b14e69c0e0085822a18bb9d8dc540d748","tarball":"https://registry.npmjs.org/alge/-/alge-0.8.0-next.1.tgz","fileCount":220,"unpackedSize":306793,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBXDCIuvstgnjKUMJtSxjF3UtqKTcfJWy2QGOU/SrL5/AiEA2WtrnTaa7Tc769iaUf7gafHpjzv5zRBfk2E1ca92EjQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj+lJsACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoeRBAAiaD52vMVwyobH3z9vmRYB5DIEJEIct4pITbO8QEw1xXyDNBM\r\nCkEdvuuQq5+fmW1Zfqw3TBmKiFnCA8J3FqMTqxQSdSGMwup11t2+5t41cJpq\r\neEM4RLHwcahTYcRCipaT8jQNgE/yAvFd5J76zsDPgHS6jPyvEayEzzZofCA2\r\nMzRfztkGGJ2K5FS1Lii0+mj5zXsO2aBOKBMXILmSQQrSM2gIiztV55v9wBI6\r\ntLOksODZ3pjBfsGJqi0+UfOm/Gwz7JbFJCzovyALCDuw2U5tF0dvDARd/52T\r\nImyMNpMFik9u3Vy0ZpBC5fCtf3uVOAWR7qpXulc65xTB8o85IkuLBonO9nRQ\r\nXWZRgRukiGsOT1O4H4qqvJNOTOmzNZgQfDvLx7AK99nwiU9COIza2d0CRvLa\r\nirVYLNbjUSBLbwOMY+3/2IxUPxhcmo1+pNycCA+K0lQKm9+F3OTI1iFKY9w2\r\nFomYm0MBieMNwMAq+YCxbhRYvOO3HcGOFB+NWhhQcMetWDFZg6vqjWQ3i4pF\r\nSSM60a5E2o6+y1dmh3QhOLafTtzCsytrdyelDtm7n+CAzQDIQ9h7WyVNYtCb\r\nJK4HFcuFdUMz2Ryf9rs8CfkKMXDpTvcKHWbfSA8fu6IKhJ3YJyyQhE6uY9qr\r\nRr2XDUWlT3opb8JUM/JRJpZZMWJju2NQX+A=\r\n=lje8\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.8.0-next.1_1677349483871_0.1253950000046702"},"_hasShrinkwrap":false},"0.8.0":{"name":"alge","version":"0.8.0","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.28.0","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.36","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.1","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.53.0","@typescript-eslint/parser":"5.53.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.29.1"},"gitHead":"9f17206cfba0ab7a822b50dd55f7af9f226b7ce5","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.8.0","_nodeVersion":"16.19.1","_npmVersion":"8.19.3","dist":{"integrity":"sha512-cpbE2Z1fU3qNoy2srv1K7kUXLBYEGO4Cc4WEgVQTOYu4X+3s/DK+6Z5t0MPhJcCTrcvl2wzKUTDnz1ViUdQ8qg==","shasum":"3c3725be2dffe4f4e19add1fac6630093be7c344","tarball":"https://registry.npmjs.org/alge/-/alge-0.8.0.tgz","fileCount":220,"unpackedSize":306786,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICyP9Ko6SWjF+gvLLryzb1AQMNdVWCX7neKA7Wo/7ZYcAiEA9LqAX/pTgu0PMTr1Ro8GVl5Rp1NO9HE7PQyudhJA85c="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj+lMSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq2KhAAoLbc3Z7pOzNXe4ZkoxUpG/qGxfSKW6Z2FTWmoSoNb+byZuYT\r\n9kM2eBtuOY3gWrtTWZFvz/JE2/nkjw4285I4j92w1zHeuNWbf9595diAI53O\r\nEOFyWsSkTFHnRjw9jKT1AOidsHDSWz7xdkHO7LQ9mg+77jdmhdEk5y38xZ1+\r\nU7RT9zuTxLTcn01ZLF4+aEEVJgce5KH0fuqLIVNxFPRE/N6M0RvAVKdHc1wX\r\nqtvwKUK0n+/O8ZLfOH61EUEUbTQqE1BuJdiM5be03E59/Vmy5njgt9u61DdQ\r\nXIl4zQjZDCPzjdXt25UgsvU9oB4OQEhThPhSyxb+8HdhDnBeBMTvSwNIq0Ou\r\nA6BelnxQuk+tAP/npZ3Hq/Qtb7OCQSEdByV/YsOfW2PJ0OmLoMHjeSJDj0w1\r\n1eKRu+F5V+zxpnEzmfrfy0PJ5QmhVt4U1eCZQlLLWA4J/ezAW36hj3wXetKi\r\nSOWFY++eXatYurLUHfJeABgLEEOMk3U5HwvpmpuveBXVsIVi/ZXvMtdZalD+\r\newI7m4jMprrQN4ndifj+8elMFitno+mdJbV2hhnB44MIkUqnsfiViWf6hfta\r\n2UMnGaLymKJJ2T3QC5VlMwa4fYG/kq9NAmacG3BhnA7usTX2x3npoLXjL0L/\r\n1ps8GMi2gE8wUXtpZkNTsHZ3C04JthqrDw0=\r\n=eVcF\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.8.0_1677349650486_0.44728927572585286"},"_hasShrinkwrap":false},"0.8.1-next.1":{"name":"alge","version":"0.8.1-next.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.28.0","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.36","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.1","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.53.0","@typescript-eslint/parser":"5.53.0","dripip":"0.10.0","eslint":"8.34.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.29.1"},"readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n> Hey 👋, FYI here are some other TypeScript-first libraries I've created that might interest you:\n>\n> [`@molt/command`](https://github.com/jasonkuhrt/molt/tree/main/packages/@molt/command) for building simple scripts and command lines.\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\nYou can see some examples in action [here](./examples/Match.ts).\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag property. The tag property can be any of the following. The first one found in the following order is used. so for example if both `_kind` and `type` are present then `_kind` is considered the tag property.\n\n- `__typename` (this is helpful if you're working with GraphQL unions)\n- `_tag`\n- `_type`\n- `_kind`\n- `type`\n- `kind`\n\nYou call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","gitHead":"998ab883d2d9d1f968d0a9e7b52d407f574ec9fd","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.8.1-next.1","_nodeVersion":"16.19.1","_npmVersion":"8.19.3","dist":{"integrity":"sha512-W/5ehu9DyHmMzvarOkBuS56Bh8Kwsg8TkN0bHKxw+Pg76LiBFPdpV8LpFwmzONXar/ZKbsCYz8BXYV82uhdWNw==","shasum":"32069d7c89ddb148425e8915ad7b873b71f470c0","tarball":"https://registry.npmjs.org/alge/-/alge-0.8.1-next.1.tgz","fileCount":220,"unpackedSize":307867,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBuAd2hRSpEKQGbjNC2e/EfdSw+tXT66vT4uaVStSIFjAiEA/QZ029EKNk8nn1y6s3so4v1p94WgFDnsQiAeHh/B7rk="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj+4CTACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpEIBAAnhvqyumprk1EXKqKtewthXwRmAYL/koj1nVc9iRVvaM1kT6+\r\nZpUoeMD4e6PnuiWMU9+I0M0hFQI3K8Yv8AJnh2yemOejAvjjFcUdBcXM7pqS\r\nz8EdSCu/lszB6ysayksdB6IY/5NMkUQe0hPSznJpwvI7lwsUxnLnoUeuemAo\r\nBUAbA5lWGi0/28QtsV1P3lJkk6zl6NBmsldNm+B356acw1sbjo7fqa6By7+s\r\nWlyE29CEORSo6LS3pqDM893a5lrJErdmYaQ62RRhoqSryFRTBDubRNtCBNjy\r\n0YNpM0kLpb7tQ7Kw/ChoLTqeJI4coMoq5HlnCu//sJkRSR8gYkHnJpuiLBpN\r\nRH7O51L85cjTbP5jcVcZIu3quF+9HYt3DsiVwDtiHWU76sB7TsAyiiurnLaf\r\n2FzA4Ej1g6psY8Vgf0lHH1NKKsWr0J8XDFacUFSJsMhlMUzQV9xasJLQuN8P\r\nt8NTrqn55MzMN7D68taaYAIOAhKbhdbx/owtEYTaJNcUjBxlAPlEhYddv8F+\r\nha0s6NUKPXJNQI37tHgra88R5hSilQRT55vrgtMEs6KtkH0NTTh14g5VX5BO\r\n+ho518dUlV2p46IijqgzNdgmsHgBRLQbvlndw2pRUaNsDTCdA6kM2kS8nOji\r\nASZbRvhqCvtJ4oa1SxfbT162/f8ZqP1qHSk=\r\n=brVE\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.8.1-next.1_1677426835113_0.09603717121665034"},"_hasShrinkwrap":false},"0.8.1":{"name":"alge","version":"0.8.1","repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"packageManager":"pnpm@7.28.0","license":"MIT","type":"module","types":"./build/esm/index.d.ts","exports":{".":{"require":{"types":"./build/cjs/index.d.ts","default":"./build/cjs/index.js"},"import":{"types":"./build/esm/index.d.ts","default":"./build/esm/index.js"}},"./types":{"require":{"types":"./build/cjs/entrypoints/types.d.ts","default":"./build/cjs/entrypoints/types.js"},"import":{"types":"./build/esm/entrypoints/types.d.ts","default":"./build/esm/entrypoints/types.js"}}},"scripts":{"ts-node":"ts-node-esm","build:toc":"markdown-toc README.md -i --maxdepth 4 && prettier --write README.md","format":"prettier --write .","lint":"eslint . --ext .ts,.tsx --fix","check:format":"prettier --check .","check:lint":"eslint . --ext .ts,.tsx --max-warnings 0","check:types":"pnpm tsc --noEmit","dev":"pnpm clean && tsc --build --watch tsconfig.cjs.json tsconfig.esm.json","build":"pnpm clean && pnpm build:cjs && pnpm build:esm","build:cjs":"pnpm tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > build/cjs/package.json","build:esm":"tsc --project tsconfig.esm.json","test":"vitest","clean":"tsc --build --clean && rm -rf build","release:pr":"dripip pr","release:canary":"dripip preview","release:stable":"dripip stable","prepack":"pnpm build"},"dependencies":{"lodash.ismatch":"^4.4.0","remeda":"^1.0.0","ts-toolbelt":"^9.6.0","zod":"^3.17.3"},"devDependencies":{"@prisma-labs/prettier-config":"0.1.0","@swc/core":"1.3.36","@swc/helpers":"0.4.14","@tsconfig/node16-strictest":"1.0.4","@types/lodash.ismatch":"4.4.7","@types/node":"18.14.1","@types/semver":"7.3.13","@types/semver-utils":"1.1.1","@typescript-eslint/eslint-plugin":"5.53.0","@typescript-eslint/parser":"5.53.0","dripip":"0.10.0","eslint":"8.35.0","eslint-config-prettier":"8.6.0","eslint-plugin-codegen":"0.17.0","eslint-plugin-deprecation":"1.3.3","eslint-plugin-only-warn":"1.1.0","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-simple-import-sort":"10.0.0","eslint-plugin-tsdoc":"0.2.17","execa":"7.0.0","fast-glob":"3.2.12","fs-jetpack":"5.1.0","markdown-toc":"1.2.0","prettier":"2.8.4","semver":"7.3.8","semver-utils":"1.1.4","ts-node":"10.9.1","tsd":"0.25.0","typescript":"4.9.5","vitest":"0.29.1"},"gitHead":"2f1fcca9216e3411ea813fd90e7a0f6243a7905c","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"},"homepage":"https://github.com/jasonkuhrt/alge#readme","_id":"alge@0.8.1","_nodeVersion":"16.19.1","_npmVersion":"8.19.3","dist":{"integrity":"sha512-kiV9nTt+XIauAXsowVygDxMZLplZxDWt0W8plE/nB32/V2ziM/P/TxDbSVK7FYIUt2Xo16h3/htDh199LNPCKQ==","shasum":"f3d8a9ceecaf6d56981ecb2a4804a0e6dd7f4e6b","tarball":"https://registry.npmjs.org/alge/-/alge-0.8.1.tgz","fileCount":220,"unpackedSize":307860,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBMRK4L41KQUzlu6ol0khwjIMfVMldUTRXlUCWLuWoRDAiEAgcOH9mKjZ0X6Cze5i4CpDWRYDs+EWnxCYw8p9efU5qQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJj+4uNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoMKA//YRmRS/b9zL/8X01J6yunICKiLfCIC7Z14dJteP1rNBY7wQOv\r\n9gFjSdkiMIWKJSB/ynZc1q/mqppY18ICwcxJ4PJ+TuBRY9cV9lpxJI+h+E1E\r\nXPN+h5PpmBgfiy+0EHk2dh/yqUCyHw4EMSCFQ4Cm9OM6SkF5Q1Q3qZ5BPQfZ\r\nwXP9cIRtvqnatc+gMS4lZyeEVkSypHBg1ZZqDhlFdDldgQ/tyx7x1Id7qNDj\r\n+3MVec/XpHfQ1okdM+lNZh750ot7XZQaOJFkvdyw8XbXTRRZSv+k81XjPYTc\r\nfSgeRa3mfsZoB9Lg9V/FC73GW3+RbYdC11dNFVbc+BTuXbRXgSAyMSJoj9hw\r\nypRgRGTPjPa6dQcM6froZtmouUBEK+jCKbs5KeHm4aWAsX3D/ELGw6cKUTsm\r\n9lT2efqsrijdauoI7F3VGnqOXDigN8UWBriWfMEo4ve9SOTKZ6hDSGhaEdeL\r\nNoaV6COZ2SxOJ6QHeEX4MdQ5RM3PUvydATG5BnruHxTy3zXz+yQcO2T23OMa\r\nZ2RrPGmEubUFzAssYTcSZPEZpf7xlMhtUAh3PMzteDGyElnELa8EIm93R1ZY\r\n8pAKh8mxeJDSUJI7g3TgVzSR8Ku6v/pa9f4ydPeeMMnPp3tqH4FZnnYQP4rj\r\nqfW0kRM9hpD9CONiwqIE7/6gvsnMcjYjPSs=\r\n=W7uJ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"},"directories":{},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alge_0.8.1_1677429644867_0.37573585500460704"},"_hasShrinkwrap":false}},"time":{"created":"2022-03-27T02:36:53.553Z","0.0.1-next.1":"2022-03-27T02:36:53.747Z","modified":"2023-02-26T16:40:46.604Z","0.1.0-next.1":"2022-03-27T03:04:18.392Z","0.1.0-next.2":"2022-03-27T03:25:54.478Z","0.1.0-next.3":"2022-03-27T03:42:07.976Z","0.1.0-next.4":"2022-03-27T12:02:06.336Z","0.1.0-next.5":"2022-03-27T12:32:26.234Z","0.1.0-next.6":"2022-03-28T13:33:46.438Z","0.1.0-next.7":"2022-03-29T00:36:06.176Z","0.1.0-next.8":"2022-04-02T16:09:37.842Z","0.1.0-next.9":"2022-04-14T01:16:04.653Z","0.1.0-next.10":"2022-04-14T12:43:15.309Z","0.1.0-next.11":"2022-04-14T21:44:44.346Z","0.1.0-next.12":"2022-04-14T22:19:00.824Z","0.1.0-next.13":"2022-04-16T14:38:45.878Z","0.1.0-next.14":"2022-04-18T00:51:43.796Z","0.1.0-next.15":"2022-04-18T01:45:45.788Z","0.1.0-next.16":"2022-04-18T20:09:52.892Z","0.1.0-next.17":"2022-04-23T12:07:39.511Z","0.1.0-next.18":"2022-04-23T22:50:21.520Z","0.1.0-next.19":"2022-04-24T02:17:43.668Z","0.1.0-next.20":"2022-04-24T02:29:21.032Z","0.1.0-next.21":"2022-04-24T03:35:06.377Z","0.1.0-next.22":"2022-04-29T03:30:02.387Z","0.1.0-next.23":"2022-04-30T05:25:57.438Z","0.1.0-next.24":"2022-04-30T17:25:31.134Z","0.1.0-next.25":"2022-05-01T12:40:59.905Z","0.1.0-next.26":"2022-05-21T23:29:14.911Z","0.1.0-next.27":"2022-05-26T00:30:18.392Z","0.1.0-next.28":"2022-07-31T00:53:40.630Z","0.1.0":"2022-07-31T00:59:47.415Z","0.1.1-next.1":"2022-08-01T02:09:13.156Z","0.1.1":"2022-08-01T02:23:37.292Z","0.2.0-next.1":"2022-08-02T22:19:40.636Z","0.2.0-next.2":"2022-08-02T23:38:24.019Z","0.2.0-next.3":"2022-08-03T03:31:39.628Z","0.2.0-next.4":"2022-08-04T03:16:06.274Z","0.2.0-next.5":"2022-08-05T00:44:33.931Z","0.2.0-next.6":"2022-08-05T01:10:18.572Z","0.2.0":"2022-08-05T01:54:39.917Z","0.3.0-next.1":"2022-08-06T21:38:48.992Z","0.3.0":"2022-08-06T21:48:33.980Z","0.4.0-next.1":"2022-08-07T10:44:00.398Z","0.4.0-next.2":"2022-08-07T10:59:22.543Z","0.4.0":"2022-08-07T11:01:22.041Z","0.4.1-next.1":"2022-08-20T22:03:23.633Z","0.4.1":"2022-08-20T22:16:35.567Z","0.4.2-next.1":"2022-08-24T03:09:56.390Z","0.4.2-next.2":"2022-08-24T03:11:07.520Z","0.4.2":"2022-08-24T03:12:50.951Z","0.4.3-next.1":"2022-08-30T02:18:33.485Z","0.4.3":"2022-08-30T02:37:59.823Z","0.5.0-next.1":"2022-08-30T03:14:37.619Z","0.5.0":"2022-08-30T03:19:09.358Z","0.5.1-next.1":"2022-09-05T18:50:21.180Z","0.5.1-next.2":"2022-09-05T21:54:14.378Z","0.5.1-next.3":"2022-09-05T22:00:54.104Z","0.5.1-next.4":"2022-09-05T22:02:22.461Z","0.5.1":"2022-09-05T22:22:24.544Z","0.6.0-next.1":"2022-09-06T22:03:41.606Z","0.6.0":"2022-09-06T22:05:30.911Z","0.6.1-next.1":"2022-09-08T15:54:01.736Z","0.6.1":"2022-09-08T15:58:19.907Z","0.7.0-next.1":"2022-09-24T13:17:19.817Z","0.7.0-next.2":"2023-02-18T19:55:51.641Z","0.7.0-next.3":"2023-02-18T20:05:08.823Z","0.7.0-next.4":"2023-02-18T20:15:59.040Z","0.7.0":"2023-02-18T20:19:43.585Z","0.8.0-next.1":"2023-02-25T18:24:44.073Z","0.8.0":"2023-02-25T18:27:30.699Z","0.8.1-next.1":"2023-02-26T15:53:55.298Z","0.8.1":"2023-02-26T16:40:45.085Z"},"maintainers":[{"name":"jasonkuhrt","email":"jasonkuhrt@me.com"}],"repository":{"type":"git","url":"git+ssh://git@github.com/jasonkuhrt/alge.git"},"author":{"name":"Jason Kuhrt"},"license":"MIT","readme":"# alge 🌱\n\n[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml)\n[![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)\n\n> Hey 👋, FYI here are some other TypeScript-first libraries I've created that might interest you:\n>\n> [`@molt/command`](https://github.com/jasonkuhrt/molt/tree/main/packages/@molt/command) for building simple scripts and command lines.\n\n## TL;DR\n\nLibrary for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) in TypeScript. Pronounced \"AL GEE\" like [the plant](https://en.wikipedia.org/wiki/Algae) ([or whatever it is](https://www.indefenseofplants.com/blog/2018/2/20/are-algae-plants)). Schemas powered by [Zod](https://github.com/colinhacks/zod) <3.\n\nAn ADT is built like so:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Length = z.number().positive()\n\n//           o---------- ADT Controller\n//           |            o--------- ADT Builder\nexport const Shape = Alge.data(`Shape`, {\n  Rectangle: {\n    width: Length,\n    height: Length,\n  },\n  Circle: {\n    radius: Length,\n  },\n  Square: {\n    size: Length,\n  },\n})\n```\n\nBuilding an ADT returns a _controller_. Controllers are an API for your data, like constructors and type guards. Constructed data is nothing special, just good old JavaScript POJOs.\n\n```ts\n//    o--------- Member Instance\n//    |        o--------- ADT Controller\n//    |        |     o-------- Member Namespace\n//    |        |     |      o-------- Constructor\n//    |        |     |      |\nconst circle = Shape.Circle.create({ radius: 50 })\n// { _tag: 'Circle', radius: 50 }\n\nconst square = Shape.Square.create({ size: 50 })\n// { _tag: 'Square', size: 5 }\n\nif (Shape.Circle.is(circle)) {\n  console.log(`I Am Circle`)\n}\n\nconst circleForTheOutsideWorld = Shape.Circle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 50 }'\n\nconst squareFromTheOutsideWorld = Shape.Square.from.json({ _tag: 'Square', size: 10 })\n// { _tag: 'Square', size: 10 }\n```\n\nYou can infer the static types from the controller:\n\n```ts\ntype Shape = Alge.infer<typeof Shape>\n```\n\nYou can pattern match on your constructed data:\n\n```ts\nconst shape = Math.random() > 0.5 ? circle : square\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\nYou can create individual records when you don't need full blown ADTs:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n\nconst Circle = Alge.record(`Circle`, { radius: z.number().positive() })\n```\n\nThis is just a taster. Places you can go next:\n\n1. [Install](#installation) and learn interactively (JSDoc is coming soon!)\n1. A formal [features breakdown](#features)\n1. [Code examples](/examples)\n1. A simple [introduction to Algebraic Data Types](#about-algebraic-data-types) (for those unfamiliar)\n1. A [video introduction](https://youtu.be/fLlVQSJx4AU) if you like that format\n\n   [![Video Cover](docs/assets/cover.jpg)](https://youtu.be/JWvy7JXE6vw)\n\n## Contents\n\n<!-- toc -->\n\n- [Installation](#installation)\n- [Roadmap](#roadmap)\n- [Features At a Glance](#features-at-a-glance)\n- [About Algebraic Data Types](#about-algebraic-data-types)\n  - [What?](#what)\n  - [Why?](#why)\n\n* [Features](#features)\n  - [Records](#records)\n    - [Definition (`.record`)](#definition-record)\n    - [Construction (`.create`)](#construction-create)\n    - [Input Defaults](#input-defaults)\n    - [Input Transformation](#input-transformation)\n    - [Input Validation](#input-validation)\n    - [Update](#update)\n    - [Metadata](#metadata)\n    - [Chaining API](#chaining-api)\n    - [Codecs](#codecs)\n      - [Definition (`.codec`)](#definition-codec)\n      - [Usage (`.to.`, `.from`)](#usage-to-from)\n      - [Built In JSON](#built-in-json)\n      - [OrThrow Decoders](#orthrow-decoders)\n  - [Data (Algebraic Data Types)](#data-algebraic-data-types)\n    - [Definition](#definition)\n      - [Referencing Records](#referencing-records)\n      - [Inline Records](#inline-records)\n      - [Referencing Zod Objects](#referencing-zod-objects)\n    - [Construction](#construction)\n    - [Chaining API](#chaining-api-1)\n    - [Identity (`.is`, `.is$`)](#identity-is-is)\n    - [Codecs](#codecs-1)\n      - [Definition (`.codec`)](#definition-codec-1)\n      - [Usage (`to`, `from`)](#usage-to-from)\n  - [Static Types](#static-types)\n    - [Namespaces](#namespaces)\n  - [String Literal Union Pattern Matching](#string-literal-union-pattern-matching)\n    - [Tag Matchers](#tag-matchers)\n    - [Done Versus Else](#done-versus-else)\n  - [ADT Pattern Matching](#adt-pattern-matching)\n    - [Tag Matchers](#tag-matchers-1)\n    - [Value Matchers](#value-matchers)\n    - [Mixing Matchers](#mixing-matchers)\n    - [Done Versus Else](#done-versus-else-1)\n\n<!-- tocstop -->\n\n## Installation\n\n```\nnpm add alge\n```\n\n## Roadmap\n\nThere is no timeline but there are priorities. Refer to the currently three [pinned issues](https://github.com/jasonkuhrt/alge/issues).\n\n## Features At a Glance\n\n- Use a \"builder\" API to define ADTs\n  - Use Zod for schema definition\n  - Define one or more codecs\n- Use the \"controller\" API to work with data\n  - Constructors\n  - Type guards\n  - Built in JSON codec\n  - Automatic ADT level codecs (for codecs common across members)\n- Pattern match on data\n  - Use tag matchers\n  - Use value matchers\n\n## About Algebraic Data Types\n\nAlge is a Type Script library for creating [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) (ADTs). This guide will take you from not knowing what ADTs are to why you might want to use Alge for them in your code.\n\n### What?\n\nAlgebraic Data Types (ADTs for short) are a methodology of modelling data. They could appear in any context that is about defining and/or navigating the shape of data. One of their fundamental benefits is that they can express different states/inrecords/facts about/of data. They are the combination of two other concepts, _product types_ and _union types_.\n\nA product type is like:\n\n```ts\ninterface Foo {\n  bar: string\n  qux: number\n}\n```\n\nA union type is like:\n\n```ts\ntype Foo = 1 | 2 | 3\n```\n\nBasically, when the power of these two data modelling techniques are combined, we get something far greater than the sum of its parts: ADTs.\n\nADTs can particularly shine at build time. While dynamically typed programing languages (\"scripting language\", e.g. Ruby, JavaScript, Python, ...) can support ADTs at runtime, adding static type support into the mix increases the ADT value proposition. Then there are yet other more minor programing language features like pattern matching that if supporting ADTs make them feel that much more beneficial too.\n\nReferences:\n\n- [Wikipedia entry on ADTs](https://en.wikipedia.org/wiki/Algebraic_data_type)\n- [Type Script documentation on discriminated unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)\n\n### Why?\n\nNow that we have some understanding of _what_ ADTs are let's build some understanding about _why_ we might want to use them. To do this we'll work with an example.\n\nLet's say we want to accept some user input about an npm package dependency version pin. It might come in the form of an exact version or a range of acceptable versions. How would we model this? Let's start without ADTs and then refactor with them to appreciate the difference. Let's assume that input parsing has been taken care of and so here we're only concerned with structured data modelling.\n\n```ts\ninterface Pin {\n  isExact: boolean\n  patch?: number\n  minor?: number\n  major?: number\n  release?: string\n  build?: string\n  range?: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nThis data modelling is flawed. There is out-of-band information about important data relationships. `release` and `build` are legitimately optional properties but `range` `patch` `minor` `major` all depend on the state of `isExact`. When `true` then `range` is undefined and the others are not, and vice-versa. In other words these configurations of the data are impossible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\nWhile these are possible:\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n}\n```\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  release: `beta`,\n}\n```\n\n```ts\nconst pin = {\n  isExact: false,\n  range: [\n    {\n      operator: `~`,\n      patch: 1,\n      minor: 0,\n      major: 0,\n    },\n  ],\n}\n```\n\nBut since our data modelling doesn't encode these _facts_ our code suffers. For example:\n\n```ts\nif (pin.isExact) {\n  doSomething(pin.major!)\n  //                       ^\n}\n```\n\nNotice the `!`. Its us telling Type Script that `major` is definitely not undefined and so the type error can be ignored. In JS its even worse, as we wouldn't even be prompted to think about such cases, unless we remember to. Seems trivial in this case, but at scale day after day often with unfamiliar code a mistake will inevitably be made. Another approach could have been this:\n\n```ts\nif (pin.isExact) {\n  if (!pin.major) throw new Error(`Bad pin data!`)\n  doSomething(pin.major)\n}\n```\n\nSo, poor data modelling affects the quality of our code by our code either needing to deal with apparently possible states that are actually impossible OR by our code carefully ignoring those impossible states. Both solutions are terrible because they make code harder to read. There is more code, and the chance that wires about impossible and possible states will cross becomes a real possibility leading to potential runtime errors.\n\nADTs solve this. Let's refactor our Pin type into an ADT to see how!\n\n```ts\ntype Pin = ExactPin | RangePin\n\ninterface ExactPin {\n  tag: `ExactPin`\n  patch: number\n  minor: number\n  major: number\n  release?: string\n  build?: string\n}\n\ninterface RangePin {\n  tag: `RangePin`\n  values: Array<{\n    operator: `~` | `>=` | `...` // etc.\n    isExact: boolean\n    patch: number\n    minor: number\n    major: number\n    release?: string\n    build?: string\n  }>\n}\n```\n\nNow we've encoded the possible states we cared about. Our code quality increases:\n\n```ts\nif (pin.tag === 'ExactPin') {\n  doSomething(pin.major) // No problem, `pin` has been narrowed from `Pin` to `ExactPin`!\n}\n```\n\nWhen a developer deals with values of `Pin` type they will have an immediately much better understanding of the possible states.\n\nIn fact every optional property in some data represents possibly different state representations and thus potentially a use case for an ADT. So for example we could go further with our above data modelling and define things like `ExactPreReleasePin` and `ExactPreReleaseBuildPin`:\n\n```ts\ninterface ExactPreReleasePin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n}\n```\n\n```ts\ninterface ExactPreReleaseBuildPin {\n  tag: `ExactPreReleasePin`\n  patch: number\n  minor: number\n  major: number\n  release: string\n  build: string\n}\n```\n\nOf course like any technique there is a point where ADT modelling is probably overkill for your use-case. That said, that line might be further out than you think. For example while the above might seem excessive, it actually answers a question the previous data modelling left ambiguous which is the question of, is the following state possible?\n\n```ts\nconst pin = {\n  isExact: true,\n  patch: 1,\n  minor: 2,\n  major: 3,\n  build: `5`,\n}\n```\n\nThe answer is no! But without the ADT that _fact_ would have to managed by humans, rather than the machine.\n\nAt scale, having well modelled data can be a life saver. The up front verbosity pays dividends downstream for all the impossible branches removed from programs' possibility space. ADTs help you (or your consumers) focus on what _can actually happen_.\n\n</br></br>\n\n# Features\n\nAll code blocks below assume these imports:\n\n```ts\nimport { Alge } from 'alge'\nimport { z } from 'zod'\n```\n\n## Records\n\n### Definition (`.record`)\n\nUse the Record Builder to define a record. At a minimum you specify the name and schema. Names should be in [pascal case](https://techterms.com/definition/pascalcase) to avoid name collisions with the Alge API (see below).\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive(),\n})\n```\n\nIf you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle', CircleSchema)\n```\n\n### Construction (`.create`)\n\nOnce you've defined a record with the Record Builder you get back a Record Controller. Use it to create instances of your record:\n\n```ts\nconst circle = Circle.create({ radius: 10 })\n// { _tag: 'circle', radius: 10 }\n```\n\nThe `_tag` property is present to track the name of your record. you normally shouldn't have to interact directly with it.\n\n### Input Defaults\n\nLeverage Zod to get defaults for your properties ([zod docs](https://github.com/colinhacks/zod#default)):\n\n```ts\nconst Circle = Alge.record('Circle', {\n  radius: z.number().positive().default(0),\n})\n\nconst circle = Circle.create()\n// { _tag: 'circle', radius: 0 }\n```\n\n### Input Transformation\n\nYou can use zod to perform input transformations:\n\n```ts\nconst Url = Alge.record('Url', {\n  // ...\n  path: z\n    .string()\n    .optional()\n    .transform((path) => (path === undefined ? '/' : path.trim() === '' ? '/' : path.trim())),\n})\n```\n\n### Input Validation\n\nInput is validated via Zod. For example a negative number where only positives are accepted.\n\n```ts\nconst circle = circle.create({ radius: -10 })\n// throws\n```\n\n### Update\n\nYou can update records. Updating creates shallow copies of data. The validation, transformations, defaults etc. setup on the zod schema will re-run on the update function ensuring data integrity. Any errors there will be thrown.\n\n```ts\nconst circleUpdated = circle.update(circle, { radius: 5 })\n```\n\n### Metadata\n\nThe controller gives you access to metadata about your record:\n\n```ts\ncircle.name // 'Circle'\ncircle.schema // a Zod schema instance\n```\n\n### Chaining API\n\nThere is a chaining API available which is more verbose but also affords more features (see further down).\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive(),\n  })\n  .done()\n```\n\nLike in the shorthand form, if you already have a Zod Object defined from somewhere else you can just pass it in:\n\n```ts\nconst Circle = Alge.record('Circle').schema(CircleSchema).done()\n```\n\n### Codecs\n\n#### Definition (`.codec`)\n\nYou can define a named codec which allows your record to be encoded to/decoded from another representation.\n\nThe encoder (`to`) transforms your record into a string.\n\nThe decoder (`from`) transforms a string into your record, or `null` if the string is invalid.\n\n```ts\nconst Circle = Alge.record('Circle')\n  .schema({\n    radius: z.number().positive().default(1),\n  })\n  .codec('graphic', {\n    //    ^[1]\n    to: (circle) => `(${circle.radius})`,\n    from (string) => {\n      const match = string.match(^/\\((\\d+)\\)/$)\n      return match ? { radius: radius[1]! } : null\n    //               ^[2]\n    }\n  })\n  .done()\n```\n\nNotes:\n\n1. We give our codec a _name_. This name is used for the derived API (see \"usage\" below).\n2. When returning the parsed data for our record we do _not_ need to deal with the `_tag` property.\n\n#### Usage (`.to.`, `.from`)\n\nCodecs are exposed under the `.from` and `.to` (decoders/encoders) properties:\n\n```ts\nconst circle = Circle.create()\n// { _tag: 'Circle', radius: 1 }\n\nCircle.to.graphic(circle)\n// (1)\n\nCircle.from.graphic(`(1)`)\n// { _tag: 'Circle', radius: 1 }\n\nCircle.from.graphic(`()`)\n// null\n```\n\n#### Built In JSON\n\nAll records have a JSON codec:\n\n```ts\nCircle.to.json(circle)\n// '{ \"_tag\": \"Circle\", \"radius\": 1 }'\n\nCircle.from.json('{ \"_tag\": \"Circle\", \"radius\": 1 }')\n// { _tag: 'Circle', radius: 1 }\n```\n\n#### OrThrow Decoders\n\nAll decoders, JSON or your custom ones, have a variant of decode that will throw an `Error` when decoding fails:\n\n```ts\nCircle.from.graphicOrThrow(`()`)\n// throws\n\nCircle.from.jsonOrThrow(`bad`)\n// throws\n```\n\n## Data (Algebraic Data Types)\n\nThe ADT Builder is an extension of the Record Builder and the ADT Controller it returns is an extension of the Record Controller.\n\n### Definition\n\n#### Referencing Records\n\nRecords can be passes into the Data Builder:\n\n```ts\nconst Circle = Alge.record('Circle', { radius: z.number() })\nconst Square = Alge.record('Square', { size: z.number() })\n\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n#### Inline Records\n\nRecords can also be defined inline:\n\n```ts\nconst Shape = Alge.data('Shape', {\n  Circle: { radius: z.number() },\n  Square: { size: z.number() },\n})\n```\n\n#### Referencing Zod Objects\n\nExisting Zod object schemas are also accepted:\n\n```ts\nconst Circle = z.object({ radius: z.number() })\nconst Square = z.object({ size: z.number() })\nconst Shape = Alge.data('Shape', { Circle, Square })\n```\n\n### Construction\n\nThe ADT Controller contains one Record Controller for every record defined under a property of that records name. Use it just like you did before:\n\n```ts\nconst circle = Shape.Circle.create({ radius: 1 })\n// { _tag: 'Circle', radius: 1 }\nconst square = Shape.Square.create({ size: 2 })\n// { _tag: 'Square', size: 2 }\n```\n\n### Chaining API\n\nAs with records before there is a chaining API for ADTs that is more verbose but has additional features.\n\n```ts\nconst Shape = Alge.data('shape').record(Circle).record(Square).done()\n```\n\nAs with the shorthand your existing Zod objects can be passed in:\n\n```ts\nconst CircleSchema = z.object({ radius: z.number() })\nconst SquareSchema = z.object({ size: z.number() })\nconst Shape = Alge.data('shape')\n  .record('Circle')\n  .schema(CircleSchema)\n  .record('Square')\n  .schema(SquareSchema)\n  .done()\n```\n\n### Identity (`.is`, `.is$`)\n\nUse the `.is` Record Controller method as a TypeScript type guard. It checks if the given value is that record or not:\n\n```ts\nconst onlyCircle = (shape: Shape): null | Shape.Circle => {\n  return Shape.Circle.is(shape) ? shape : null\n}\n```\n\nWhen you're working with unknown values there is the `.$is` method which takes `unknown` input. It is less type safe than `.is` so avoid `.is$` when you can:\n\n```ts\nconst onlyCircle = (someValue: unknown): null | Shape.Circle => {\n  return Shape.Circle.$is(someValue) ? someValue : null\n}\n```\n\n### Codecs\n\nWhen a codec of some name is defined for every record in an ADT then something special happens. The ADT gets access to a generalized version of the codec with these features:\n\n1. A generalized decoder that will take a string and return a record instance of the first record decoder to return non-null. The static type is a union of all the records in the ADT (plus `null`).\n2. A generalized encoder that will dispatch automatically to the correct Record encoder based on the passed in record's `_tag` value.\n\n#### Definition (`.codec`)\n\nHere is an example of defining a custom codec for each record in an ADT.\n\n```ts\nconst circlePattern = /^\\(( *)\\)$/\nconst squarePattern = /^\\[( *)\\]$/\n\nconst shape = Alge.data('Shape')\n  .record(`Circle`)\n  .schema({\n    radius: z.number(),\n  })\n  .codec('graphic', {\n    to: (circle) => `(${' '.repeat(circle.radius)})`,\n    from: (string) => {\n      const match = string.exec(circleString)\n      return match ? { radius: match[1]!.length } : null\n    },\n  })\n  .record(`square`)\n  .schema({\n    size: z.number(),\n  })\n  .codec('graphic', {\n    to: (square) => `[${' '.repeat(square.size)}]`,\n    from: (string) => {\n      const match = squarePattern.exec(string)\n      return match ? { size: match[1]!.length } : null\n    },\n  })\n  .done()\n```\n\n#### Usage (`to`, `from`)\n\n```ts\nconst circle = Shape.Circle.create({ radius: 3 })\n// { _tag: 'circle', radius: 3 }\nconst circleString = Shape.Circle.to.graphic(circle)\n// '(   )'\nconst circle2 = Shape.Circle.from.graphic(circleString)\n// { _tag: 'circle', radius: 3 }\nconst circle3 = Shape.Circle.from.graphic('(]')\n// null\nconst shape1 = shape.from.graphic('()')\n// type: circle | square | null\n// value: { _tag: 'circle', radius: 0 }\nconst shape2 = Shape.from.graphic('[]')\n// type: circle | square | null\n// value: { _tag: 'square', size: 0 }\nconst shape3 = Shape.from.graphic('!')\n// type: circle | square | null\n// value: null\nconst shape4 = Shape.from.graphicOrThrow('!')\n// type: circle | square\n// value: throws\n```\n\nAs mentioned all records have a JSON codec, thus all ADTs have a generalized one.\n\n```ts\nconst circleJson = Shape.to.json(circle)\n// '{ \"_tag\": \"circle\", \"radius\": 50 }'\nconst circle2 = shape.from.json(circleJson)\n// { \"_tag\": \"circle\", \"radius\": 50 }\n```\n\n## Static Types\n\nOften you will write code (e.g. your own functions) that need to be typed with your adt. alge has \"type functions\" for this which leverages typescript inference.\n\nFor adts there is `alge.Infer`. it return an object with a property _per_ record of the adt _as well as_ a special property `*` which is _a union of all records_.\n\n```ts\ntype Shape = Alge.Infer<typeof shape>\n/*\n{\n  Circle: { _tag: 'Circle', radius: number }\n  Square: { _tag: 'Square', size: number }\n  '*':    | { _tag: 'Circle', radius: number }\n          | { _tag: 'Square', size: number }\n}\n*/\n\nconst doSomething = (shape: Shape['*']): null | Shape['circle'] => {\n  // todo\n}\n```\n\nFor lone records there is `alge.InferRecord`.\n\n```ts\ntype Circle = Alge.InferRecord<typeof Circle>\n\nconst doSomething = (circle: Circle) => {\n  // todo\n}\n```\n\n### Namespaces\n\nWhen working with inferred adt types, if you prefer to work with namespaces rather than objects to reference types you can use the following pattern:\n\n```ts\ntype ShapeInferred = Alge.Infer<typeof Shape>\n\ntype Shape = ShapeInferred['*']\n\nnamespace Shape {\n  export type Circle = ShapeInferred['Circle']\n  export type Square = ShapeInferred['Square']\n}\n\nconst doSomething = (shape: Shape): null | Shape.Circle => {\n  // todo\n}\n```\n\n## String Literal Union Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things you can match on unions of string literals. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag matchers\n- Finish with `.done()` to statically verify union exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\n### Tag Matchers\n\nTag Matchers simply branch based on the string literal. You call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst randomThing = pickRandom(['lego', 'basketball', 'videoGame'])\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got Lego!`)\n  .basketball(() => `Got Basketball`)\n  .videoGame(() => `Got video game!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst randomThing = pickRandom(['lego', 'rockClimbing', 'hiking'])\n\nconst sentence = Alge.match(randomThing)\n  .lego(() => `Got a toy!`)\n  .else((thing) => `Got an activity! (${thing})`)\n\nconst maybeLego = Alge.match(randomThing)\n  .lego(() => 'Got Lego!')\n  .else(null)\n```\n\n## ADT Pattern Matching\n\nUse `.match` to dispatch code execution based on data patterns. Among other things You can match on your ADT's variants either by their tag or a data pattern. The flow is:\n\n- Pass your value to `Alge.match` to begin the pattern matching.\n- Chain tag or data (or both) matchers\n- Finish with `.done()` to statically verify variant exhaustiveness or `.else(...)` if you want to specify a fallback value.\n\nYou can see some examples in action [here](./examples/Match.ts).\n\n### Tag Matchers\n\nTag Matchers simply branch based on the variant's tag property. The tag property can be any of the following. The first one found in the following order is used. so for example if both `_kind` and `type` are present then `_kind` is considered the tag property.\n\n- `__typename` (this is helpful if you're working with GraphQL unions)\n- `_tag`\n- `_type`\n- `_kind`\n- `type`\n- `kind`\n\nYou call `.done()` to perform the exhaustiveness check. If you can't call this (because of static type error) then your pattern matching is not exhaustive. This catches bugs!\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Value Matchers\n\nValue Matchers allow you to specify that the branch only matches when the actually data of the variant also matches your criteria.\n\nSince these kinds of matchers are dynamic you cannot use `.done` with them but instead must use `.else` to specify a fallback value in case they do not match.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .else({ ok: true })\n```\n\n### Mixing Matchers\n\nYou can mix matchers. Order matters. More specific matchers must come before more general matchers. Alge automates these checks for you:\n\n- Cannot specify a value matcher _after_ a tag matcher (static & runtime enforcement)\n- Future Feature (#todo-issue)[https://github.com/jasonkuhrt/alge/issues/todo]: Cannot specify a more specific data matcher after a less specific one\n\n```ts\nconst result = Alge.match(shape)\n  .Circle({ radius: 13 }, () => `Got an unlucky circle!`)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .Square({ size: 13 }, () => `Got an unlucky square!`)\n  .Square((square) => `Got a square of size ${square.size}!`)\n  .done()\n```\n\n### Done Versus Else\n\nWhen you don't want to be exhaustive, use `else` instead of `done`. The value you specify in `else` will be used if no matcher matches.\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else(null)\n```\n\nIf you don't want your else to be an eager value, make it lazy with a function. Also this gives you access to the data (with its type statically narrowed):\n\n```ts\nconst result = Alge.match(shape)\n  .Circle((circle) => `Got a circle of radius ${circle.radius}!`)\n  .else((shape) => (Math.random() > 0.5 ? [1, shape] : [2, shape]))\n```\n\n</br></br>\n\n![alt](https://repobeats.axiom.co/api/embed/3c932f1cb76da4ad21328bfdd0ad1c6fbbe76a0b.svg 'repobeats analytics image')\n","readmeFilename":"README.md","description":"[![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) [![npm version](https://img.shields.io/npm/v/alge.svg)](https://www.npmjs.com/package/alge)","homepage":"https://github.com/jasonkuhrt/alge#readme","bugs":{"url":"https://github.com/jasonkuhrt/alge/issues"}}