From ec194c8834dd775ef7cca44f2c02434002fcd42a Mon Sep 17 00:00:00 2001 From: Gerald Date: Fri, 22 May 2026 12:28:03 +0900 Subject: [PATCH] feat: add onDebug callback --- package.json | 2 - pnpm-lock.yaml | 174 ++++++++++---------------------------------- pnpm-workspace.yaml | 1 + src/bin.ts | 36 ++++++++- src/index.ts | 100 +++++++++++++++++-------- src/types.ts | 30 +++++++- 6 files changed, 170 insertions(+), 173 deletions(-) create mode 100644 pnpm-workspace.yaml diff --git a/package.json b/package.json index e0d25ec..5dc88fe 100644 --- a/package.json +++ b/package.json @@ -41,14 +41,12 @@ "dependencies": { "commander": "^14.0.0", "console-table-printer": "^2.14.6", - "debug": "^4.4.1", "es-toolkit": "^1.39.9", "jsonwebtoken": "^9.0.2", "read-package-up": "^11.0.0" }, "devDependencies": { "@eslint/js": "^9.33.0", - "@types/debug": "^4.1.12", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.2.1", "del-cli": "^6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5ffb17..cdb5618 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: console-table-printer: specifier: ^2.14.6 version: 2.14.6 - debug: - specifier: ^4.4.1 - version: 4.4.1 es-toolkit: specifier: ^1.39.9 version: 1.39.9 @@ -30,9 +27,6 @@ importers: '@eslint/js': specifier: ^9.33.0 version: 9.33.0 - '@types/debug': - specifier: ^4.1.12 - version: 4.1.12 '@types/jsonwebtoken': specifier: ^9.0.10 version: 9.0.10 @@ -44,10 +38,10 @@ importers: version: 6.0.0 eslint: specifier: ^9.33.0 - version: 9.33.0(jiti@1.21.7) + version: 9.33.0 eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.33.0(jiti@1.21.7)) + version: 10.1.8(eslint@9.33.0) husky: specifier: ^9.1.7 version: 9.1.7 @@ -59,10 +53,10 @@ importers: version: 5.9.2 typescript-eslint: specifier: ^8.39.1 - version: 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.39.1(eslint@9.33.0)(typescript@5.9.2) vite: specifier: ^7.1.2 - version: 7.1.2(@types/node@24.2.1)(jiti@1.21.7)(terser@5.43.1) + version: 7.1.2(@types/node@24.2.1) packages: @@ -288,22 +282,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -350,56 +328,67 @@ packages: resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.46.2': resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.46.2': resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.46.2': resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.46.2': resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.46.2': resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.46.2': resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.46.2': resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.46.2': resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.46.2': resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.46.2': resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.46.2': resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} @@ -420,9 +409,6 @@ packages: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -536,9 +522,6 @@ packages: buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -558,9 +541,6 @@ packages: resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} engines: {node: '>=20'} - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -782,10 +762,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} - hasBin: true - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1002,13 +978,6 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -1029,11 +998,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - terser@5.43.1: - resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} - engines: {node: '>=10'} - hasBin: true - tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -1226,9 +1190,9 @@ snapshots: '@esbuild/win32-x64@0.25.9': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.33.0(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.7.0(eslint@9.33.0)': dependencies: - eslint: 9.33.0(jiti@1.21.7) + eslint: 9.33.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -1283,30 +1247,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 - optional: true - - '@jridgewell/resolve-uri@3.1.2': - optional: true - - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 - optional: true - - '@jridgewell/sourcemap-codec@1.5.5': - optional: true - - '@jridgewell/trace-mapping@0.3.30': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - optional: true - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1381,10 +1321,6 @@ snapshots: '@sindresorhus/merge-streams@2.3.0': {} - '@types/debug@4.1.12': - dependencies: - '@types/ms': 2.1.0 - '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -1402,15 +1338,15 @@ snapshots: '@types/normalize-package-data@2.4.4': {} - '@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) + '@typescript-eslint/parser': 8.39.1(eslint@9.33.0)(typescript@5.9.2) '@typescript-eslint/scope-manager': 8.39.1 - '@typescript-eslint/type-utils': 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) + '@typescript-eslint/type-utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) '@typescript-eslint/visitor-keys': 8.39.1 - eslint: 9.33.0(jiti@1.21.7) + eslint: 9.33.0 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -1419,14 +1355,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2)': dependencies: '@typescript-eslint/scope-manager': 8.39.1 '@typescript-eslint/types': 8.39.1 '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) '@typescript-eslint/visitor-keys': 8.39.1 debug: 4.4.1 - eslint: 9.33.0(jiti@1.21.7) + eslint: 9.33.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -1449,13 +1385,13 @@ snapshots: dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.39.1(eslint@9.33.0)(typescript@5.9.2)': dependencies: '@typescript-eslint/types': 8.39.1 '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) + '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) debug: 4.4.1 - eslint: 9.33.0(jiti@1.21.7) + eslint: 9.33.0 ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: @@ -1479,13 +1415,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/utils@8.39.1(eslint@9.33.0)(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0) '@typescript-eslint/scope-manager': 8.39.1 '@typescript-eslint/types': 8.39.1 '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - eslint: 9.33.0(jiti@1.21.7) + eslint: 9.33.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -1531,9 +1467,6 @@ snapshots: buffer-equal-constant-time@1.0.1: {} - buffer-from@1.1.2: - optional: true - callsites@3.1.0: {} chalk@4.1.2: @@ -1549,9 +1482,6 @@ snapshots: commander@14.0.0: {} - commander@2.20.3: - optional: true - concat-map@0.0.1: {} console-table-printer@2.14.6: @@ -1621,9 +1551,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@1.21.7)): + eslint-config-prettier@10.1.8(eslint@9.33.0): dependencies: - eslint: 9.33.0(jiti@1.21.7) + eslint: 9.33.0 eslint-scope@8.4.0: dependencies: @@ -1634,9 +1564,9 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.33.0(jiti@1.21.7): + eslint@9.33.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.1 @@ -1671,8 +1601,6 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 - optionalDependencies: - jiti: 1.21.7 transitivePeerDependencies: - supports-color @@ -1797,9 +1725,6 @@ snapshots: isexe@2.0.0: {} - jiti@1.21.7: - optional: true - js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -2015,15 +1940,6 @@ snapshots: source-map-js@1.2.1: {} - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - optional: true - - source-map@0.6.1: - optional: true - spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -2044,14 +1960,6 @@ snapshots: dependencies: has-flag: 4.0.0 - terser@5.43.1: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.3) @@ -2071,13 +1979,13 @@ snapshots: type-fest@4.41.0: {} - typescript-eslint@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2): + typescript-eslint@8.39.1(eslint@9.33.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) - '@typescript-eslint/parser': 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) + '@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.39.1(eslint@9.33.0)(typescript@5.9.2) '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) - eslint: 9.33.0(jiti@1.21.7) + '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) + eslint: 9.33.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -2099,7 +2007,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite@7.1.2(@types/node@24.2.1)(jiti@1.21.7)(terser@5.43.1): + vite@7.1.2(@types/node@24.2.1): dependencies: esbuild: 0.25.9 fdir: 6.4.6(picomatch@4.0.3) @@ -2110,8 +2018,6 @@ snapshots: optionalDependencies: '@types/node': 24.2.1 fsevents: 2.3.3 - jiti: 1.21.7 - terser: 5.43.1 which@2.0.2: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..69abd9a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1 @@ +allowBuilds: {} diff --git a/src/bin.ts b/src/bin.ts index 226e1a0..331f81a 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -20,7 +20,7 @@ program .option('--api-key ', 'API key from AMO') .option('--api-secret ', 'API secret from AMO') .option( - '--api-url-prefix ', + '--api-url-prefix ', 'the API URL prefix, https://addons.mozilla.org if unspecified', ) .option('--addon-id ', 'addon UUID which can be found in AMO') @@ -60,6 +60,7 @@ This command could be run multiple times to check the status, and the version wi 'the compatibility info as a JSON string, e.g. `["android","firefox"]`', ) .option('--output ', 'the file path to save the signed XPI file') + .option('--verbose', 'show verbose logs') .action( wrapError(async (_, command) => { const options = { @@ -70,7 +71,7 @@ This command could be run multiple times to check the status, and the version wi const downloadedFile = await signAddon({ apiKey: options.apiKey as string, apiSecret: options.apiSecret as string, - apiPrefix: options.apiPrefix as string, + apiUrlPrefix: options.apiUrlPrefix as string, addonId: options.addonId as string, addonVersion: options.addonVersion as string, channel: options.channel as ChannelType, @@ -84,6 +85,7 @@ This command could be run multiple times to check the status, and the version wi ? (JSON.parse(options.compatibility) as CompatibilityInfo) : undefined, output: options.output as string, + onDebug: getLogHandler(options.verbose as boolean), }); console.info(downloadedFile); }), @@ -108,7 +110,10 @@ program const client = new AMOClient( options.apiKey, options.apiSecret, - options.apiPrefix, + options.apiUrlPrefix as string, + { + onDebug: getLogHandler(options.verbose as boolean), + }, ); const page = options.page ?? 1; const pageSize = options.pageSize ?? 25; @@ -185,5 +190,30 @@ function loadFromEnv() { apiSecret: process.env.AMO_SECRET, apiUrlPrefix: process.env.AMO_URL_PREFIX, addonId: process.env.AMO_ADDON_ID, + verbose: !!process.env.AMO_VERBOSE, + }; +} + +const FRIENDLY_LOGS: Record = { + 'upload-file-start': 'Uploading dist file...', + 'create-version-start': 'Creating version...', + 'update-source-start': 'Uploading source file...', + 'update-version-start': 'Updating version notes...', + 'wait-start': 'Waiting for signed file...', + 'download-start': 'Downloading...', +}; + +function getLogHandler(verbose: boolean) { + return (type: string, payload?: unknown) => { + const friendly = FRIENDLY_LOGS[type]; + if (friendly) { + console.error(`> ${friendly}`); + } + if (verbose) { + console.error(`[AMOClient] ${type}`); + if (payload) { + console.dir(payload, { depth: 3, colors: true }); + } + } }; } diff --git a/src/index.ts b/src/index.ts index 023bfc2..f818653 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,3 @@ -import debug from 'debug'; import { isEqual } from 'es-toolkit'; import jwt from 'jsonwebtoken'; import { createWriteStream, openAsBlob } from 'node:fs'; @@ -9,6 +8,7 @@ import { finished } from 'node:stream/promises'; import type { ReadableStream } from 'node:stream/web'; import { setTimeout } from 'node:timers/promises'; import type { + AMOClientOptions, ChannelType, CompatibilityInfo, SignAddonParam, @@ -18,8 +18,6 @@ import type { VersionListResponse, } from './types'; -const log = debug('amo-upload'); - export class FatalError extends Error {} export class ProcessingError extends Error {} @@ -51,7 +49,8 @@ export class AMOClient { constructor( private apiKey: string, private apiSecret: string, - public apiPrefix = 'https://addons.mozilla.org', + public apiUrlPrefix = 'https://addons.mozilla.org', + private options: AMOClientOptions = {}, ) { if (!apiKey || !apiSecret) { throw new Error('apiKey and apiSecret are required'); @@ -59,12 +58,13 @@ export class AMOClient { } private updateToken(force = false, ttl = 60) { - const now = Date.now(); + const now = Math.floor(Date.now() / 1000); if (this.tokenExpire < now || force) { const token = this.getJWT(ttl); this.headers.Authorization = `JWT ${token}`; // Refresh token a few seconds before it really expires this.tokenExpire = now + ttl - 10; + this.options.onDebug?.('token-update'); } } @@ -80,14 +80,19 @@ export class AMOClient { } private async request(url: string, opts?: RequestInit) { - const res = await this.fetch(this.apiPrefix + url, opts); + this.options.onDebug?.('request-start', { + method: opts?.method || 'GET', + url, + }); + const res = await this.fetch(this.apiUrlPrefix + url, opts); const data = (await res.json()) as T; + this.options.onDebug?.('request-end', { url, status: res.status }); if (!res.ok) throw { res, data }; return data; } async uploadFile(distFile: string, channel: ChannelType) { - log('Starting uploadFile %s', distFile); + this.options.onDebug?.('upload-file-start', { distFile }); const formData = new FormData(); formData.set('upload', await openAsBlob(distFile), basename(distFile)); formData.set('channel', channel); @@ -98,11 +103,11 @@ export class AMOClient { body: formData, }, ); + this.options.onDebug?.('upload-file-processing', { uuid }); try { - log('Start polling for the upload result'); await poll( async (i) => { - log('Polling %s', i); + this.options.onDebug?.('upload-file-poll', { times: i }); const data: UploadResponse = await this.request( `/api/v5/addons/upload/${uuid}/`, ); @@ -127,7 +132,7 @@ export class AMOClient { } throw error; } - log('Finished uploadFile %s', distFile); + this.options.onDebug?.('upload-file-end', { uuid }); return uuid; } @@ -145,7 +150,7 @@ export class AMOClient { const uploadUuid = await this.uploadFile(distFile, channel); const { approvalNotes, compatibility, releaseNotes, sourceFile } = extra || {}; - log('Starting createVersion'); + this.options.onDebug?.('create-version-start', { addonId, uploadUuid }); let versionInfo: VersionDetail = await this.request( `/api/v5/addons/addon/${addonId}/versions/`, { @@ -161,7 +166,7 @@ export class AMOClient { }), }, ); - log('Finished createVersion: %o', versionInfo); + this.options.onDebug?.('create-version-end', { id: versionInfo.id }); if (sourceFile) { versionInfo = await this.updateSource(addonId, versionInfo, sourceFile); } @@ -174,10 +179,13 @@ export class AMOClient { sourceFile: string, ) { if (versionInfo.source) { - log('Source is already uploaded, skipping'); + this.options.onDebug?.('update-source-skip', { id: versionInfo.id }); return versionInfo; } - log('Starting updateSource: %s', sourceFile); + this.options.onDebug?.('update-source-start', { + id: versionInfo.id, + sourceFile, + }); const formData = new FormData(); formData.set('source', await openAsBlob(sourceFile), basename(sourceFile)); versionInfo = await this.request( @@ -187,7 +195,7 @@ export class AMOClient { body: formData, }, ); - log('Finished updateSource: %s', sourceFile); + this.options.onDebug?.('update-source-end', { id: versionInfo.id }); return versionInfo; } @@ -218,9 +226,12 @@ export class AMOClient { updates = { ...updates, release_notes: releaseNotes }; } if (!updates) { - log('No update found, skipping patchVersion'); + this.options.onDebug?.('update-version-skip', { id: versionInfo.id }); } else { - log('Starting updateNotes: %s', versionInfo.id); + this.options.onDebug?.('update-version-start', { + id: versionInfo.id, + updates, + }); versionInfo = await this.request( `/api/v5/addons/addon/${addonId}/versions/${versionInfo.id}/`, { @@ -231,7 +242,7 @@ export class AMOClient { body: JSON.stringify(updates), }, ); - log('Finished updateNotes: %s', versionInfo.id); + this.options.onDebug?.('update-version-end', { id: versionInfo.id }); } if (sourceFile) { versionInfo = await this.updateSource(addonId, versionInfo, sourceFile); @@ -268,6 +279,35 @@ export class AMOClient { return this.getSignedFileFromDetail(versionDetail); } + async waitForSignedFile( + addonId: string, + version: string, + options: { + pollInterval: number; + maxRetry: number; + immediate?: boolean; + }, + ) { + this.options.onDebug?.('wait-start', { addonId, version }); + const result = await poll( + async (i) => { + this.options.onDebug?.('wait-poll', { times: i }); + const file = await this.getSignedFile(addonId, version); + if (!file) + throw new ProcessingError('The file has not been signed yet'); + return file; + }, + options.pollInterval, + options.maxRetry, + options.immediate, + ); + this.options.onDebug?.('wait-end', { + id: result.id, + status: result.status, + }); + return result; + } + getJWT(ttl: number) { const issuedAt = Math.floor(Date.now() / 1000); const payload = { @@ -283,6 +323,7 @@ export class AMOClient { } async downloadFile(url: string, output?: string) { + this.options.onDebug?.('download-start', { url }); const res = await this.fetch(url); const filename = url.split('/').pop() || 'noname'; if (output) { @@ -299,6 +340,7 @@ export class AMOClient { } const stream = createWriteStream(output); await finished(Readable.fromWeb(res.body as ReadableStream).pipe(stream)); + this.options.onDebug?.('download-end', { url, outFile: output }); return output; } } @@ -306,7 +348,7 @@ export class AMOClient { export async function signAddon({ apiKey, apiSecret, - apiPrefix, + apiUrlPrefix, addonId, addonVersion, channel = 'listed', @@ -320,8 +362,9 @@ export async function signAddon({ pollInterval = 30000, pollRetry = 4, pollRetryExisting = 1, + ...clientOptions }: SignAddonParam) { - const client = new AMOClient(apiKey, apiSecret, apiPrefix); + const client = new AMOClient(apiKey, apiSecret, apiUrlPrefix, clientOptions); let versionDetail: VersionDetail | undefined; try { @@ -355,17 +398,10 @@ export async function signAddon({ ); } - log('Starting polling for the signed file'); - const signedFile = await poll( - async (i) => { - log('Polling %s', i); - const file = await client.getSignedFile(addonId, addonVersion); - if (!file) throw new ProcessingError('The file has not been signed yet'); - return file; - }, + const signedFile = await client.waitForSignedFile(addonId, addonVersion, { pollInterval, - isNewVersion ? pollRetry : pollRetryExisting, - !isNewVersion, - ); - return client.downloadFile(signedFile.url, output); + maxRetry: isNewVersion ? pollRetry : pollRetryExisting, + immediate: !isNewVersion, + }); + return await client.downloadFile(signedFile.url, output); } diff --git a/src/types.ts b/src/types.ts index f12c3b0..e15637b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,6 +11,32 @@ export type CompatibilityMap = Partial< >; export type CompatibilityInfo = string[] | CompatibilityMap; +export type AMOClientActions = + | 'token-update' + | 'request-start' + | 'request-end' + | 'upload-file-start' + | 'upload-file-processing' + | 'upload-file-poll' + | 'upload-file-end' + | 'create-version-start' + | 'create-version-end' + | 'update-source-skip' + | 'update-source-start' + | 'update-source-end' + | 'update-version-skip' + | 'update-version-start' + | 'update-version-end' + | 'wait-start' + | 'wait-poll' + | 'wait-end' + | 'download-start' + | 'download-end'; + +export interface AMOClientOptions { + onDebug?: (type: AMOClientActions, payload?: unknown) => void; +} + export interface FileInfo { id: number; created: string; @@ -53,10 +79,10 @@ export interface UploadResponse { validation: object; } -export interface SignAddonParam { +export interface SignAddonParam extends AMOClientOptions { apiKey: string; apiSecret: string; - apiPrefix: string; + apiUrlPrefix: string; addonId: string; addonVersion: string; channel?: ChannelType;