diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b18fd29..325c4bf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,15 @@ updates: directory: '/' schedule: interval: 'weekly' + + - package-ecosystem: npm + directory: "/" + schedule: + interval: cron + cronjob: "0 0 21 3,6,9,12 *" + timezone: "Etc/UTC" + open-pull-requests-limit: 10 + groups: + npm-dependencies: + patterns: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6140be..d4b864a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,17 +13,19 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['16', '18'] + node-version: ['24'] steps: - uses: actions/checkout@v6 - - name: Set up Node ${{ matrix.ruby-version }} - uses: actions/setup-node@v6 + + - name: Set up Node + uses: gjtorikian/actions/setup-languages@main with: + node: true node-version: ${{ matrix.node-version }} - - name: Install dependencies - run: npm install + - name: Build Typescript run: npm run build + - name: Run tests run: npm test diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..042a177 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "rules": { + "no-bitwise": "off" + } +} diff --git a/package-lock.json b/package-lock.json index 8027b54..53ebe6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,14 @@ "version": "5.0.7", "license": "MIT", "devDependencies": { - "@types/jest": "^23.3.14", - "@types/node": "^10.17.60", + "@types/jest": "^29", + "@types/node": "^18", "jest": "^29.7.0", - "prettier": "^1.19.1", + "oxlint": "^0.16", + "prettier": "^3", "release-it": "^19.0.4", "ts-jest": "^29.1.4", - "tslint": "^5.20.1", - "tslint-config-prettier": "^1.18.0", - "typescript": "^4.9.4" + "typescript": "^5" }, "engines": { "node": ">= 18.0.0" @@ -1170,6 +1169,118 @@ "@octokit/openapi-types": "^25.1.0" } }, + "node_modules/@oxlint/darwin-arm64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-0.16.12.tgz", + "integrity": "sha512-G7phYhlIA4ke2nW7tHLl+E5+rvdzgGA6830D+e+y1RGllT0w2ONGdKcVTj+2pXGCw6yPmCC5fDsDEn2+RPTfxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/darwin-x64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-0.16.12.tgz", + "integrity": "sha512-P/LSOgJ6SzQ3OKEIf3HsebgokZiZ5nDuTgIL4LpNCHlkOLDu/fT8XL9pSkR5y+60v0SOxUF/+aN0Q8EmxblrCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxlint/linux-arm64-gnu": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-0.16.12.tgz", + "integrity": "sha512-0N/ZsW+cL7ZAUvOHbzMp3iApt5b/Q81q2e9RgEzkI6gUDCJK8/blWg0se/i6y9e24WH0ZC4bcxY1+Qz4ZQ+mFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-arm64-musl": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-0.16.12.tgz", + "integrity": "sha512-MoG1SIw4RGowsOsPjm5HjRWymisRZWBea7ewMoXA5xIVQ3eqECifG0KJW0OZp96Ad8DFBEavdlNuImB2uXsMwg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-gnu": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-0.16.12.tgz", + "integrity": "sha512-STho8QdMLfn/0lqRU94tGPaYX8lGJccPbqeUcEr3eK5gZ5ZBdXmiHlvkcngXFEXksYC8/5VoJN7Vf3HsmkEskw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/linux-x64-musl": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-0.16.12.tgz", + "integrity": "sha512-i7pzSoj9nCg/ZzOe8dCZeFWyRRWDylR9tIX04xRTq3G6PBLm6i9VrOdEkxbgM9+pCkRzUc0a9D7rbtCF34TQUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxlint/win32-arm64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-0.16.12.tgz", + "integrity": "sha512-wcxq3IBJ7ZlONlXJxQM+7EMx+LX1nkz3ZS3R0EtDM76EOZaqe8BMkW5cXVhF8jarZTZC18oKAckW4Ng9d8adBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxlint/win32-x64": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-0.16.12.tgz", + "integrity": "sha512-Ae1fx7wmAcMVqzS8rLINaFRpAdh29QzHh133bEYMHzfWBYyK/hLu9g4GLwC/lEIVQu9884b8qutGfdOk6Qia3w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@phun-ky/typeof": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@phun-ky/typeof/-/typeof-1.2.8.tgz", @@ -1290,16 +1401,32 @@ } }, "node_modules/@types/jest": { - "version": "23.3.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.14.tgz", - "integrity": "sha512-Q5hTcfdudEL2yOmluA1zaSyPbzWPmJ3XfSWeP3RyoYvS9hnje1ZyagrZOuQ6+1nQC1Gw+7gap3pLNL3xL6UBug==", - "dev": true + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } }, "node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "dev": true + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/parse-path": { "version": "7.0.3", @@ -1633,15 +1760,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -1908,12 +2026,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2109,15 +2221,6 @@ "node": ">=8" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -3848,15 +3951,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4134,6 +4228,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/oxlint": { + "version": "0.16.12", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-0.16.12.tgz", + "integrity": "sha512-1oN3P9bzE90zkbjLTc+uICVLwSR+eQaDaYVipS0BtmtmEd3ccQue0y7npCinb35YqKzIv1LZxhoU9nm5fgmQuw==", + "dev": true, + "license": "MIT", + "bin": { + "oxc_language_server": "bin/oxc_language_server", + "oxlint": "bin/oxlint" + }, + "engines": { + "node": ">=8.*" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/darwin-arm64": "0.16.12", + "@oxlint/darwin-x64": "0.16.12", + "@oxlint/linux-arm64-gnu": "0.16.12", + "@oxlint/linux-arm64-musl": "0.16.12", + "@oxlint/linux-x64-gnu": "0.16.12", + "@oxlint/linux-x64-musl": "0.16.12", + "@oxlint/win32-arm64": "0.16.12", + "@oxlint/win32-x64": "0.16.12" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4360,15 +4481,19 @@ } }, "node_modules/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, + "license": "MIT", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/pretty-format": { @@ -5517,155 +5642,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev" - } - }, - "node_modules/tslint-config-prettier": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", - "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", - "dev": true, - "bin": { - "tslint-config-prettier-check": "bin/check.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/tslint/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/tslint/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/tslint/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tslint/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tslint/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5688,16 +5664,17 @@ } }, "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici": { diff --git a/package.json b/package.json index ff2d742..aac17fc 100644 --- a/package.json +++ b/package.json @@ -18,18 +18,17 @@ "is text file" ], "devDependencies": { - "@types/jest": "^23.3.14", - "@types/node": "^10.17.60", + "@types/jest": "^29", + "@types/node": "^18", "jest": "^29.7.0", - "prettier": "^1.19.1", + "oxlint": "^0.16", + "prettier": "^3", "release-it": "^19.0.4", "ts-jest": "^29.1.4", - "tslint": "^5.20.1", - "tslint-config-prettier": "^1.18.0", - "typescript": "^4.9.4" + "typescript": "^5" }, "engines": { - "node": ">= 18.0.0" + "node": ">= 24.0.0" }, "files": [ "lib/**/*" @@ -50,8 +49,8 @@ }, "scripts": { "build": "tsc", - "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\" && tslint --fix -c tslint.json 'src/**/*.ts'", - "lint": "tslint -p tsconfig.json", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "lint": "oxlint src test", "prepare": "npm run build", "release": "release-it", "prepublishOnly": "npm test && npm run lint", diff --git a/src/index.ts b/src/index.ts index d006334..6422356 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,5 @@ import * as fs from 'fs'; -import { promisify } from 'util'; - -const statAsync = promisify(fs.stat); -const openAsync = promisify(fs.open); -const closeAsync = promisify(fs.close); +import { open, stat } from 'node:fs/promises'; const MAX_BYTES: number = 512; const UTF8_BOUNDARY_RESERVE: number = 3; @@ -115,30 +111,17 @@ function isBinaryProto(fileBuffer: Buffer, totalBytes: number): boolean { export async function isBinaryFile(file: string | Buffer, size?: number): Promise { if (isString(file)) { - const stat = await statAsync(file); - - isStatFile(stat); - - const fileDescriptor = await openAsync(file, 'r'); - - const allocBuffer = Buffer.alloc(MAX_BYTES + UTF8_BOUNDARY_RESERVE); - - // Read the file with no encoding for raw buffer access. - // NB: something is severely wrong with promisify, had to construct my own Promise - return new Promise((fulfill, reject) => { - fs.read(fileDescriptor, allocBuffer, 0, MAX_BYTES + UTF8_BOUNDARY_RESERVE, 0, (err, bytesRead, _) => { - closeAsync(fileDescriptor); - if (err) { - reject(err); - } else { - try { - fulfill(isBinaryCheck(allocBuffer, bytesRead)); - } catch (error) { - reject(error); - } - } - }); - }); + const fileStat = await stat(file); + isStatFile(fileStat); + + const fileHandle = await open(file, 'r'); + try { + const allocBuffer = Buffer.alloc(MAX_BYTES + UTF8_BOUNDARY_RESERVE); + const { bytesRead } = await fileHandle.read(allocBuffer, 0, MAX_BYTES + UTF8_BOUNDARY_RESERVE, 0); + return isBinaryCheck(allocBuffer, bytesRead); + } finally { + await fileHandle.close(); + } } else { if (size === undefined) { size = file.length; diff --git a/test/index.test.ts b/test/index.test.ts index 7fc4324..1741a94 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,14 +1,13 @@ import { isBinaryFile, isBinaryFileSync } from '../src/index'; import * as fs from 'fs'; -import { promises as fsPromises } from 'fs'; import * as path from 'path'; -const FIXTURE_PATH = "./test/fixtures"; +const FIXTURE_PATH = './test/fixtures'; describe('async', () => { - it("does not require size if bytes are given", async () => { - const bytes = fs.readFileSync(path.join(FIXTURE_PATH, "grep")); + it('does not require size if bytes are given', async () => { + const bytes = fs.readFileSync(path.join(FIXTURE_PATH, 'grep')); expect.assertions(1); @@ -17,8 +16,8 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return true on a binary program, accepting path", async () => { - const file = path.join(FIXTURE_PATH, "grep"); + it('should return true on a binary program, accepting path', async () => { + const file = path.join(FIXTURE_PATH, 'grep'); expect.assertions(1); @@ -27,9 +26,9 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return true on a binary program, accepting bytes & size", async () => { - const bytes = fs.readFileSync(path.join(FIXTURE_PATH, "grep")); - const size = fs.lstatSync(path.join(FIXTURE_PATH, "grep")).size; + it('should return true on a binary program, accepting bytes & size', async () => { + const bytes = fs.readFileSync(path.join(FIXTURE_PATH, 'grep')); + const size = fs.lstatSync(path.join(FIXTURE_PATH, 'grep')).size; expect.assertions(1); @@ -38,8 +37,8 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return false on a extensionless script, accepting path", async () => { - const file = path.join(FIXTURE_PATH, "perl_script"); + it('should return false on a extensionless script, accepting path', async () => { + const file = path.join(FIXTURE_PATH, 'perl_script'); expect.assertions(1); @@ -48,9 +47,9 @@ describe('async', () => { expect(result).toBe(false); }); - it("should return false on a extensionless script, accepting bytes & size", async () => { - const bytes = fs.readFileSync(path.join(FIXTURE_PATH, "perl_script")); - const size = fs.lstatSync(path.join(FIXTURE_PATH, "perl_script")).size; + it('should return false on a extensionless script, accepting bytes & size', async () => { + const bytes = fs.readFileSync(path.join(FIXTURE_PATH, 'perl_script')); + const size = fs.lstatSync(path.join(FIXTURE_PATH, 'perl_script')).size; expect.assertions(1); @@ -59,9 +58,8 @@ describe('async', () => { expect(result).toBe(false); }); - - it("should return false on a russian text", async () => { - const file = path.join(FIXTURE_PATH, "russian_file.rst"); + it('should return false on a russian text', async () => { + const file = path.join(FIXTURE_PATH, 'russian_file.rst'); expect.assertions(1); @@ -70,8 +68,8 @@ describe('async', () => { expect(result).toBe(false); }); - it("should return false on a zero-byte image file", async () => { - const file = path.join(FIXTURE_PATH, "null_file.gif"); + it('should return false on a zero-byte image file', async () => { + const file = path.join(FIXTURE_PATH, 'null_file.gif'); expect.assertions(1); @@ -80,8 +78,8 @@ describe('async', () => { expect(result).toBe(false); }); - it("should return true on a gif", async () => { - const file = path.join(FIXTURE_PATH, "trunks.gif"); + it('should return true on a gif', async () => { + const file = path.join(FIXTURE_PATH, 'trunks.gif'); expect.assertions(1); @@ -90,8 +88,8 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return false on some UTF8 lua file", async () => { - const file = path.join(FIXTURE_PATH, "no.lua"); + it('should return false on some UTF8 lua file', async () => { + const file = path.join(FIXTURE_PATH, 'no.lua'); expect.assertions(1); @@ -100,24 +98,26 @@ describe('async', () => { expect(result).toBe(false); }); - it("should boom on a directory", async () => { - const file = path.join(FIXTURE_PATH, "dir"); + it('should boom on a directory', async () => { + const file = path.join(FIXTURE_PATH, 'dir'); expect.assertions(1); - await expect(isBinaryFile(file)).rejects.toThrow("Path provided was not a file!"); + await expect(isBinaryFile(file)).rejects.toThrow('Path provided was not a file!'); }); - it("should boom on non-existent file", async () => { - const file = path.join(FIXTURE_PATH, "blahblahblbahhhhhh"); + it('should boom on non-existent file', async () => { + const file = path.join(FIXTURE_PATH, 'blahblahblbahhhhhh'); expect.assertions(1); - await expect(isBinaryFile(file)).rejects.toThrow("ENOENT: no such file or directory, stat 'test/fixtures/blahblahblbahhhhhh'"); + await expect(isBinaryFile(file)).rejects.toThrow( + "ENOENT: no such file or directory, stat 'test/fixtures/blahblahblbahhhhhh'", + ); }); - it("should return true on a PDF", async () => { - const file = path.join(FIXTURE_PATH, "pdf.pdf"); + it('should return true on a PDF', async () => { + const file = path.join(FIXTURE_PATH, 'pdf.pdf'); expect.assertions(1); @@ -126,8 +126,8 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return true on a tricky PDF that needs a header check", async () => { - const file = path.join(FIXTURE_PATH, "test.pdf"); + it('should return true on a tricky PDF that needs a header check', async () => { + const file = path.join(FIXTURE_PATH, 'test.pdf'); expect.assertions(1); @@ -136,8 +136,8 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return false on a protobuf.proto", async () => { - const file = path.join(FIXTURE_PATH, "protobuf.proto"); + it('should return false on a protobuf.proto', async () => { + const file = path.join(FIXTURE_PATH, 'protobuf.proto'); expect.assertions(1); @@ -146,8 +146,8 @@ describe('async', () => { expect(result).toBe(false); }); - it("should return false on a protobuf.proto.txt", async () => { - const file = path.join(FIXTURE_PATH, "protobuf.proto.txt"); + it('should return false on a protobuf.proto.txt', async () => { + const file = path.join(FIXTURE_PATH, 'protobuf.proto.txt'); expect.assertions(1); @@ -156,8 +156,8 @@ describe('async', () => { expect(result).toBe(false); }); - it("should return true on a protobuf.proto.bin", async () => { - const file = path.join(FIXTURE_PATH, "protobuf.proto.bin"); + it('should return true on a protobuf.proto.bin', async () => { + const file = path.join(FIXTURE_PATH, 'protobuf.proto.bin'); expect.assertions(1); @@ -166,8 +166,8 @@ describe('async', () => { expect(result).toBe(true); }); - it("should return false on a Vai script file", async () => { - const file = path.join(FIXTURE_PATH, "vai_script.txt"); + it('should return false on a Vai script file', async () => { + const file = path.join(FIXTURE_PATH, 'vai_script.txt'); expect.assertions(1); @@ -175,124 +175,121 @@ describe('async', () => { expect(result).toBe(false); }); - }); describe('sync', () => { - it("should require size if bytes are given", () => { - const bytes = fs.readFileSync(path.join(FIXTURE_PATH, "grep")); + it('should require size if bytes are given', () => { + const bytes = fs.readFileSync(path.join(FIXTURE_PATH, 'grep')); const result = isBinaryFileSync(bytes); expect(result).toBe(true); }); - it("should return true on a binary program, accepting path", () => { - const file = path.join(FIXTURE_PATH, "grep"); + it('should return true on a binary program, accepting path', () => { + const file = path.join(FIXTURE_PATH, 'grep'); const result = isBinaryFileSync(file); expect(result).toBe(true); }); - it("should return true on a binary program, accepting bytes & size", () => { - const bytes = fs.readFileSync(path.join(FIXTURE_PATH, "grep")); - const size = fs.lstatSync(path.join(FIXTURE_PATH, "grep")).size; + it('should return true on a binary program, accepting bytes & size', () => { + const bytes = fs.readFileSync(path.join(FIXTURE_PATH, 'grep')); + const size = fs.lstatSync(path.join(FIXTURE_PATH, 'grep')).size; const result = isBinaryFileSync(bytes, size); expect(result).toBe(true); }); - it("should return false on a extensionless script, accepting path", () => { - const file = path.join(FIXTURE_PATH, "perl_script"); + it('should return false on a extensionless script, accepting path', () => { + const file = path.join(FIXTURE_PATH, 'perl_script'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); - it("should return false on a extensionless script, accepting bytes & size", () => { - const bytes = fs.readFileSync(path.join(FIXTURE_PATH, "perl_script")); - const size = fs.lstatSync(path.join(FIXTURE_PATH, "perl_script")).size; + it('should return false on a extensionless script, accepting bytes & size', () => { + const bytes = fs.readFileSync(path.join(FIXTURE_PATH, 'perl_script')); + const size = fs.lstatSync(path.join(FIXTURE_PATH, 'perl_script')).size; const result = isBinaryFileSync(bytes, size); expect(result).toBe(false); }); - - it("should return false on a russian text", () => { - const file = path.join(FIXTURE_PATH, "russian_file.rst"); + it('should return false on a russian text', () => { + const file = path.join(FIXTURE_PATH, 'russian_file.rst'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); - it("should return false on a zero-byte image file", () => { - const file = path.join(FIXTURE_PATH, "null_file.gif"); + it('should return false on a zero-byte image file', () => { + const file = path.join(FIXTURE_PATH, 'null_file.gif'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); - it("should return true on a gif", () => { - const file = path.join(FIXTURE_PATH, "trunks.gif"); + it('should return true on a gif', () => { + const file = path.join(FIXTURE_PATH, 'trunks.gif'); const result = isBinaryFileSync(file); expect(result).toBe(true); }); - it("should return false on some UTF8 lua file", () => { - const file = path.join(FIXTURE_PATH, "no.lua"); + it('should return false on some UTF8 lua file', () => { + const file = path.join(FIXTURE_PATH, 'no.lua'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); - it("should boom on a directory", () => { - const file = path.join(FIXTURE_PATH, "dir"); + it('should boom on a directory', () => { + const file = path.join(FIXTURE_PATH, 'dir'); try { isBinaryFileSync(file); } catch (e: any) { - - expect(e.message).toBe("Path provided was not a file!") + expect(e.message).toBe('Path provided was not a file!'); } }); - it("should boom on non-existent file", () => { - const file = path.join(FIXTURE_PATH, "blahblahblbahhhhhh"); + it('should boom on non-existent file', () => { + const file = path.join(FIXTURE_PATH, 'blahblahblbahhhhhh'); try { isBinaryFileSync(file); } catch (e: any) { - expect(e.message).toBe("ENOENT: no such file or directory, stat 'test/fixtures/blahblahblbahhhhhh'") + expect(e.message).toBe("ENOENT: no such file or directory, stat 'test/fixtures/blahblahblbahhhhhh'"); } }); - it("should return true on a PDF", () => { - const file = path.join(FIXTURE_PATH, "pdf.pdf"); + it('should return true on a PDF', () => { + const file = path.join(FIXTURE_PATH, 'pdf.pdf'); const result = isBinaryFileSync(file); expect(result).toBe(true); }); - it("should return true on a tricky PDF that needs a header check", () => { - const file = path.join(FIXTURE_PATH, "test.pdf"); + it('should return true on a tricky PDF that needs a header check', () => { + const file = path.join(FIXTURE_PATH, 'test.pdf'); const result = isBinaryFileSync(file); expect(result).toBe(true); }); - it("should return false for non-UTF8 files", async () => { - const encodingDir = path.join(FIXTURE_PATH, "encodings") + it('should return false for non-UTF8 files', async () => { + const encodingDir = path.join(FIXTURE_PATH, 'encodings'); const files = fs.readdirSync(encodingDir); files.forEach((file) => { @@ -302,8 +299,8 @@ describe('sync', () => { }); }); - it("should return false on a Vai script file", () => { - const file = path.join(FIXTURE_PATH, "vai_script.txt"); + it('should return false on a Vai script file', () => { + const file = path.join(FIXTURE_PATH, 'vai_script.txt'); const result = isBinaryFileSync(file); @@ -311,50 +308,50 @@ describe('sync', () => { }); }); -it("should return false on a UTF-8 file with emoji", () => { - const file = path.join(FIXTURE_PATH, "emoji.txt"); +it('should return false on a UTF-8 file with emoji', () => { + const file = path.join(FIXTURE_PATH, 'emoji.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on UTF-8 file with 4-byte sequence truncated at byte 508", () => { - const file = path.join(FIXTURE_PATH, "508A-4byte.txt"); +it('should return false on UTF-8 file with 4-byte sequence truncated at byte 508', () => { + const file = path.join(FIXTURE_PATH, '508A-4byte.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on UTF-8 file with 3-byte sequence truncated at byte 509", () => { - const file = path.join(FIXTURE_PATH, "509A-3byte.txt"); +it('should return false on UTF-8 file with 3-byte sequence truncated at byte 509', () => { + const file = path.join(FIXTURE_PATH, '509A-3byte.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on UTF-8 file with 4-byte sequence truncated at byte 509", () => { - const file = path.join(FIXTURE_PATH, "509A-4byte.txt"); +it('should return false on UTF-8 file with 4-byte sequence truncated at byte 509', () => { + const file = path.join(FIXTURE_PATH, '509A-4byte.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on UTF-8 file with 2-byte sequence truncated at byte 510", () => { - const file = path.join(FIXTURE_PATH, "510A-2byte.txt"); +it('should return false on UTF-8 file with 2-byte sequence truncated at byte 510', () => { + const file = path.join(FIXTURE_PATH, '510A-2byte.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on UTF-8 file with 3-byte sequence truncated at byte 510", () => { - const file = path.join(FIXTURE_PATH, "510A-3byte.txt"); +it('should return false on UTF-8 file with 3-byte sequence truncated at byte 510', () => { + const file = path.join(FIXTURE_PATH, '510A-3byte.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on UTF-8 file with 4-byte sequence truncated at byte 510", () => { - const file = path.join(FIXTURE_PATH, "510A-4byte.txt"); +it('should return false on UTF-8 file with 4-byte sequence truncated at byte 510', () => { + const file = path.join(FIXTURE_PATH, '510A-4byte.txt'); const result = isBinaryFileSync(file); expect(result).toBe(false); }); -it("should return false on real-world Python file with UTF-8 at boundary (utf8-boundary-truncation bug case)", () => { - const file = path.join(FIXTURE_PATH, "utf8-boundary-truncation_case.py"); +it('should return false on real-world Python file with UTF-8 at boundary (utf8-boundary-truncation bug case)', () => { + const file = path.join(FIXTURE_PATH, 'utf8-boundary-truncation_case.py'); const result = isBinaryFileSync(file); expect(result).toBe(false); -}); \ No newline at end of file +}); diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 44cb52d..0000000 --- a/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": ["tslint:recommended", "tslint-config-prettier"], - "rules": { - "no-bitwise": false - } -}