From 68cd852426d27c3a746f09059f37e5133649df19 Mon Sep 17 00:00:00 2001 From: Marc Weiner Date: Thu, 30 Jul 2026 06:33:14 -0400 Subject: [PATCH 1/6] chore: add workflow job and step timeouts Add timeout-minutes to CI/release jobs and slow steps (install, deploy, build) so hung workflows fail fast instead of burning Actions minutes. --- .github/workflows/ci.yml | 4 ++++ .github/workflows/release.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35904b7..e6633d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ jobs: test: name: Test runs-on: ubuntu-latest + timeout-minutes: 15 strategy: fail-fast: false matrix: @@ -26,6 +27,7 @@ jobs: - name: Check package.json deps run: node scripts/check-no-file-deps.mjs - name: Install dependencies + timeout-minutes: 5 run: npm ci - name: Build @@ -40,6 +42,7 @@ jobs: typecheck: name: Type Check runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v4 @@ -54,6 +57,7 @@ jobs: - name: Check package.json deps run: node scripts/check-no-file-deps.mjs - name: Install dependencies + timeout-minutes: 5 run: npm ci - name: Type check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fe4f99c..edb6567 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ jobs: release: name: Release runs-on: ubuntu-latest + timeout-minutes: 30 concurrency: group: deploy-${{github.ref}} cancel-in-progress: true From 7cc25ae171d5e05e9715841a52e7afeee75441f6 Mon Sep 17 00:00:00 2001 From: Marc Weiner Date: Thu, 30 Jul 2026 07:09:01 -0400 Subject: [PATCH 2/6] chore: tighten install step timeout to 3 minutes --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6633d1..3977f77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: - name: Check package.json deps run: node scripts/check-no-file-deps.mjs - name: Install dependencies - timeout-minutes: 5 + timeout-minutes: 3 run: npm ci - name: Build @@ -57,7 +57,7 @@ jobs: - name: Check package.json deps run: node scripts/check-no-file-deps.mjs - name: Install dependencies - timeout-minutes: 5 + timeout-minutes: 3 run: npm ci - name: Type check From 131f875ba1243d0d5b1400af0283186ceaec0e75 Mon Sep 17 00:00:00 2001 From: Marc Weiner Date: Thu, 30 Jul 2026 07:22:45 -0400 Subject: [PATCH 3/6] chore: simplify workflow timeouts to job-level only (CI 10m, release 15m) --- .github/workflows/ci.yml | 6 ++---- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3977f77..0341c01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: test: name: Test runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 10 strategy: fail-fast: false matrix: @@ -27,7 +27,6 @@ jobs: - name: Check package.json deps run: node scripts/check-no-file-deps.mjs - name: Install dependencies - timeout-minutes: 3 run: npm ci - name: Build @@ -42,7 +41,7 @@ jobs: typecheck: name: Type Check runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 10 steps: - uses: actions/checkout@v4 @@ -57,7 +56,6 @@ jobs: - name: Check package.json deps run: node scripts/check-no-file-deps.mjs - name: Install dependencies - timeout-minutes: 3 run: npm ci - name: Type check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index edb6567..c0c999a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: release: name: Release runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 15 concurrency: group: deploy-${{github.ref}} cancel-in-progress: true From fa6df6542ac31000e32588d40d45b15750335d94 Mon Sep 17 00:00:00 2001 From: Marc Weiner Date: Thu, 30 Jul 2026 16:28:40 -0400 Subject: [PATCH 4/6] chore: simplify CI and enforce no-local-deps via eslint Single matrix job (build, lint, test). Drop check-no-file-deps script; use package-json/no-local-dependencies in lint. --- .github/workflows/ci.yml | 31 +--- eslint.config.mjs | 14 ++ package-lock.json | 310 +++++++++++++++++++++++++++++++++ package.json | 4 +- scripts/check-no-file-deps.mjs | 61 ------- 5 files changed, 328 insertions(+), 92 deletions(-) delete mode 100644 scripts/check-no-file-deps.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0341c01..ddf6da5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,8 +5,8 @@ on: branches: [main, next] jobs: - test: - name: Test + ci: + name: CI (Node ${{ matrix.node-version }}) runs-on: ubuntu-latest timeout-minutes: 10 strategy: @@ -22,10 +22,6 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' - - - - name: Check package.json deps - run: node scripts/check-no-file-deps.mjs - name: Install dependencies run: npm ci @@ -37,26 +33,3 @@ jobs: - name: Test with coverage run: npm test - - typecheck: - name: Type Check - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' - - - - - name: Check package.json deps - run: node scripts/check-no-file-deps.mjs - - name: Install dependencies - run: npm ci - - - name: Type check - run: npx tsc --noEmit diff --git a/eslint.config.mjs b/eslint.config.mjs index 60a03be..4c88e99 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,4 +1,6 @@ import tseslint from 'typescript-eslint'; +import packageJson from 'eslint-plugin-package-json'; +import * as jsoncParser from 'jsonc-eslint-parser'; export default tseslint.config( { @@ -136,4 +138,16 @@ export default tseslint.config( '@typescript-eslint/no-explicit-any': 'off', }, }, + { + files: ['package.json'], + languageOptions: { + parser: jsoncParser, + }, + plugins: { + 'package-json': packageJson, + }, + rules: { + 'package-json/no-local-dependencies': ['error', {ignorePrivate: false}], + }, + }, ); diff --git a/package-lock.json b/package-lock.json index 44b0b2a..0f0719f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "ajv": "^8.18.0", "ajv-formats": "3.0.1", "eslint": "^10.0.0", + "eslint-plugin-package-json": "^1.6.1", "joi": "18.0.1", "kizu": "^4.0.0", "ts-node": "^10.9.0", @@ -29,6 +30,13 @@ } } }, + "node_modules/@altano/repository-tools": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@altano/repository-tools/-/repository-tools-2.0.3.tgz", + "integrity": "sha512-cSR/ZYDF6Wp9OeAJMyLYYN1GenAAhV17W+w38ELP+3c5Ltsy9jkkCymi33nz/qnXyef3n6Fbr1h2yt3dvUN5sQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1250,6 +1258,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -1383,6 +1398,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-newline": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz", + "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/diff": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", @@ -1505,6 +1546,83 @@ } } }, + "node_modules/eslint-fix-utils": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-fix-utils/-/eslint-fix-utils-0.4.3.tgz", + "integrity": "sha512-EKzNhsNavV5DdkHVltHBM9TqfsonCmiUhOm1Ra97zo03M9IAx3wtM1eC27eWGMj/D3LrALmHHNe1QlQH1P7Nxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/estree": ">=1", + "eslint": ">=8" + }, + "peerDependenciesMeta": { + "@types/estree": { + "optional": true + } + } + }, + "node_modules/eslint-json-compat-utils": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/eslint-json-compat-utils/-/eslint-json-compat-utils-0.2.3.tgz", + "integrity": "sha512-RbBmDFyu7FqnjE8F0ZxPNzx5UaptdeS9Uu50r7A+D7s/+FCX+ybiyViYEgFUaFIFqSWJgZRTpL5d8Kanxxl2lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esquery": "^1.6.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": "*", + "jsonc-eslint-parser": "^2.4.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@eslint/json": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-package-json": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-package-json/-/eslint-plugin-package-json-1.6.1.tgz", + "integrity": "sha512-iKcIfIYZXXb20vghnigDUemIISW9D2dMPuDZ+IGRnIOPa53+ZwU1HnsvsbSB7zGq4MAo1V9ZYfL0uTEVpCFEiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@altano/repository-tools": "^2.0.1", + "change-case": "^5.4.4", + "detect-indent": "^7.0.2", + "detect-newline": "^4.0.1", + "eslint-fix-utils": "~0.4.3", + "eslint-json-compat-utils": "^0.2.3", + "jsonc-eslint-parser": "^3.1.0", + "package-json-validator": "^1.5.0", + "semver": "^7.7.3", + "sort-object-keys": "^2.0.0", + "sort-package-json": "^4.0.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + }, + "peerDependencies": { + "@eslint/json": ">=1.0.0", + "@types/estree": ">=1.0.0", + "eslint": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@eslint/json": { + "optional": true + }, + "@types/estree": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", @@ -1762,6 +1880,16 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/git-hooks-list": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz", + "integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1799,6 +1927,19 @@ "node": ">=8" } }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1880,6 +2021,19 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -1940,6 +2094,24 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonc-eslint-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-3.1.0.tgz", + "integrity": "sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2029,6 +2201,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -2076,6 +2258,22 @@ "dev": true, "license": "MIT" }, + "node_modules/npm-package-arg": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", + "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -2166,6 +2364,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-validator": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-1.5.2.tgz", + "integrity": "sha512-eHXskJQU4aCiSfjhRfTVtCJ+22/EzLHgYgZv5Gj3teb3NJrnTMzq5BnKAWKvR+PLpknCO1PmOCImDuO+dX4Vaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "npm-package-arg": "^13.0.2", + "semver": "^7.7.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^7.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2209,6 +2423,16 @@ "node": ">= 0.8.0" } }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/property-expr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", @@ -2356,6 +2580,71 @@ "dev": true, "license": "ISC" }, + "node_modules/sort-object-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz", + "integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/sort-package-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-4.0.0.tgz", + "integrity": "sha512-6aYOlYI9AWioZ+rzu+4zKLmoFqJP0/fHDxrd7X04yqEibikY+5YVF0EYlyGn4v6X2PJY7yAUWV7oeP+i5rOm/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-indent": "^7.0.2", + "detect-newline": "^4.0.1", + "git-hooks-list": "^4.1.1", + "is-plain-obj": "^4.1.0", + "semver": "^7.7.3", + "sort-object-keys": "^2.0.1", + "tinyglobby": "^0.2.15" + }, + "bin": { + "sort-package-json": "cli.js" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -2618,6 +2907,27 @@ "dev": true, "license": "MIT" }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index 88e4360..cfaad51 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,7 @@ "benchmark": "npm run build && node benchmark.js", "lint": "eslint . --fix", "build": "tsc", - "validate": "npm run check:package-json && npm run build && npm run lint && npm test", - "check:package-json": "node scripts/check-no-file-deps.mjs" + "validate": "npm run build && npm run lint && npm test" }, "peerDependencies": { "typescript": ">=5.0.0" @@ -50,6 +49,7 @@ "ajv": "^8.18.0", "ajv-formats": "3.0.1", "eslint": "^10.0.0", + "eslint-plugin-package-json": "^1.6.1", "joi": "18.0.1", "kizu": "^4.0.0", "ts-node": "^10.9.0", diff --git a/scripts/check-no-file-deps.mjs b/scripts/check-no-file-deps.mjs deleted file mode 100644 index 7dec517..0000000 --- a/scripts/check-no-file-deps.mjs +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env node -/** - * Fail if package.json declares file: or file:// dependency specs. - * Canonical copy: infra/ci-cd/check-no-file-deps.mjs - * Service repos ship an identical copy at scripts/check-no-file-deps.mjs for CI. - * - * Local monorepo dev uses npm link — never commit file: paths. - */ -import { readFileSync } from "node:fs"; -import { join } from "node:path"; - -const DEP_FIELDS = [ - "dependencies", - "devDependencies", - "optionalDependencies", - "peerDependencies", - "overrides", -]; - -const pkgPath = join(process.cwd(), "package.json"); -let pkg; -try { - pkg = JSON.parse(readFileSync(pkgPath, "utf8")); -} catch (err) { - console.error(`check-no-file-deps: failed to read ${pkgPath}: ${err.message}`); - process.exit(1); -} - -const violations = []; - -function isFileSpec(value) { - return typeof value === "string" && (value.startsWith("file:") || value.startsWith("file://")); -} - -function checkDeps(deps, path) { - if (!deps || typeof deps !== "object" || Array.isArray(deps)) { - return; - } - for (const [name, version] of Object.entries(deps)) { - if (isFileSpec(version)) { - violations.push(`${path}.${name}: ${JSON.stringify(version)}`); - } else if (version && typeof version === "object") { - checkDeps(version, `${path}.${name}`); - } - } -} - -for (const field of DEP_FIELDS) { - checkDeps(pkg[field], field); -} - -if (violations.length > 0) { - console.error("package.json must not use file: or file:// dependency specs."); - console.error("Use npm link for local development instead."); - for (const v of violations) { - console.error(` ${v}`); - } - process.exit(1); -} - -console.log("check-no-file-deps: ok"); From c519db5abb372a02b197fb04ff09335c18d30306 Mon Sep 17 00:00:00 2001 From: Marc Weiner Date: Fri, 31 Jul 2026 00:07:04 -0400 Subject: [PATCH 5/6] fix: standardize CI job name to ci with typecheck in matrix Drop custom job display name so branch rulesets can require ci (N). Single matrix job: lint, tsc --noEmit, build, test. --- .github/workflows/ci.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddf6da5..cd1315b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,6 @@ on: jobs: ci: - name: CI (Node ${{ matrix.node-version }}) runs-on: ubuntu-latest timeout-minutes: 10 strategy: @@ -25,11 +24,14 @@ jobs: - name: Install dependencies run: npm ci - - name: Build - run: npm run build - - name: Lint run: npm run lint - - name: Test with coverage + - name: Type check + run: npx tsc --noEmit + + - name: Build + run: npm run build + + - name: Test run: npm test From 7752136f7722d58c9381695f4d7f31854ed7b4b9 Mon Sep 17 00:00:00 2001 From: Marc Weiner Date: Fri, 31 Jul 2026 00:15:47 -0400 Subject: [PATCH 6/6] chore: drop eslint no-local-deps rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm ci on CI runners fails on committed file: paths — no lint rule needed. --- eslint.config.mjs | 14 --- package-lock.json | 310 ---------------------------------------------- package.json | 1 - 3 files changed, 325 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 4c88e99..60a03be 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,6 +1,4 @@ import tseslint from 'typescript-eslint'; -import packageJson from 'eslint-plugin-package-json'; -import * as jsoncParser from 'jsonc-eslint-parser'; export default tseslint.config( { @@ -138,16 +136,4 @@ export default tseslint.config( '@typescript-eslint/no-explicit-any': 'off', }, }, - { - files: ['package.json'], - languageOptions: { - parser: jsoncParser, - }, - plugins: { - 'package-json': packageJson, - }, - rules: { - 'package-json/no-local-dependencies': ['error', {ignorePrivate: false}], - }, - }, ); diff --git a/package-lock.json b/package-lock.json index 0f0719f..44b0b2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "ajv": "^8.18.0", "ajv-formats": "3.0.1", "eslint": "^10.0.0", - "eslint-plugin-package-json": "^1.6.1", "joi": "18.0.1", "kizu": "^4.0.0", "ts-node": "^10.9.0", @@ -30,13 +29,6 @@ } } }, - "node_modules/@altano/repository-tools": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@altano/repository-tools/-/repository-tools-2.0.3.tgz", - "integrity": "sha512-cSR/ZYDF6Wp9OeAJMyLYYN1GenAAhV17W+w38ELP+3c5Ltsy9jkkCymi33nz/qnXyef3n6Fbr1h2yt3dvUN5sQ==", - "dev": true, - "license": "ISC" - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -1258,13 +1250,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/change-case": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", - "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", - "dev": true, - "license": "MIT" - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -1398,32 +1383,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/detect-indent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", - "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-newline": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz", - "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/diff": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", @@ -1546,83 +1505,6 @@ } } }, - "node_modules/eslint-fix-utils": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/eslint-fix-utils/-/eslint-fix-utils-0.4.3.tgz", - "integrity": "sha512-EKzNhsNavV5DdkHVltHBM9TqfsonCmiUhOm1Ra97zo03M9IAx3wtM1eC27eWGMj/D3LrALmHHNe1QlQH1P7Nxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/estree": ">=1", - "eslint": ">=8" - }, - "peerDependenciesMeta": { - "@types/estree": { - "optional": true - } - } - }, - "node_modules/eslint-json-compat-utils": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/eslint-json-compat-utils/-/eslint-json-compat-utils-0.2.3.tgz", - "integrity": "sha512-RbBmDFyu7FqnjE8F0ZxPNzx5UaptdeS9Uu50r7A+D7s/+FCX+ybiyViYEgFUaFIFqSWJgZRTpL5d8Kanxxl2lQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esquery": "^1.6.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": "*", - "jsonc-eslint-parser": "^2.4.0 || ^3.0.0" - }, - "peerDependenciesMeta": { - "@eslint/json": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-package-json": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-package-json/-/eslint-plugin-package-json-1.6.1.tgz", - "integrity": "sha512-iKcIfIYZXXb20vghnigDUemIISW9D2dMPuDZ+IGRnIOPa53+ZwU1HnsvsbSB7zGq4MAo1V9ZYfL0uTEVpCFEiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@altano/repository-tools": "^2.0.1", - "change-case": "^5.4.4", - "detect-indent": "^7.0.2", - "detect-newline": "^4.0.1", - "eslint-fix-utils": "~0.4.3", - "eslint-json-compat-utils": "^0.2.3", - "jsonc-eslint-parser": "^3.1.0", - "package-json-validator": "^1.5.0", - "semver": "^7.7.3", - "sort-object-keys": "^2.0.0", - "sort-package-json": "^4.0.0" - }, - "engines": { - "node": "^22.22.2 || >=24.15.0" - }, - "peerDependencies": { - "@eslint/json": ">=1.0.0", - "@types/estree": ">=1.0.0", - "eslint": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@eslint/json": { - "optional": true - }, - "@types/estree": { - "optional": true - } - } - }, "node_modules/eslint-scope": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", @@ -1880,16 +1762,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/git-hooks-list": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.2.1.tgz", - "integrity": "sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/fisker/git-hooks-list?sponsor=1" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1927,19 +1799,6 @@ "node": ">=8" } }, - "node_modules/hosted-git-info": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", - "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2021,19 +1880,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -2094,24 +1940,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonc-eslint-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-3.1.0.tgz", - "integrity": "sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.5.0", - "eslint-visitor-keys": "^5.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2201,16 +2029,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -2258,22 +2076,6 @@ "dev": true, "license": "MIT" }, - "node_modules/npm-package-arg": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", - "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", - "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^9.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^7.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -2364,22 +2166,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json-validator": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-1.5.2.tgz", - "integrity": "sha512-eHXskJQU4aCiSfjhRfTVtCJ+22/EzLHgYgZv5Gj3teb3NJrnTMzq5BnKAWKvR+PLpknCO1PmOCImDuO+dX4Vaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "npm-package-arg": "^13.0.2", - "semver": "^7.7.2", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^7.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2423,16 +2209,6 @@ "node": ">= 0.8.0" } }, - "node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/property-expr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", @@ -2580,71 +2356,6 @@ "dev": true, "license": "ISC" }, - "node_modules/sort-object-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz", - "integrity": "sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/sort-package-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-4.0.0.tgz", - "integrity": "sha512-6aYOlYI9AWioZ+rzu+4zKLmoFqJP0/fHDxrd7X04yqEibikY+5YVF0EYlyGn4v6X2PJY7yAUWV7oeP+i5rOm/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-indent": "^7.0.2", - "detect-newline": "^4.0.1", - "git-hooks-list": "^4.1.1", - "is-plain-obj": "^4.1.0", - "semver": "^7.7.3", - "sort-object-keys": "^2.0.1", - "tinyglobby": "^0.2.15" - }, - "bin": { - "sort-package-json": "cli.js" - }, - "engines": { - "node": ">=22" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", - "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -2907,27 +2618,6 @@ "dev": true, "license": "MIT" }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", - "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index cfaad51..ebadc84 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "ajv": "^8.18.0", "ajv-formats": "3.0.1", "eslint": "^10.0.0", - "eslint-plugin-package-json": "^1.6.1", "joi": "18.0.1", "kizu": "^4.0.0", "ts-node": "^10.9.0",