├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── cli.js ├── funding.yml ├── index.js ├── license ├── package.json ├── readme.md ├── test.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | on: 3 | - pull_request 4 | - push 5 | jobs: 6 | main: 7 | name: ${{matrix.node}} 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: ${{matrix.node}} 14 | - run: npm install 15 | - run: npm test 16 | - uses: codecov/codecov-action@v1 17 | strategy: 18 | matrix: 19 | node: 20 | - lts/hydrogen 21 | - node 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.d.ts 3 | *.log 4 | coverage/ 5 | node_modules/ 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | *.md 3 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import fs from 'node:fs' 3 | import {URL} from 'node:url' 4 | import process from 'node:process' 5 | import {levenshteinEditDistance} from './index.js' 6 | 7 | /** @type {Object.} */ 8 | const pack = JSON.parse( 9 | String(fs.readFileSync(new URL('package.json', import.meta.url))) 10 | ) 11 | 12 | const argv = process.argv.slice(2) 13 | let insensitive = false 14 | let pos = argv.indexOf('--insensitive') 15 | 16 | if (pos !== -1) { 17 | argv.splice(pos, 1) 18 | insensitive = true 19 | } 20 | 21 | pos = argv.indexOf('-i') 22 | if (pos !== -1) { 23 | argv.splice(pos, 1) 24 | insensitive = true 25 | } 26 | 27 | if (argv.includes('--help') || argv.includes('-h')) { 28 | console.log(help()) 29 | } else if (argv.includes('--version') || argv.includes('-v')) { 30 | console.log(pack.version) 31 | } else if (argv.length === 0) { 32 | process.stdin.resume() 33 | process.stdin.setEncoding('utf8') 34 | process.stdin.on('data', function (data) { 35 | getDistance(String(data).trim()) 36 | }) 37 | } else { 38 | getDistance(argv.join(' ')) 39 | } 40 | 41 | function help() { 42 | return [ 43 | '', 44 | ' Usage: ' + pack.name + ' [options] ', 45 | '', 46 | ' ' + pack.description, 47 | '', 48 | ' Options:', 49 | '', 50 | ' -h, --help output usage information', 51 | ' -v, --version output version number', 52 | ' -i, --insensitive ignore casing', 53 | '', 54 | ' Usage:', 55 | '', 56 | ' # output distance', 57 | ' $ ' + pack.name + ' sitting kitten', 58 | ' ' + distance(['sitting', 'kitten']), 59 | '', 60 | ' # output distance from stdin', 61 | ' $ echo "saturday,sunday" | ' + pack.name, 62 | ' ' + distance(['saturday', 'sunday']) 63 | ].join('\n') 64 | } 65 | 66 | /** 67 | * @param {string} value 68 | */ 69 | function getDistance(value) { 70 | const values = value.split(',').join(' ').split(/\s+/) 71 | 72 | if (values.length === 2) { 73 | // @ts-ignore yes, the length is 2. 74 | console.log(distance(values)) 75 | } else { 76 | process.stderr.write(help()) 77 | process.exit(1) 78 | } 79 | } 80 | 81 | /** 82 | * @param {[string, string]} values 83 | * @return {number} 84 | */ 85 | function distance(values) { 86 | return levenshteinEditDistance(values[0], values[1], insensitive) 87 | } 88 | -------------------------------------------------------------------------------- /funding.yml: -------------------------------------------------------------------------------- 1 | github: wooorm 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** @type {Array} */ 2 | const codes = [] 3 | /** @type {Array} */ 4 | const cache = [] 5 | 6 | /** 7 | * Levenshtein edit distance. 8 | * 9 | * @param {string} value 10 | * Primary value. 11 | * @param {string} other 12 | * Other value. 13 | * @param {boolean} [insensitive=false] 14 | * Compare insensitive to ASCII casing. 15 | * @returns {number} 16 | * Distance between `value` and `other`. 17 | */ 18 | export function levenshteinEditDistance(value, other, insensitive) { 19 | if (value === other) { 20 | return 0 21 | } 22 | 23 | if (value.length === 0) { 24 | return other.length 25 | } 26 | 27 | if (other.length === 0) { 28 | return value.length 29 | } 30 | 31 | if (insensitive) { 32 | value = value.toLowerCase() 33 | other = other.toLowerCase() 34 | } 35 | 36 | let index = 0 37 | 38 | while (index < value.length) { 39 | // eslint-disable-next-line unicorn/prefer-code-point 40 | codes[index] = value.charCodeAt(index) 41 | cache[index] = ++index 42 | } 43 | 44 | let indexOther = 0 45 | /** @type {number} */ 46 | let result 47 | 48 | while (indexOther < other.length) { 49 | // eslint-disable-next-line unicorn/prefer-code-point 50 | const code = other.charCodeAt(indexOther) 51 | let index = -1 52 | let distance = indexOther++ 53 | result = distance 54 | 55 | while (++index < value.length) { 56 | const distanceOther = code === codes[index] ? distance : distance + 1 57 | distance = cache[index] 58 | result = 59 | distance > result 60 | ? distanceOther > result 61 | ? result + 1 62 | : distanceOther 63 | : distanceOther > distance 64 | ? distance + 1 65 | : distanceOther 66 | cache[index] = result 67 | } 68 | } 69 | 70 | // @ts-expect-error: always assigned. 71 | return result 72 | } 73 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014 Titus Wormer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "levenshtein-edit-distance", 3 | "version": "3.0.1", 4 | "description": "Levenshtein edit distance", 5 | "license": "MIT", 6 | "keywords": [ 7 | "vladimir", 8 | "levenshtein", 9 | "edit", 10 | "distance", 11 | "bin", 12 | "cli" 13 | ], 14 | "homepage": "https://words.github.io/levenshtein-edit-distance/", 15 | "repository": "words/levenshtein-edit-distance", 16 | "funding": { 17 | "type": "github", 18 | "url": "https://github.com/sponsors/wooorm" 19 | }, 20 | "bugs": "https://github.com/words/levenshtein-edit-distance/issues", 21 | "author": "Titus Wormer (https://wooorm.com)", 22 | "contributors": [ 23 | "Titus Wormer (https://wooorm.com)" 24 | ], 25 | "sideEffects": false, 26 | "type": "module", 27 | "bin": "cli.js", 28 | "main": "index.js", 29 | "types": "index.d.ts", 30 | "files": [ 31 | "cli.js", 32 | "index.d.ts", 33 | "index.js" 34 | ], 35 | "devDependencies": { 36 | "@types/node": "^18.0.0", 37 | "c8": "^7.0.0", 38 | "prettier": "^2.0.0", 39 | "remark-cli": "^11.0.0", 40 | "remark-preset-wooorm": "^9.0.0", 41 | "type-coverage": "^2.0.0", 42 | "typescript": "^4.0.0", 43 | "xo": "^0.52.0" 44 | }, 45 | "scripts": { 46 | "prepack": "npm run build && npm run format", 47 | "build": "tsc --build --clean && tsc --build && type-coverage", 48 | "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", 49 | "test-api": "node --conditions development test.js", 50 | "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", 51 | "test": "npm run build && npm run format && npm run test-coverage" 52 | }, 53 | "prettier": { 54 | "tabWidth": 2, 55 | "useTabs": false, 56 | "singleQuote": true, 57 | "bracketSpacing": false, 58 | "semi": false, 59 | "trailingComma": "none" 60 | }, 61 | "xo": { 62 | "prettier": true 63 | }, 64 | "remarkConfig": { 65 | "plugins": [ 66 | "preset-wooorm" 67 | ] 68 | }, 69 | "typeCoverage": { 70 | "atLeast": 100, 71 | "detail": true, 72 | "strict": true 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # levenshtein-edit-distance 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Size][size-badge]][size] 7 | 8 | [Levenshtein distance][wiki] (by [Vladimir Levenshtein][vlad]). 9 | 10 | ## Contents 11 | 12 | * [What is this?](#what-is-this) 13 | * [When should I use this?](#when-should-i-use-this) 14 | * [Install](#install) 15 | * [Use](#use) 16 | * [API](#api) 17 | * [`levenshteinEditDistance(value, other[, insensitive])`](#levenshteineditdistancevalue-other-insensitive) 18 | * [CLI](#cli) 19 | * [Types](#types) 20 | * [Compatibility](#compatibility) 21 | * [Related](#related) 22 | * [Contribute](#contribute) 23 | * [Security](#security) 24 | * [License](#license) 25 | 26 | ## What is this? 27 | 28 | This package exposes a string similarity algorithm. 29 | That means it gets two strings (typically words), and turns it into the minimum 30 | number of single-character edits (insertions, deletions or substitutions) 31 | needed to turn one string into the other. 32 | 33 | ## When should I use this? 34 | 35 | You’re probably dealing with natural language, and know you need this, if 36 | you’re here! 37 | 38 | ## Install 39 | 40 | This package is [ESM only][esm]. 41 | In Node.js (version 14.14+, 16.0+), install with [npm][]: 42 | 43 | ```sh 44 | npm install levenshtein-edit-distance 45 | ``` 46 | 47 | In Deno with [`esm.sh`][esmsh]: 48 | 49 | ```js 50 | import {levenshteinEditDistance} from 'https://esm.sh/levenshtein-edit-distance@3' 51 | ``` 52 | 53 | In browsers with [`esm.sh`][esmsh]: 54 | 55 | ```html 56 | 59 | ``` 60 | 61 | ## Use 62 | 63 | ```js 64 | import {levenshteinEditDistance} from 'levenshtein-edit-distance' 65 | 66 | levenshteinEditDistance('levenshtein', 'levenshtein') // => 0 67 | levenshteinEditDistance('sitting', 'kitten') // => 3 68 | levenshteinEditDistance('gumbo', 'gambol') // => 2 69 | levenshteinEditDistance('saturday', 'sunday') // => 3 70 | 71 | // Insensitive to order: 72 | levenshteinEditDistance('aarrgh', 'aargh') === levenshtein('aargh', 'aarrgh') // => true 73 | 74 | // Sensitive to ASCII casing by default: 75 | levenshteinEditDistance('DwAyNE', 'DUANE') !== levenshtein('dwayne', 'DuAnE') // => true 76 | // Insensitive: 77 | levenshteinEditDistance('DwAyNE', 'DUANE', true) === levenshtein('dwayne', 'DuAnE', true) // => true 78 | ``` 79 | 80 | ## API 81 | 82 | This package exports the identifier `levenshteinEditDistance`. 83 | There is no default export. 84 | 85 | ### `levenshteinEditDistance(value, other[, insensitive])` 86 | 87 | Levenshtein edit distance. 88 | 89 | ###### `value` 90 | 91 | Primary value (`string`, required). 92 | 93 | ###### `other` 94 | 95 | Other value (`string`, required). 96 | 97 | ###### `insensitive` 98 | 99 | Compare insensitive to ASCII casing (`boolean`, default: `false`). 100 | 101 | ##### Returns 102 | 103 | Distance between `value` and `other` (`number`). 104 | 105 | ## CLI 106 | 107 | ```txt 108 | Usage: levenshtein-edit-distance [options] word word 109 | 110 | Levenshtein edit distance. 111 | 112 | Options: 113 | 114 | -h, --help output usage information 115 | -v, --version output version number 116 | -i, --insensitive ignore casing 117 | 118 | Usage: 119 | 120 | # output distance 121 | $ levenshtein-edit-distance sitting kitten 122 | # 3 123 | 124 | # output distance from stdin 125 | $ echo "saturday,sunday" | levenshtein-edit-distance 126 | # 3 127 | ``` 128 | 129 | ## Types 130 | 131 | This package is fully typed with [TypeScript][]. 132 | It exports no additional types. 133 | 134 | ## Compatibility 135 | 136 | This package is at least compatible with all maintained versions of Node.js. 137 | As of now, that is Node.js 14.14+ and 16.0+. 138 | It also works in Deno and modern browsers. 139 | 140 | ## Related 141 | 142 | * [`levenshtein.c`](https://github.com/wooorm/levenshtein.c) 143 | — C API 144 | * [`levenshtein`](https://github.com/wooorm/levenshtein) 145 | — C CLI 146 | * [`levenshtein-rs`](https://github.com/wooorm/levenshtein-rs) 147 | — Rust API 148 | * [`stemmer`](https://github.com/words/stemmer) 149 | — porter stemming algorithm 150 | * [`lancaster-stemmer`](https://github.com/words/lancaster-stemmer) 151 | — lancaster stemming algorithm 152 | * [`double-metaphone`](https://github.com/words/double-metaphone) 153 | — double metaphone algorithm 154 | * [`soundex-code`](https://github.com/words/soundex-code) 155 | — soundex algorithm 156 | * [`dice-coefficient`](https://github.com/words/dice-coefficient) 157 | — sørensen–dice coefficient 158 | * [`syllable`](https://github.com/words/syllable) 159 | — syllable count of English words 160 | 161 | ## Contribute 162 | 163 | Yes please! 164 | See [How to Contribute to Open Source][contribute]. 165 | 166 | ## Security 167 | 168 | This package is safe. 169 | 170 | ## License 171 | 172 | [MIT][license] © [Titus Wormer][author] 173 | 174 | 175 | 176 | [build-badge]: https://github.com/words/levenshtein-edit-distance/workflows/main/badge.svg 177 | 178 | [build]: https://github.com/words/levenshtein-edit-distance/actions 179 | 180 | [coverage-badge]: https://img.shields.io/codecov/c/github/words/levenshtein-edit-distance.svg 181 | 182 | [coverage]: https://codecov.io/github/words/levenshtein-edit-distance 183 | 184 | [downloads-badge]: https://img.shields.io/npm/dm/levenshtein-edit-distance.svg 185 | 186 | [downloads]: https://www.npmjs.com/package/levenshtein-edit-distance 187 | 188 | [size-badge]: https://img.shields.io/bundlephobia/minzip/levenshtein-edit-distance.svg 189 | 190 | [size]: https://bundlephobia.com/result?p=levenshtein-edit-distance 191 | 192 | [npm]: https://www.npmjs.com 193 | 194 | [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 195 | 196 | [esmsh]: https://esm.sh 197 | 198 | [typescript]: https://www.typescriptlang.org 199 | 200 | [contribute]: https://opensource.guide/how-to-contribute/ 201 | 202 | [license]: license 203 | 204 | [author]: https://wooorm.com 205 | 206 | [wiki]: https://en.wikipedia.org/wiki/Levenshtein_distance 207 | 208 | [vlad]: https://en.wikipedia.org/wiki/Vladimir_Levenshtein 209 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import util from 'node:util' 3 | import cp from 'node:child_process' 4 | import fs from 'node:fs' 5 | import {URL} from 'node:url' 6 | import {PassThrough} from 'node:stream' 7 | import test from 'node:test' 8 | import {levenshteinEditDistance as levenshtein} from './index.js' 9 | 10 | const exec = util.promisify(cp.exec) 11 | 12 | /** @type {Object.} */ 13 | const pack = JSON.parse( 14 | String(fs.readFileSync(new URL('package.json', import.meta.url))) 15 | ) 16 | 17 | test('api', async function (t) { 18 | await t.test('should work', function () { 19 | assert.equal(levenshtein('', 'a'), 1) 20 | assert.equal(levenshtein('a', ''), 1) 21 | assert.equal(levenshtein('', ''), 0) 22 | assert.equal(levenshtein('levenshtein', 'levenshtein'), 0) 23 | assert.equal(levenshtein('sitting', 'kitten'), 3) 24 | assert.equal(levenshtein('gumbo', 'gambol'), 2) 25 | assert.equal(levenshtein('saturday', 'sunday'), 3) 26 | 27 | assert.notEqual( 28 | levenshtein('DwAyNE', 'DUANE'), 29 | levenshtein('dwayne', 'DuAnE'), 30 | 'should not match case insensitive' 31 | ) 32 | 33 | assert.equal( 34 | levenshtein('DwAyNE', 'DUANE', true), 35 | levenshtein('dwayne', 'DuAnE', true), 36 | 'should match case if `insensitive` is given' 37 | ) 38 | 39 | assert.equal( 40 | levenshtein('aarrgh', 'aargh'), 41 | levenshtein('aargh', 'aarrgh'), 42 | 'should not care about parameter order' 43 | ) 44 | }) 45 | 46 | await t.test('Compatibility with `fast-levenshtein`', function () { 47 | assert.equal(levenshtein('a', 'b'), 1) 48 | assert.equal(levenshtein('ab', 'ac'), 1) 49 | assert.equal(levenshtein('ac', 'bc'), 1) 50 | assert.equal(levenshtein('abc', 'axc'), 1) 51 | assert.equal(levenshtein('xabxcdxxefxgx', '1ab2cd34ef5g6'), 6) 52 | assert.equal(levenshtein('xabxcdxxefxgx', 'abcdefg'), 6) 53 | assert.equal(levenshtein('javawasneat', 'scalaisgreat'), 7) 54 | assert.equal(levenshtein('example', 'samples'), 3) 55 | assert.equal(levenshtein('sturgeon', 'urgently'), 6) 56 | assert.equal(levenshtein('levenshtein', 'frankenstein'), 6) 57 | assert.equal(levenshtein('distance', 'difference'), 5) 58 | assert.equal( 59 | levenshtein( 60 | '因為我是中國人所以我會說中文', 61 | '因為我是英國人所以我會說英文' 62 | ), 63 | 2 64 | ) 65 | assert.equal( 66 | levenshtein( 67 | 'Morbi interdum ultricies neque varius condimentum. Donec ' + 68 | 'volutpat turpis interdum metus ultricies vulputate. Duis ' + 69 | 'ultricies rhoncus sapien, sit amet fermentum risus ' + 70 | 'imperdiet vitae. Ut et lectus', 71 | 'Duis erat dolor, cursus in tincidunt a, lobortis in odio. ' + 72 | 'Cras magna sem, pharetra et iaculis quis, faucibus quis ' + 73 | 'tellus. Suspendisse dapibus sapien in justo cursus' 74 | ), 75 | 143 76 | ) 77 | }) 78 | }) 79 | 80 | test('cli', async function () { 81 | try { 82 | await exec('./cli.js sitting') 83 | assert.fail('should not pass') 84 | } catch (error) { 85 | assert.ok( 86 | /Usage: levenshtein-edit-distance/.test(String(error)), 87 | 'not enough arguments' 88 | ) 89 | } 90 | 91 | assert.deepEqual( 92 | await exec('./cli.js sitting kitten'), 93 | {stdout: '3\n', stderr: ''}, 94 | 'spaces' 95 | ) 96 | 97 | assert.deepEqual( 98 | await exec('./cli.js sitting,kitten'), 99 | {stdout: '3\n', stderr: ''}, 100 | 'commas' 101 | ) 102 | 103 | assert.deepEqual( 104 | await exec('./cli.js sitting, kitten'), 105 | {stdout: '3\n', stderr: ''}, 106 | 'commas and spaces' 107 | ) 108 | 109 | assert.deepEqual( 110 | await exec('./cli.js a A'), 111 | {stdout: '1\n', stderr: ''}, 112 | 'case-sensitive: default' 113 | ) 114 | 115 | assert.deepEqual( 116 | await exec('./cli.js a A -i'), 117 | {stdout: '0\n', stderr: ''}, 118 | 'case-sensitive: -i' 119 | ) 120 | 121 | assert.deepEqual( 122 | await exec('./cli.js a A --insensitive'), 123 | {stdout: '0\n', stderr: ''}, 124 | 'case-sensitive: --insensitive' 125 | ) 126 | 127 | await new Promise(function (resolve) { 128 | const input = new PassThrough() 129 | const subprocess = cp.exec('./cli.js', function (error, stdout, stderr) { 130 | assert.deepEqual([error, stdout, stderr], [null, '6\n', ''], 'stdin') 131 | setImmediate(resolve) 132 | }) 133 | assert(subprocess.stdin, 'expected stdin on `subprocess`') 134 | input.pipe(subprocess.stdin) 135 | input.write('sturgeon') 136 | setImmediate(function () { 137 | input.end(' urgently') 138 | }) 139 | }) 140 | 141 | await new Promise(function (resolve) { 142 | const input = new PassThrough() 143 | const subprocess = cp.exec('./cli.js', function (error, stdout, stderr) { 144 | assert.deepEqual( 145 | [ 146 | Boolean(error), 147 | stdout, 148 | /Usage: levenshtein-edit-distance/.test(stderr) 149 | ], 150 | [true, '', true], 151 | 'stdin (not enough arguments)' 152 | ) 153 | setImmediate(resolve) 154 | }) 155 | 156 | assert(subprocess.stdin, 'expected stdin on `subprocess`') 157 | input.pipe(subprocess.stdin) 158 | input.end('sturgeon') 159 | }) 160 | 161 | const h = await exec('./cli.js -h') 162 | assert.ok(/\sUsage: levenshtein-edit-distance/.test(h.stdout), '-h') 163 | 164 | const help = await exec('./cli.js --help') 165 | assert.ok(/\sUsage: levenshtein-edit-distance/.test(help.stdout), '-h') 166 | 167 | assert.deepEqual( 168 | await exec('./cli.js -v'), 169 | {stdout: pack.version + '\n', stderr: ''}, 170 | '-v' 171 | ) 172 | 173 | assert.deepEqual( 174 | await exec('./cli.js --version'), 175 | {stdout: pack.version + '\n', stderr: ''}, 176 | '--version' 177 | ) 178 | }) 179 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["**/**.js"], 3 | "exclude": ["coverage", "node_modules"], 4 | "compilerOptions": { 5 | "checkJs": true, 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "exactOptionalPropertyTypes": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "lib": ["es2020"], 11 | "module": "node16", 12 | "newLine": "lf", 13 | "skipLibCheck": true, 14 | "strict": true, 15 | "target": "es2020" 16 | } 17 | } 18 | --------------------------------------------------------------------------------