From 1db6a25498e5a7f3447771bf2ae310c61ec821ba Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 12:00:08 +0200 Subject: [PATCH 01/10] chore(dependencies): bump @types/node to ^22.18.0 and tsx to ^4.20.5 --- package.json | 4 ++-- pnpm-lock.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ac647d2..d5d2398 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@sveltejs/kit": "^2.37.0", "@sveltejs/vite-plugin-svelte": "^6.1.3", "@tailwindcss/vite": "^4.1.12", - "@types/node": "^22.0.0", + "@types/node": "^22.18.0", "eslint": "^9.34.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.11.0", @@ -57,7 +57,7 @@ "svelte": "^5.38.6", "svelte-check": "^4.3.1", "tailwindcss": "^4.1.12", - "tsx": "^4.19.2", + "tsx": "^4.20.5", "typescript": "^5.9.2", "typescript-eslint": "^8.41.0", "vite": "^7.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4798852..a474f9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,7 +27,7 @@ importers: specifier: ^4.1.12 version: 4.1.12(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) '@types/node': - specifier: ^22.0.0 + specifier: ^22.18.0 version: 22.18.0 eslint: specifier: ^9.34.0 @@ -63,7 +63,7 @@ importers: specifier: ^4.1.12 version: 4.1.12 tsx: - specifier: ^4.19.2 + specifier: ^4.20.5 version: 4.20.5 typescript: specifier: ^5.9.2 From 487907809be2748c366d5acd116abf7d60bd5c41 Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:24:29 +0200 Subject: [PATCH 02/10] docs: update description --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5d2398..77f12d7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "cron-builder-parser", "private": true, "version": "1.3.0", - "description": "Strict POSIX cron: Builder & Parser (SvelteKit app)", + "description": "Strict POSIX cron: Builder & Parser", "author": "Daschi (https://github.com/Daschi1)", "repository": { "type": "git", From 63f97a6204166e862557eb1d16b9fb6d0bcf5607 Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:24:39 +0200 Subject: [PATCH 03/10] docs: fix repository url --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77f12d7..7d30b7a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "author": "Daschi (https://github.com/Daschi1)", "repository": { "type": "git", - "url": "git+https://github.com/Daschi1/cron-builder-parser.git" + "url": "https://github.com/Daschi1/cron-builder-parser.git" }, "bugs": { "url": "https://github.com/Daschi1/cron-builder-parser/issues" From 2a2c1be0aff50bbd60ab4fd3e395a3d72078ee4c Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:09:12 +0200 Subject: [PATCH 04/10] refactor: major rework of licenses page --- Dockerfile | 1 + package.json | 11 +- pnpm-lock.yaml | 628 +++++---------------- scripts/generate-licenses.ts | 916 ++++++++++++++++++++++--------- src/lib/ui/Input.svelte | 74 ++- src/routes/licenses/+page.svelte | 92 +++- src/routes/licenses/+page.ts | 47 +- 7 files changed, 950 insertions(+), 819 deletions(-) diff --git a/Dockerfile b/Dockerfile index b0c0763..ec223e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY . . # Install deps from store and build (postinstall will automatically generate licenses.json) RUN pnpm install --frozen-lockfile --offline \ + && pnpm gen:licenses \ && pnpm build \ && pnpm prune --prod diff --git a/package.json b/package.json index 7d30b7a..30a9774 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,11 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "format": "prettier --write .", "lint": "prettier --check . && eslint .", - "gen:licenses": "tsx scripts/generate-licenses.ts", - "postinstall": "pnpm run gen:licenses" + "clean:pnpm": "node -e \"const fs=require('fs');for (const p of ['node_modules','pnpm-lock.yaml','.pnpm-state','.pnpm-state.json']) { try { fs.rmSync(p,{recursive:true,force:true}); } catch {} }\"", + "clean:store": "pnpm store prune || echo WARN: skipping pnpm store prune (EPERM or locked); continuing", + "reinstall": "pnpm install --no-frozen-lockfile --force", + "gen:licenses": "tsx scripts/generate-licenses.ts --out=static/licenses.json --debug", + "gen:licenses:fresh": "pnpm run clean:pnpm && pnpm run clean:store && pnpm run reinstall && pnpm run gen:licenses" }, "devDependencies": { "@eslint/compat": "^1.3.2", @@ -50,7 +53,6 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.11.0", "globals": "^16.3.0", - "license-checker-rseidelsohn": "^4.4.2", "prettier": "^3.6.2", "prettier-plugin-svelte": "^3.4.0", "prettier-plugin-tailwindcss": "^0.6.14", @@ -61,7 +63,8 @@ "typescript": "^5.9.2", "typescript-eslint": "^8.41.0", "vite": "^7.1.3", - "vite-plugin-devtools-json": "^1.0.0" + "vite-plugin-devtools-json": "^1.0.0", + "yaml": "^2.8.1" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a474f9d..14055a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,16 +16,16 @@ importers: version: 9.34.0 '@sveltejs/adapter-node': specifier: ^5.3.1 - version: 5.3.1(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))) + version: 5.3.1(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))) '@sveltejs/kit': specifier: ^2.37.0 - version: 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + version: 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': specifier: ^6.1.3 - version: 6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + version: 6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.12 - version: 4.1.12(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + version: 4.1.12(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) '@types/node': specifier: ^22.18.0 version: 22.18.0 @@ -41,9 +41,6 @@ importers: globals: specifier: ^16.3.0 version: 16.3.0 - license-checker-rseidelsohn: - specifier: ^4.4.2 - version: 4.4.2 prettier: specifier: ^3.6.2 version: 3.6.2 @@ -73,10 +70,13 @@ importers: version: 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.9.2) vite: specifier: ^7.1.3 - version: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + version: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) vite-plugin-devtools-json: specifier: ^1.0.0 - version: 1.0.0(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + version: 1.0.0(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) + yaml: + specifier: ^2.8.1 + version: 2.8.1 packages: @@ -303,10 +303,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -339,14 +335,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@npmcli/fs@3.1.1': - resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -386,103 +374,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.47.1': - resolution: {integrity: sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g==} + '@rollup/rollup-android-arm-eabi@4.49.0': + resolution: {integrity: sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.47.1': - resolution: {integrity: sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA==} + '@rollup/rollup-android-arm64@4.49.0': + resolution: {integrity: sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.47.1': - resolution: {integrity: sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ==} + '@rollup/rollup-darwin-arm64@4.49.0': + resolution: {integrity: sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.47.1': - resolution: {integrity: sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ==} + '@rollup/rollup-darwin-x64@4.49.0': + resolution: {integrity: sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.47.1': - resolution: {integrity: sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw==} + '@rollup/rollup-freebsd-arm64@4.49.0': + resolution: {integrity: sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.47.1': - resolution: {integrity: sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA==} + '@rollup/rollup-freebsd-x64@4.49.0': + resolution: {integrity: sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.47.1': - resolution: {integrity: sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A==} + '@rollup/rollup-linux-arm-gnueabihf@4.49.0': + resolution: {integrity: sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.47.1': - resolution: {integrity: sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q==} + '@rollup/rollup-linux-arm-musleabihf@4.49.0': + resolution: {integrity: sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.47.1': - resolution: {integrity: sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA==} + '@rollup/rollup-linux-arm64-gnu@4.49.0': + resolution: {integrity: sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.47.1': - resolution: {integrity: sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA==} + '@rollup/rollup-linux-arm64-musl@4.49.0': + resolution: {integrity: sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.47.1': - resolution: {integrity: sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg==} + '@rollup/rollup-linux-loongarch64-gnu@4.49.0': + resolution: {integrity: sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.47.1': - resolution: {integrity: sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ==} + '@rollup/rollup-linux-ppc64-gnu@4.49.0': + resolution: {integrity: sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.47.1': - resolution: {integrity: sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow==} + '@rollup/rollup-linux-riscv64-gnu@4.49.0': + resolution: {integrity: sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.47.1': - resolution: {integrity: sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw==} + '@rollup/rollup-linux-riscv64-musl@4.49.0': + resolution: {integrity: sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.47.1': - resolution: {integrity: sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ==} + '@rollup/rollup-linux-s390x-gnu@4.49.0': + resolution: {integrity: sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.47.1': - resolution: {integrity: sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g==} + '@rollup/rollup-linux-x64-gnu@4.49.0': + resolution: {integrity: sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.47.1': - resolution: {integrity: sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ==} + '@rollup/rollup-linux-x64-musl@4.49.0': + resolution: {integrity: sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.47.1': - resolution: {integrity: sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA==} + '@rollup/rollup-win32-arm64-msvc@4.49.0': + resolution: {integrity: sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.47.1': - resolution: {integrity: sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ==} + '@rollup/rollup-win32-ia32-msvc@4.49.0': + resolution: {integrity: sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.47.1': - resolution: {integrity: sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA==} + '@rollup/rollup-win32-x64-msvc@4.49.0': + resolution: {integrity: sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==} cpu: [x64] os: [win32] @@ -691,10 +679,6 @@ packages: resolution: {integrity: sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - abbrev@2.0.0: - resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -708,22 +692,10 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.0: - resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -731,10 +703,6 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-find-index@1.0.2: - resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} - engines: {node: '>=0.10.0'} - axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -821,15 +789,6 @@ packages: devalue@5.3.2: resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} @@ -954,10 +913,6 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -977,10 +932,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1003,10 +954,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hosted-git-info@6.1.3: - resolution: {integrity: sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1031,10 +978,6 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1055,9 +998,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@2.5.1: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true @@ -1069,10 +1009,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1093,11 +1029,6 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - license-checker-rseidelsohn@4.4.2: - resolution: {integrity: sha512-Sf8WaJhd2vELvCne+frS9AXqnY/vv591s2/nZcJDwTnoNgltG4mAmoenffVb8L2YPRYbxARLyrHJBC38AVfpuA==} - engines: {node: '>=18', npm: '>=8'} - hasBin: true - lightningcss-darwin-arm64@1.30.1: resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} @@ -1173,19 +1104,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - magic-string@0.30.18: resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} @@ -1212,11 +1133,6 @@ packages: resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} engines: {node: '>= 18'} - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - mkdirp@3.0.1: resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} engines: {node: '>=10'} @@ -1241,19 +1157,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - nopt@7.2.1: - resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - normalize-package-data@5.0.0: - resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1266,9 +1169,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1284,10 +1184,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1414,15 +1310,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - read-installed-packages@2.0.1: - resolution: {integrity: sha512-t+fJOFOYaZIjBpTVxiV8Mkt7yQyy4E6MSrrnt5FmPd4enYvpU/9DYGirDmN1XQwkfeuWIhM/iu0t2rm6iSr0CA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - read-package-json@6.0.4: - resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - deprecated: This package is no longer supported. Please use @npmcli/package-json instead. - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -1443,8 +1330,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.47.1: - resolution: {integrity: sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg==} + rollup@4.49.0: + resolution: {integrity: sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1471,58 +1358,14 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - sirv@3.0.1: resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} engines: {node: '>=18'} - slide@1.1.6: - resolution: {integrity: sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - spdx-compare@1.0.0: - resolution: {integrity: sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} - - spdx-ranges@2.1.1: - resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==} - - spdx-satisfies@5.0.1: - resolution: {integrity: sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1579,10 +1422,6 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - treeify@1.1.0: - resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} - engines: {node: '>=0.6'} - ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -1623,9 +1462,6 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vite-plugin-devtools-json@1.0.0: resolution: {integrity: sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw==} peerDependencies: @@ -1688,14 +1524,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -1704,6 +1532,11 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1852,15 +1685,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.2 @@ -1896,18 +1720,11 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@npmcli/fs@3.1.1': - dependencies: - semver: 7.7.2 - - '@pkgjs/parseargs@0.11.0': - optional: true - '@polka/url@1.0.0-next.29': {} - '@rollup/plugin-commonjs@28.0.6(rollup@4.47.1)': + '@rollup/plugin-commonjs@28.0.6(rollup@4.49.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.47.1) + '@rollup/pluginutils': 5.2.0(rollup@4.49.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -1915,90 +1732,90 @@ snapshots: magic-string: 0.30.18 picomatch: 4.0.3 optionalDependencies: - rollup: 4.47.1 + rollup: 4.49.0 - '@rollup/plugin-json@6.1.0(rollup@4.47.1)': + '@rollup/plugin-json@6.1.0(rollup@4.49.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.47.1) + '@rollup/pluginutils': 5.2.0(rollup@4.49.0) optionalDependencies: - rollup: 4.47.1 + rollup: 4.49.0 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.47.1)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.49.0)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.47.1) + '@rollup/pluginutils': 5.2.0(rollup@4.49.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.47.1 + rollup: 4.49.0 - '@rollup/pluginutils@5.2.0(rollup@4.47.1)': + '@rollup/pluginutils@5.2.0(rollup@4.49.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.47.1 + rollup: 4.49.0 - '@rollup/rollup-android-arm-eabi@4.47.1': + '@rollup/rollup-android-arm-eabi@4.49.0': optional: true - '@rollup/rollup-android-arm64@4.47.1': + '@rollup/rollup-android-arm64@4.49.0': optional: true - '@rollup/rollup-darwin-arm64@4.47.1': + '@rollup/rollup-darwin-arm64@4.49.0': optional: true - '@rollup/rollup-darwin-x64@4.47.1': + '@rollup/rollup-darwin-x64@4.49.0': optional: true - '@rollup/rollup-freebsd-arm64@4.47.1': + '@rollup/rollup-freebsd-arm64@4.49.0': optional: true - '@rollup/rollup-freebsd-x64@4.47.1': + '@rollup/rollup-freebsd-x64@4.49.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.47.1': + '@rollup/rollup-linux-arm-gnueabihf@4.49.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.47.1': + '@rollup/rollup-linux-arm-musleabihf@4.49.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.47.1': + '@rollup/rollup-linux-arm64-gnu@4.49.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.47.1': + '@rollup/rollup-linux-arm64-musl@4.49.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.47.1': + '@rollup/rollup-linux-loongarch64-gnu@4.49.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.47.1': + '@rollup/rollup-linux-ppc64-gnu@4.49.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.47.1': + '@rollup/rollup-linux-riscv64-gnu@4.49.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.47.1': + '@rollup/rollup-linux-riscv64-musl@4.49.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.47.1': + '@rollup/rollup-linux-s390x-gnu@4.49.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.47.1': + '@rollup/rollup-linux-x64-gnu@4.49.0': optional: true - '@rollup/rollup-linux-x64-musl@4.47.1': + '@rollup/rollup-linux-x64-musl@4.49.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.47.1': + '@rollup/rollup-win32-arm64-msvc@4.49.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.47.1': + '@rollup/rollup-win32-ia32-msvc@4.49.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.47.1': + '@rollup/rollup-win32-x64-msvc@4.49.0': optional: true '@standard-schema/spec@1.0.0': {} @@ -2007,19 +1824,19 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-node@5.3.1(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))': + '@sveltejs/adapter-node@5.3.1(@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))': dependencies: - '@rollup/plugin-commonjs': 28.0.6(rollup@4.47.1) - '@rollup/plugin-json': 6.1.0(rollup@4.47.1) - '@rollup/plugin-node-resolve': 16.0.1(rollup@4.47.1) - '@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) - rollup: 4.47.1 + '@rollup/plugin-commonjs': 28.0.6(rollup@4.49.0) + '@rollup/plugin-json': 6.1.0(rollup@4.49.0) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.49.0) + '@sveltejs/kit': 2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) + rollup: 4.49.0 - '@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + '@sveltejs/kit@2.37.0(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + '@sveltejs/vite-plugin-svelte': 6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 @@ -2032,27 +1849,27 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 5.38.6 - vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) - '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + '@sveltejs/vite-plugin-svelte': 6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) debug: 4.4.1 svelte: 5.38.6 - vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + '@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.1.3(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)))(svelte@5.38.6)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) debug: 4.4.1 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.18 svelte: 5.38.6 - vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) - vitefu: 1.1.1(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -2120,12 +1937,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 - '@tailwindcss/vite@4.1.12(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + '@tailwindcss/vite@4.1.12(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.12 '@tailwindcss/oxide': 4.1.12 tailwindcss: 4.1.12 - vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) '@types/cookie@0.6.0': {} @@ -2232,8 +2049,6 @@ snapshots: '@typescript-eslint/types': 8.41.0 eslint-visitor-keys: 4.2.1 - abbrev@2.0.0: {} - acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -2247,22 +2062,14 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-regex@5.0.1: {} - - ansi-regex@6.2.0: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} - argparse@2.0.1: {} aria-query@5.3.2: {} - array-find-index@1.0.2: {} - axobject-query@4.1.0: {} balanced-match@1.0.2: {} @@ -2327,12 +2134,6 @@ snapshots: devalue@5.3.2: {} - eastasianwidth@0.2.0: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 @@ -2510,11 +2311,6 @@ snapshots: flatted@3.3.3: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - fsevents@2.3.3: optional: true @@ -2532,15 +2328,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - globals@14.0.0: {} globals@16.3.0: {} @@ -2555,10 +2342,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hosted-git-info@6.1.3: - dependencies: - lru-cache: 7.18.3 - ignore@5.3.2: {} ignore@7.0.5: {} @@ -2576,8 +2359,6 @@ snapshots: is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -2596,12 +2377,6 @@ snapshots: isexe@2.0.0: {} - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jiti@2.5.1: {} js-yaml@4.1.0: @@ -2610,8 +2385,6 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@3.0.2: {} - json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -2629,22 +2402,6 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - license-checker-rseidelsohn@4.4.2: - dependencies: - chalk: 4.1.2 - debug: 4.4.1 - lodash.clonedeep: 4.5.0 - mkdirp: 1.0.4 - nopt: 7.2.1 - read-installed-packages: 2.0.1 - semver: 7.7.2 - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - spdx-satisfies: 5.0.1 - treeify: 1.1.0 - transitivePeerDependencies: - - supports-color - lightningcss-darwin-arm64@1.30.1: optional: true @@ -2698,14 +2455,8 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.clonedeep@4.5.0: {} - lodash.merge@4.6.2: {} - lru-cache@10.4.3: {} - - lru-cache@7.18.3: {} - magic-string@0.30.18: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -2731,8 +2482,6 @@ snapshots: dependencies: minipass: 7.1.2 - mkdirp@1.0.4: {} - mkdirp@3.0.1: {} mri@1.2.0: {} @@ -2745,19 +2494,6 @@ snapshots: natural-compare@1.4.0: {} - nopt@7.2.1: - dependencies: - abbrev: 2.0.0 - - normalize-package-data@5.0.0: - dependencies: - hosted-git-info: 6.1.3 - is-core-module: 2.16.1 - semver: 7.7.2 - validate-npm-package-license: 3.0.4 - - npm-normalize-package-bin@3.0.1: {} - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -2775,8 +2511,6 @@ snapshots: dependencies: p-limit: 3.1.0 - package-json-from-dist@1.0.1: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -2787,11 +2521,6 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -2843,25 +2572,6 @@ snapshots: queue-microtask@1.2.3: {} - read-installed-packages@2.0.1: - dependencies: - '@npmcli/fs': 3.1.1 - debug: 4.4.1 - read-package-json: 6.0.4 - semver: 7.7.2 - slide: 1.1.6 - optionalDependencies: - graceful-fs: 4.2.11 - transitivePeerDependencies: - - supports-color - - read-package-json@6.0.4: - dependencies: - glob: 10.4.5 - json-parse-even-better-errors: 3.0.2 - normalize-package-data: 5.0.0 - npm-normalize-package-bin: 3.0.1 - readdirp@4.1.2: {} resolve-from@4.0.0: {} @@ -2876,30 +2586,30 @@ snapshots: reusify@1.1.0: {} - rollup@4.47.1: + rollup@4.49.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.47.1 - '@rollup/rollup-android-arm64': 4.47.1 - '@rollup/rollup-darwin-arm64': 4.47.1 - '@rollup/rollup-darwin-x64': 4.47.1 - '@rollup/rollup-freebsd-arm64': 4.47.1 - '@rollup/rollup-freebsd-x64': 4.47.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.47.1 - '@rollup/rollup-linux-arm-musleabihf': 4.47.1 - '@rollup/rollup-linux-arm64-gnu': 4.47.1 - '@rollup/rollup-linux-arm64-musl': 4.47.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.47.1 - '@rollup/rollup-linux-ppc64-gnu': 4.47.1 - '@rollup/rollup-linux-riscv64-gnu': 4.47.1 - '@rollup/rollup-linux-riscv64-musl': 4.47.1 - '@rollup/rollup-linux-s390x-gnu': 4.47.1 - '@rollup/rollup-linux-x64-gnu': 4.47.1 - '@rollup/rollup-linux-x64-musl': 4.47.1 - '@rollup/rollup-win32-arm64-msvc': 4.47.1 - '@rollup/rollup-win32-ia32-msvc': 4.47.1 - '@rollup/rollup-win32-x64-msvc': 4.47.1 + '@rollup/rollup-android-arm-eabi': 4.49.0 + '@rollup/rollup-android-arm64': 4.49.0 + '@rollup/rollup-darwin-arm64': 4.49.0 + '@rollup/rollup-darwin-x64': 4.49.0 + '@rollup/rollup-freebsd-arm64': 4.49.0 + '@rollup/rollup-freebsd-x64': 4.49.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.49.0 + '@rollup/rollup-linux-arm-musleabihf': 4.49.0 + '@rollup/rollup-linux-arm64-gnu': 4.49.0 + '@rollup/rollup-linux-arm64-musl': 4.49.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.49.0 + '@rollup/rollup-linux-ppc64-gnu': 4.49.0 + '@rollup/rollup-linux-riscv64-gnu': 4.49.0 + '@rollup/rollup-linux-riscv64-musl': 4.49.0 + '@rollup/rollup-linux-s390x-gnu': 4.49.0 + '@rollup/rollup-linux-x64-gnu': 4.49.0 + '@rollup/rollup-linux-x64-musl': 4.49.0 + '@rollup/rollup-win32-arm64-msvc': 4.49.0 + '@rollup/rollup-win32-ia32-msvc': 4.49.0 + '@rollup/rollup-win32-x64-msvc': 4.49.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -2920,66 +2630,14 @@ snapshots: shebang-regex@3.0.0: {} - signal-exit@4.1.0: {} - sirv@3.0.1: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 - slide@1.1.6: {} - source-map-js@1.2.1: {} - spdx-compare@1.0.0: - dependencies: - array-find-index: 1.0.2 - spdx-expression-parse: 3.0.1 - spdx-ranges: 2.1.1 - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.22 - - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 - - spdx-license-ids@3.0.22: {} - - spdx-ranges@2.1.1: {} - - spdx-satisfies@5.0.1: - dependencies: - spdx-compare: 1.0.0 - spdx-expression-parse: 3.0.1 - spdx-ranges: 2.1.1 - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.2.0 - strip-json-comments@3.1.1: {} supports-color@7.2.0: @@ -3052,8 +2710,6 @@ snapshots: totalist@3.0.1: {} - treeify@1.1.0: {} - ts-api-utils@2.1.0(typescript@5.9.2): dependencies: typescript: 5.9.2 @@ -3092,23 +2748,18 @@ snapshots: uuid@11.1.0: {} - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - vite-plugin-devtools-json@1.0.0(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)): + vite-plugin-devtools-json@1.0.0(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)): dependencies: uuid: 11.1.0 - vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) - vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5): + vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.47.1 + rollup: 4.49.0 tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.18.0 @@ -3116,10 +2767,11 @@ snapshots: jiti: 2.5.1 lightningcss: 1.30.1 tsx: 4.20.5 + yaml: 2.8.1 - vitefu@1.1.1(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)): + vitefu@1.1.1(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)): optionalDependencies: - vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + vite: 7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1) which@2.0.2: dependencies: @@ -3127,22 +2779,12 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - yallist@5.0.0: {} yaml@1.10.2: {} + yaml@2.8.1: {} + yocto-queue@0.1.0: {} zimmerframe@1.1.2: {} diff --git a/scripts/generate-licenses.ts b/scripts/generate-licenses.ts index 5f6c8e8..fe849f3 100644 --- a/scripts/generate-licenses.ts +++ b/scripts/generate-licenses.ts @@ -1,314 +1,692 @@ -/// -// Generate a consolidated licenses.json for all production dependencies -// Usage: pnpm gen:licenses -// Requires: devDependency "license-checker-rseidelsohn" and "tsx" to run - -import { createRequire } from "module"; -import { mkdir, writeFile, readdir, readFile, stat } from "fs/promises"; -import path from "path"; -import { exec } from "child_process"; - -// Types that reflect the shape we emit and partially what license-checker returns -interface LicenseCheckerPackageInfo { - licenses?: string | string[]; - repository?: string | null; - publisher?: string | null; - email?: string | null; - url?: string | null; - homepage?: string | null; - licenseFile?: string | null; -} - -export type LicensePackage = { +/** + * Dependency inventory generator for pnpm workspaces/projects. + * - Includes the root project and all transitive dependencies. + * - Cross‑platform (Windows/macOS/Linux). Safe for CI/Docker. + * - Privacy‑safe by default: absolute paths are scrubbed from output and logs. + * + * Usage + * pnpm run gen:licenses + * # or + * tsx scripts/generate-licenses.ts [flags] + * + * Flags (with defaults) + * --out= Output JSON file. Default: dependencies.full.json + * --no-enrich Skip reading installed package.json files for extra fields. + * --no-lock Ignore pnpm-lock.yaml when building the union set. + * --debug Verbose progress with samples and source paths summary. + * --print After writing the file, print all package IDs to stdout. + * --sample= In debug mode, limit sample size printed. Default: 20 + * --keep-paths Keep absolute paths in output JSON and logs (OFF by default). + * --debug-paths When --debug is enabled, also show absolute paths in logs. + * --pnpm= Explicit pnpm binary to use (e.g., "C:\\pnpm\\pnpm.exe"). + * + * Output + * JSON with metadata and a packages[] array. Each package has: + * id, name, version, license, author, description, homepage, repository, + * publisherEmail and optionally path (relative to project root unless --keep-paths). + * + * Notes + * - The generator merges information from: pnpm list, the virtual store scan, + * the root node_modules scan, pnpm-lock.yaml (unless --no-lock), and pnpm licenses. + * - To avoid leaking machine paths, path is sanitized to a project‑relative path by default. + * - Requires devDependency: yaml + */ + +import { execFile, exec } from "node:child_process"; +import { promisify } from "node:util"; +import * as fs from "node:fs/promises"; +import * as fss from "node:fs"; +import * as path from "node:path"; +import YAML from "yaml"; + +const execFileP = promisify(execFile); +const execP = promisify(exec); + +/* ----------------------------- Types ----------------------------- */ +type Json = unknown; + +interface PnpmListNode { name: string; version: string; - license: string; - repository: string | null; - publisher: string | null; - email: string | null; - url: string | null; -}; + path?: string; + private?: boolean; + dependencies?: PnpmListNode[]; +} + +interface PnpmLicenseEntry { + name: string; + version: string; + license?: string; + licenses?: string; + author?: string | { name?: string; email?: string; url?: string }; + description?: string; + homepage?: string; + repository?: string | { url?: string }; + email?: string; +} + +interface PkgJson { + name?: string; + version?: string; + description?: string; + author?: PnpmLicenseEntry["author"]; + homepage?: string; + repository?: string | { url?: string }; + license?: string; + funding?: unknown; + maintainers?: unknown; + contributors?: unknown; +} -export type LicensesPayload = { - generatedAt: string; - total: number; - byLicense: Record; - packages: LicensePackage[]; +type PnpmLockLike = { + lockfileVersion?: unknown; + packages?: Record; + snapshots?: Record; }; -const requireCjs = createRequire(import.meta.url); +interface PackageRecord { + id: string; // name@version + name: string; + version: string; + path?: string; // absolute during processing; sanitized to relative at output (unless --keep-paths) + license?: string; + author?: string; + description?: string; + homepage?: string; + repository?: string; + publisherEmail?: string; + funding?: unknown; + maintainers?: unknown; + contributors?: unknown; +} + +/* --------------------------- CLI options ------------------------- */ +const ARGV = new Set(process.argv.slice(2)); +function readArg(name: string, def?: string) { + for (const a of ARGV) if (a.startsWith(`${name}=`)) return a.slice(name.length + 1); + return def; +} +const OUT_PATH = path.resolve(process.cwd(), readArg("--out", "dependencies.full.json")!); +const ENRICH = !ARGV.has("--no-enrich"); +const USE_LOCK = !ARGV.has("--no-lock"); +const DEBUG = ARGV.has("--debug"); +const PRINT_ALL = ARGV.has("--print"); +const SAMPLE = Number(readArg("--sample", "20")) || 20; +const KEEP_PATHS = ARGV.has("--keep-paths"); +const DEBUG_PATHS = ARGV.has("--debug-paths"); + +const isWin = process.platform === "win32"; +const PNPM_OVERRIDE = readArg("--pnpm"); // e.g. --pnpm="C:\\path\\to\\pnpm.exe" +const CWD = process.cwd(); + +/* --------------------------- PNPM runner -------------------------- */ +type RunOut = { stdout: string; stderr: string }; + +function log(msg: string) { + console.log(msg); +} +function logPath(label: string, p?: string) { + if (!DEBUG) return; + const shown = KEEP_PATHS && DEBUG_PATHS ? (p ?? "") : sanitizeForDisplay(p); + log(`${label}${shown ? `: ${shown}` : ""}`); +} + +async function runPnpm(pnpmArgs: string[]): Promise { + const maxBuffer = 1024 * 1024 * 64; + + const npmExecPath = process.env.npm_execpath || process.env.npm_execPath || ""; + if (npmExecPath && /pnpm/i.test(npmExecPath)) { + if (DEBUG) log("[runPnpm] using pnpm via npm_execpath"); + if (DEBUG && DEBUG_PATHS) log(` node: ${process.execPath}`); + if (DEBUG && DEBUG_PATHS) log(` execpath: ${npmExecPath}`); + return execFileP(process.execPath, [npmExecPath, ...pnpmArgs], { encoding: "utf8", maxBuffer }); + } + + if (PNPM_OVERRIDE) { + if (DEBUG) log("[runPnpm] using explicit pnpm override"); + if (DEBUG && DEBUG_PATHS) log(` override: ${PNPM_OVERRIDE}`); + return execFileP(PNPM_OVERRIDE, pnpmArgs, { encoding: "utf8", maxBuffer, shell: isWin }); + } -function loadLicenseChecker(): { - init: ( - options: Record, - cb: (err: unknown, results: Record) => void - ) => void; -} { try { - // license-checker-rseidelsohn is CommonJS; require via createRequire - const licenseChecker = requireCjs("license-checker-rseidelsohn"); - return licenseChecker as { - init: ( - options: Record, - cb: (err: unknown, results: Record) => void - ) => void; - }; + const cmd = isWin ? "pnpm.cmd" : "pnpm"; + if (DEBUG) log(`[runPnpm] trying ${cmd}`); + return await execFileP(cmd, pnpmArgs, { encoding: "utf8", maxBuffer, shell: isWin }); } catch { - throw new Error( - 'Missing devDependency "license-checker-rseidelsohn". Install it with: pnpm add -D license-checker-rseidelsohn' - ); + /* continue to npx fallback */ } + + const npxCmd = isWin ? "npx.cmd" : "npx"; + if (DEBUG) log(`[runPnpm] falling back to ${npxCmd} pnpm ...`); + const cmdStr = `${npxCmd} pnpm ${pnpmArgs.map(escapeArg).join(" ")}`; + return execP(cmdStr, { encoding: "utf8", maxBuffer }); +} +function escapeArg(a: string) { + if (/[\s"]/g.test(a)) return `"${a.replace(/"/g, '\\"')}"`; + return a; } -function parseKey(key: string): { name: string; version: string } { - // key is typically "name@version"; scoped packages look like "@scope/name@version" - const lastAt = key.lastIndexOf("@"); - if (lastAt <= 0) return { name: key, version: "" }; - const name = key.slice(0, lastAt); - const version = key.slice(lastAt + 1); - return { name, version }; +/* --------------------------- Utilities --------------------------- */ +function normalizePkgId(name: string, version: string) { + return `${name}@${version}`; +} +function cleanseAuthor(author: PnpmLicenseEntry["author"]): string | undefined { + if (!author) return undefined; + if (typeof author === "string") return author; + const parts: string[] = []; + if (author.name) parts.push(author.name); + if (author.email) parts.push(`<${author.email}>`); + if (author.url) parts.push(author.url); + return parts.join(" ") || undefined; +} +function pickFirstDefined(...vals: (T | undefined | null | "" | false)[]) { + for (const v of vals) if (v !== undefined && v !== null && v !== "" && v !== false) return v as T; + return undefined; +} +function repoToString(repo: PnpmLicenseEntry["repository"]): string | undefined { + if (!repo) return undefined; + if (typeof repo === "string") return repo; + return repo.url; +} +function extractHomepage(pj: PkgJson): string | undefined { + const homepage = pj?.homepage; + const repo = typeof pj?.repository === "string" ? pj.repository : pj?.repository?.url; + return pickFirstDefined(homepage, repo); +} +function safeJson(s: string): T { + try { + return JSON.parse(s) as T; + } catch (e) { + const preview = s.slice(0, 500); + throw new Error(`Failed to parse JSON. Preview:\n${preview}\n\n${(e as Error).message}`); + } +} +function uniqueById(arr: T[]): T[] { + const seen = new Set(); + return arr.filter((x) => { + if (seen.has(x.id)) return false; + seen.add(x.id); + return true; + }); } -function normalizeLicense(licenses: string | string[] | undefined): string { - if (!licenses) return "UNKNOWN"; - return Array.isArray(licenses) ? licenses.join(" OR ") : String(licenses); +/* ---- privacy helpers ---- */ +function toPosix(p: string) { + return p.split(path.sep).join("/"); +} +function sanitizeForOutputPath(p?: string): string | undefined { + if (!p) return undefined; + if (KEEP_PATHS) return p; + const rel = path.relative(CWD, p); + if (!rel || rel === "") return "."; + if (rel.startsWith("..")) return undefined; // outside project -> drop + return toPosix(rel); +} +function sanitizeForDisplay(p?: string): string | undefined { + const s = sanitizeForOutputPath(p); + return s ?? "(outside project)"; +} + +/* ----------------------- Primary (pnpm list) ---------------------- */ +async function runPnpmList(): Promise { + const { stdout } = await runPnpm(["list", "--json", "--depth", "Infinity"]); + const parsed = safeJson(stdout); + const arr = Array.isArray(parsed) ? parsed : [parsed]; + return flattenTree(arr); +} +function flattenTree(nodes: PnpmListNode[]): PnpmListNode[] { + const out: PnpmListNode[] = []; + const stack = [...nodes]; + const seen = new Set(); + while (stack.length) { + const n = stack.pop()!; + if (!n?.name || !n?.version) continue; + const key = `${n.path ?? ""}::${n.name}@${n.version}`; + if (seen.has(key)) continue; + seen.add(key); + out.push(n); + if (n.dependencies) for (const d of n.dependencies) stack.push(d); + } + return out; +} +function indexPkgJsons(nodes: PnpmListNode[]): PackageRecord[] { + return nodes.map((n) => ({ + id: normalizePkgId(n.name, n.version), + name: n.name, + version: n.version, + path: n.path // absolute for now; sanitized later + })); } -async function generate(): Promise { - const start = process.cwd(); +/* ------------------------- Licenses merge ------------------------- */ +async function runPnpmLicenses(): Promise { + const { stdout } = await runPnpm(["licenses", "list", "--json", "--long"]); + const parsed = safeJson(stdout); + // Some pnpm versions wrap as { packages: [...] } + if (Array.isArray(parsed)) return parsed; + if (parsed && typeof parsed === "object") { + const maybe = (parsed as { packages?: unknown }).packages; + if (Array.isArray(maybe)) return maybe as PnpmLicenseEntry[]; + } + return []; +} +function mergeLicenses(records: PackageRecord[], licenses: PnpmLicenseEntry[]): PackageRecord[] { + const map = new Map(); + for (const l of licenses) map.set(normalizePkgId(l.name, l.version), l); + for (const r of records) { + const hit = map.get(r.id); + if (!hit) continue; + r.license = pickFirstDefined(hit.license, hit.licenses, r.license); + r.author = pickFirstDefined(cleanseAuthor(hit.author), r.author); + r.description = pickFirstDefined(hit.description, r.description); + r.homepage = pickFirstDefined(hit.homepage, repoToString(hit.repository), r.homepage); + r.repository = pickFirstDefined(repoToString(hit.repository), r.repository); + r.publisherEmail = pickFirstDefined(hit.email, r.publisherEmail); + } + return records; +} - const licenseChecker = loadLicenseChecker(); +/* -------------------- Virtual store + root scans ------------------ */ +async function getNodeModulesDir(): Promise { + const { stdout } = await runPnpm(["root"]); + const nm = stdout.trim(); + if (!nm) throw new Error("Could not resolve node_modules directory via `pnpm root`."); + return path.isAbsolute(nm) ? nm : path.resolve(CWD, nm); +} +async function resolveVirtualStoreDir(projectRootNm: string): Promise { + try { + const { stdout } = await runPnpm(["config", "get", "virtual-store-dir"]); + const v = stdout.trim(); + if (v && v !== "undefined") { + const vsd = path.isAbsolute(v) ? v : path.resolve(CWD, v); + logPath("[virtual store] from config", vsd); + return vsd; + } + } catch { + /* ignore */ + } + const vsd = path.join(projectRootNm, ".pnpm"); + logPath("[virtual store] default", vsd); + return vsd; +} - const options = { - start, - production: true, - development: true, - excludePrivatePackages: false, - direct: false, // include transitive deps as well - licenseFile: false // do not include license file path to avoid leaking absolute paths - } satisfies Record; +// Read entries under a node_modules directory (handles @scopes and symlinks) +async function collectPackagesUnderDir(dir: string): Promise { + const out: PackageRecord[] = []; + let entries: import("node:fs").Dirent[]; + try { + entries = (await fs.readdir(dir, { + withFileTypes: true + })) as unknown as import("node:fs").Dirent[]; + } catch { + return out; + } - let packages: Record = await new Promise((resolve, reject) => { - licenseChecker.init(options, (err, results) => { - if (err) return reject(err); - resolve(results ?? {}); - }); - }); + for (const ent of entries) { + const name = ent.name; + // skip bins / internals + if (name === ".bin" || name === ".pnpm") continue; - // Fallback via CLI if API seems to return only the root package - const apiKeys = Object.keys(packages ?? {}); - const likelyOnlyRoot = apiKeys.length <= 1; - if (likelyOnlyRoot) { - // Attempt via pnpm exec first - try { - const { stdout } = await new Promise<{ stdout: string; stderr: string }>( - (resolve, reject) => { - exec( - `${process.platform === "win32" ? "pnpm.cmd" : "pnpm"} exec license-checker-rseidelsohn --json --production --development`, - { cwd: start, maxBuffer: 10 * 1024 * 1024 }, - (err, stdout, stderr) => { - if (err) return reject(err); - resolve({ stdout, stderr }); - } - ); + // Handle scopes first + if (name.startsWith("@")) { + const scopeDir = path.join(dir, name); + let scoped: import("node:fs").Dirent[]; + try { + scoped = (await fs.readdir(scopeDir, { + withFileTypes: true + })) as unknown as import("node:fs").Dirent[]; + } catch { + continue; + } + for (const child of scoped) { + const childPath = path.join(scopeDir, child.name); + const pjPath = path.join(childPath, "package.json"); + if (!(await exists(pjPath))) continue; + try { + const pj = JSON.parse(await fs.readFile(pjPath, "utf8")); + if (!pj?.name || !pj?.version) continue; + out.push(fromPackageJson(pj, path.dirname(pjPath))); // absolute now; sanitize later + } catch { + /* ignore */ } - ); - const parsed = JSON.parse(stdout) as Record; - const count = parsed ? Object.keys(parsed).length : 0; - console.log(`[fallback pnpm exec] parsed ${count} entries`); - if (parsed && count > 0) { - packages = parsed; } + continue; + } + + // Non-scoped at this level (dir OR symlink to dir) + const pkgPath = path.join(dir, name); + const pjPath = path.join(pkgPath, "package.json"); + if (!(await exists(pjPath))) continue; + try { + const pj = JSON.parse(await fs.readFile(pjPath, "utf8")); + if (!pj?.name || !pj?.version) continue; + out.push(fromPackageJson(pj, path.dirname(pjPath))); // absolute now; sanitize later } catch { - // Fallback to executing the local .bin directly - try { - const bin = path.join( - start, - "node_modules", - ".bin", - process.platform === "win32" - ? "license-checker-rseidelsohn.cmd" - : "license-checker-rseidelsohn" - ); - const { stdout } = await new Promise<{ stdout: string; stderr: string }>( - (resolve, reject) => { - exec( - `"${bin}" --json --production --development`, - { cwd: start, maxBuffer: 20 * 1024 * 1024 }, - (err, stdout, stderr) => { - if (err) return reject(err); - resolve({ stdout, stderr }); - } - ); - } - ); - const parsed = JSON.parse(stdout) as Record; - const count = parsed ? Object.keys(parsed).length : 0; - console.log(`[fallback .bin] parsed ${count} entries`); - if (parsed && count > 0) { - packages = parsed; - } - } catch (e2) { - console.warn("license-checker CLI fallback failed:", (e2 as Error)?.message); + /* ignore */ + } + } + return out; +} + +async function exists(p: string) { + try { + await fs.access(p); + return true; + } catch { + return false; + } +} + +function fromPackageJson( + pj: PkgJson & { name: string; version: string }, + pkgDir?: string +): PackageRecord { + return { + id: `${pj.name}@${pj.version}`, + name: pj.name, + version: pj.version, + path: pkgDir, // absolute; sanitized for output + description: pj.description, + author: cleanseAuthor(pj.author), + homepage: extractHomepage(pj), + repository: typeof pj.repository === "string" ? pj.repository : pj?.repository?.url, + license: pj.license, + funding: pj.funding, + maintainers: pj.maintainers, + contributors: pj.contributors + }; +} + +async function scanVirtualStore(): Promise { + const nm = await getNodeModulesDir(); + const vsd = await resolveVirtualStoreDir(nm); + if (!fss.existsSync(vsd)) { + throw new Error(`Virtual store not found (did you run "pnpm install"?).`); + } + + const entries = await fs.readdir(vsd, { withFileTypes: true }); + let totalBuckets = 0; + const records: PackageRecord[] = []; + + for (const ent of entries) { + if (!ent.isDirectory()) continue; + const innerNm = path.join(vsd, ent.name, "node_modules"); + if (!(await exists(innerNm))) continue; + totalBuckets++; + const pkgs = await collectPackagesUnderDir(innerNm); + records.push(...pkgs); + } + + const uniq = uniqueById(records).sort((a, b) => a.id.localeCompare(b.id)); + if (DEBUG) + log( + `[virtual store] buckets=${totalBuckets}, scanned=${records.length}, unique=${uniq.length}` + ); + return uniq; +} + +async function scanRootNodeModules(): Promise { + const nm = await getNodeModulesDir(); + const records = await collectPackagesUnderDir(nm); + if (DEBUG) log(`[root node_modules] scanned=${records.length}`); + return uniqueById(records); +} + +/* -------------------------- Lockfile parse ------------------------ */ +async function findLockfileUpwards(startDir: string): Promise { + let dir = path.resolve(startDir); + while (true) { + const candidate = path.join(dir, "pnpm-lock.yaml"); + if (fss.existsSync(candidate)) return candidate; + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + +function parseLockKey(rawKey: string): { name: string; version: string } | null { + // Accept both: + // - "/name@1.2.3" + // - "@scope/name@1.2.3" + // - "name@1.2.3(peer@x)(peer2@y)" + // - "/@scope/name@npm:1.2.3" + let key = String(rawKey).trim(); + if (!key) return null; + // strip leading slash + if (key.startsWith("/")) key = key.slice(1); + // drop peer suffix like "(...)" if present + const parenIdx = key.indexOf("("); + if (parenIdx > -1) key = key.slice(0, parenIdx); + // last '@' separates version (works with @scopes) + const at = key.lastIndexOf("@"); + if (at <= 0 || at === key.length - 1) return null; + const name = key.slice(0, at); + let version = key.slice(at + 1); + if (version.startsWith("npm:")) version = version.slice(4); + return { name, version }; +} + +async function parseLockfile(): Promise<{ + entries: PackageRecord[]; + lockPath?: string; + lockVersion?: string; +}> { + const lockPath = await findLockfileUpwards(CWD); + if (!lockPath) { + if (DEBUG) log("[lockfile] not found"); + return { entries: [] }; + } + const text = await fs.readFile(lockPath, "utf8"); + const data = YAML.parse(text) as PnpmLockLike; + const lockVersion = String(data?.lockfileVersion ?? ""); + const out: PackageRecord[] = []; + + // v7/8: data.packages has "/name@version" keys + // v9: data.snapshots has "name@version(...)" keys, and data.packages may still exist + const maps = [data?.packages, data?.snapshots].filter(Boolean); + + for (const m of maps) { + if (m && typeof m === "object") { + for (const rawKey of Object.keys(m)) { + const parsed = parseLockKey(rawKey); + if (!parsed) continue; + out.push({ + id: `${parsed.name}@${parsed.version}`, + name: parsed.name, + version: parsed.version + }); } } + } - // If we still only see the root or nothing, fallback to scanning pnpm store structure - const stillOnlyRoot = Object.keys(packages ?? {}).length <= 1; - if (stillOnlyRoot) { - console.log( - "[fallback pnpm .pnpm scan] attempting to enumerate packages from node_modules/.pnpm" - ); - const pnpmDir = path.join(start, "node_modules", ".pnpm"); - try { - const entries = await readdir(pnpmDir, { withFileTypes: true }); - const seen = new Set(); - const result: Record = {}; - for (const ent of entries) { - if (!ent.isDirectory()) continue; - const base = path.join(pnpmDir, ent.name, "node_modules"); - try { - let hasNodeModules = false; - try { - await stat(base); - hasNodeModules = true; - } catch { - hasNodeModules = false; - } - if (!hasNodeModules) continue; - const nmEntries = await readdir(base, { withFileTypes: true }); - for (const nmEnt of nmEntries) { - if (nmEnt.name.startsWith("@") && nmEnt.isDirectory()) { - const scopeDir = path.join(base, nmEnt.name); - const scoped = await readdir(scopeDir, { withFileTypes: true }); - for (const pkgEnt of scoped) { - if (!pkgEnt.isDirectory()) continue; - const pkgDir = path.join(scopeDir, pkgEnt.name); - const pkgJsonPath = path.join(pkgDir, "package.json"); - const key = pkgDir; - if (seen.has(key)) continue; - seen.add(key); - try { - const raw = await readFile(pkgJsonPath, "utf8"); - const pj = JSON.parse(raw); - const lic = normalizeLicense(pj.license?.type ?? pj.license ?? pj.licenses); - const repository = - typeof pj.repository === "string" ? pj.repository : pj.repository?.url; - result[`${pj.name}@${pj.version}`] = { - licenses: lic, - repository: repository ?? null, - publisher: - (typeof pj.author === "string" ? pj.author : pj.author?.name) ?? null, - email: (typeof pj.author === "object" ? pj.author?.email : null) ?? null, - url: pj.homepage ?? null - }; - } catch { - // ignore unreadable or missing package.json - } - } - } else if (nmEnt.isDirectory()) { - const pkgDir = path.join(base, nmEnt.name); - const pkgJsonPath = path.join(pkgDir, "package.json"); - const key = pkgDir; - if (seen.has(key)) continue; - seen.add(key); - try { - const raw = await readFile(pkgJsonPath, "utf8"); - const pj = JSON.parse(raw); - const lic = normalizeLicense(pj.license?.type ?? pj.license ?? pj.licenses); - const repository = - typeof pj.repository === "string" ? pj.repository : pj.repository?.url; - result[`${pj.name}@${pj.version}`] = { - licenses: lic, - repository: repository ?? null, - publisher: - (typeof pj.author === "string" ? pj.author : pj.author?.name) ?? null, - email: (typeof pj.author === "object" ? pj.author?.email : null) ?? null, - url: pj.homepage ?? null - }; - } catch { - // ignore unreadable or missing package.json - } - } - } - } catch { - // ignore non-standard entries - } - } - if (Object.keys(result).length > 0) { - packages = result; - } - } catch (e3) { - console.warn("pnpm .pnpm scan fallback failed:", (e3 as Error)?.message); + const uniq = uniqueById(out).sort((a, b) => a.id.localeCompare(b.id)); + const shown = KEEP_PATHS && DEBUG_PATHS ? lockPath : sanitizeForDisplay(lockPath); + if (DEBUG) log(`[lockfile] path=${shown}, version=${lockVersion || "?"}, parsed=${uniq.length}`); + return { entries: uniq, lockPath, lockVersion }; +} + +/* ---------------------------- Enrichment -------------------------- */ +async function enrichFromInstalledPackageJson(records: PackageRecord[]): Promise { + for (const r of records) { + if (!r.path) continue; // absolute path for reading only + try { + const pjStr = await fs.readFile(path.join(r.path, "package.json"), "utf8"); + const pj = JSON.parse(pjStr); + r.description = r.description ?? pj.description; + r.author = r.author ?? cleanseAuthor(pj.author); + r.homepage = r.homepage ?? extractHomepage(pj); + r.license = r.license ?? pj.license; + if (!r.repository && pj.repository) { + r.repository = typeof pj.repository === "string" ? pj.repository : pj.repository.url; } + if (pj.funding) r.funding = pj.funding; + if (pj.maintainers) r.maintainers = pj.maintainers; + if (pj.contributors) r.contributors = pj.contributors; + } catch { + /* ignore */ } } + return records; +} - // Ensure the root project itself is included (fallback scans may omit it) +/* ----------------------- Root project injection ------------------- */ +async function readRootProjectRecord(): Promise { + const pjPath = path.resolve(CWD, "package.json"); + if (!fss.existsSync(pjPath)) return null; try { - const pj = requireCjs(path.join(start, "package.json")); - if (pj?.name && pj?.version) { - const repository = typeof pj.repository === "string" ? pj.repository : pj.repository?.url; - const rootLic = normalizeLicense( - pj.license?.type ?? pj.license ?? pj.licenses ?? (pj.private ? "UNLICENSED" : undefined) - ); - packages[`${pj.name}@${pj.version}`] = { - licenses: rootLic, - repository: repository ?? null, - publisher: (typeof pj.author === "string" ? pj.author : pj.author?.name) ?? null, - email: (typeof pj.author === "object" ? pj.author?.email : null) ?? null, - url: pj.homepage ?? null - }; - } + const pj = JSON.parse(await fs.readFile(pjPath, "utf8")); + if (!pj?.name || !pj?.version) return null; + return fromPackageJson(pj, path.dirname(pjPath)); // absolute now; sanitize later } catch { - // ignore if package.json is missing or unreadable + return null; } +} + +/* ----------------------------- Logging ---------------------------- */ +function printSample(title: string, list: PackageRecord[], n = SAMPLE) { + if (!DEBUG) return; + const ids = list.map((x) => x.id).sort((a, b) => a.localeCompare(b)); + log(`${title} sample(${Math.min(n, ids.length)} of ${ids.length}):`); + for (const id of ids.slice(0, n)) log(` - ${id}`); + if (ids.length > n) log(` ... (+${ids.length - n} more)`); +} - // Include all packages, including the root project itself - const items: LicensePackage[] = Object.entries(packages).map(([key, info]) => { - const { name, version } = parseKey(key); - const license = normalizeLicense(info.licenses); - return { - name, - version, - license, - repository: info.repository ?? null, - publisher: info.publisher ?? null, - email: info.email ?? null, - url: info.url ?? info.homepage ?? null - } satisfies LicensePackage; +/* -------------------------- Sanitization -------------------------- */ +function sanitizeRecordsForOutput(records: PackageRecord[]): PackageRecord[] { + // Do not mutate originals; clone with sanitized path + return records.map((r) => { + const clone: PackageRecord = { ...r }; + const sp = sanitizeForOutputPath(clone.path); + if (sp === undefined && !KEEP_PATHS) delete (clone as { path?: string }).path; + else if (sp !== undefined) clone.path = sp; + return clone; }); +} - // Build a summary by license type - const byLicense = items.reduce>((acc, it) => { - acc[it.license] = (acc[it.license] ?? 0) + 1; - return acc; - }, {}); +/* ------------------------------- Main ----------------------------- */ +async function main() { + const started = Date.now(); - items.sort((a, b) => a.name.localeCompare(b.name)); + // 0) Always include the root project + const rootRecord = await readRootProjectRecord(); + if (DEBUG && rootRecord) log(`[root] ${rootRecord.id}`); - return { - generatedAt: new Date().toISOString(), - total: items.length, - byLicense, - packages: items - } satisfies LicensesPayload; -} + // 1) Try pnpm list (may return only root in some envs) + let records_list: PackageRecord[] = []; + try { + const listNodes = await runPnpmList(); + records_list = indexPkgJsons(listNodes); + if (DEBUG) log(`[pnpm list] nodes=${records_list.length}`); + printSample("[pnpm list]", records_list); + } catch { + if (DEBUG) log(`[pnpm list] failed, will use fallbacks`); + } -async function main() { - const outDir = path.join(process.cwd(), "static"); - const outFile = path.join(outDir, "licenses.json"); + // 2) Virtual store + root node_modules scans + let usedVirtualStoreFallback = false; + let records_vs: PackageRecord[] = []; + let records_rootnm: PackageRecord[] = []; + if (records_list.length <= 1) { + usedVirtualStoreFallback = true; + if (DEBUG) log(`[info] Falling back to scanning virtual store...`); + records_vs = await scanVirtualStore(); + printSample("[virtual store]", records_vs); + records_rootnm = await scanRootNodeModules(); + printSample("[root node_modules]", records_rootnm); + } - const payload = await generate(); + // Union of installed sources + const installedUnion = uniqueById([...records_list, ...records_vs, ...records_rootnm]); + if (DEBUG) + log( + `[installed] pre-dedupe=${records_list.length + records_vs.length + records_rootnm.length}, unique=${installedUnion.length}` + ); + printSample("[installed union]", installedUnion); - await mkdir(outDir, { recursive: true }); - await writeFile(outFile, JSON.stringify(payload, null, 2), "utf8"); + // 3) Lockfile union (source of truth for complete set), unless disabled + let usedLock = false; + let lockEntries: PackageRecord[] = []; + if (USE_LOCK) { + usedLock = true; + const { entries } = await parseLockfile(); + lockEntries = entries; + printSample("[lockfile entries]", lockEntries); + } - console.log(`\nGenerated ${payload.total} package license entries at: ${outFile}`); + // Merge installed + lock (prefer installed metadata) + const map = new Map(); + for (const r of installedUnion) map.set(r.id, r); + for (const lr of lockEntries) if (!map.has(lr.id)) map.set(lr.id, lr); + let merged = Array.from(map.values()); + if (DEBUG) + log( + `[merge] installed=${installedUnion.length}, lock=${lockEntries.length}, merged=${merged.length}` + ); + + // 4) Merge license info (best effort) + try { + const licenseEntries = await runPnpmLicenses(); + merged = mergeLicenses(merged, licenseEntries); + } catch { + if (DEBUG) log(`[pnpm licenses] failed (continuing)`); + } + + // 5) Optional enrichment from installed package.json + if (ENRICH) merged = await enrichFromInstalledPackageJson(merged); + + // 6) Ensure root is present; dedupe; sort + if (rootRecord) merged = uniqueById([rootRecord, ...merged]); + merged = merged.sort((a, b) => a.id.localeCompare(b.id)); + + // Debug split: installed vs lock-only + if (DEBUG) { + const installedSet = new Set(installedUnion.map((r) => r.id)); + const lockOnly = merged.filter((r) => !installedSet.has(r.id)); + log( + `[summary] installedUnique=${installedUnion.length}, lockOnly=${lockOnly.length}, total=${merged.length}` + ); + printSample("[lock-only sample]", lockOnly); + } + + // 7) Sanitize paths for OUTPUT + const outputPackages = sanitizeRecordsForOutput(merged); + + // 8) Output file (root is privacy-safe ".") + const payload = { + generatedAt: new Date().toISOString(), + root: ".", // scrub absolute project path + stats: { + usedVirtualStoreFallback, + usedLockfile: usedLock, + counts: { + pnpmList: records_list.length, + virtualStore: records_vs.length, + rootNodeModules: records_rootnm.length, + installedUnion: installedUnion.length, + lockfile: lockEntries.length, + final: outputPackages.length + } + }, + packages: outputPackages + }; + + await fs.writeFile(OUT_PATH, JSON.stringify(payload, null, 2) + "\n", "utf8"); + + const ms = Date.now() - started; + const outShown = sanitizeForDisplay(OUT_PATH) || path.basename(OUT_PATH); + log( + `Wrote ${outputPackages.length} packages to ${outShown} in ${ms}ms (fallback=${usedVirtualStoreFallback}, lock=${usedLock})` + ); + + if (PRINT_ALL) { + log("\nAll package IDs:"); + for (const r of outputPackages) log(r.id); + } } -main().catch((err: unknown) => { - console.error("Failed to generate licenses.json"); - console.error((err as Error)?.stack ?? err); +main().catch((err) => { + console.error("[export-deps] ERROR:"); + console.error(err?.stack || err?.message || err); process.exit(1); }); diff --git a/src/lib/ui/Input.svelte b/src/lib/ui/Input.svelte index 6781f10..9d0770f 100644 --- a/src/lib/ui/Input.svelte +++ b/src/lib/ui/Input.svelte @@ -1,11 +1,17 @@ - +
+ + + {#if clearable && value} + + {/if} +
+ + diff --git a/src/routes/licenses/+page.svelte b/src/routes/licenses/+page.svelte index 8e887d4..a999db8 100644 --- a/src/routes/licenses/+page.svelte +++ b/src/routes/licenses/+page.svelte @@ -1,24 +1,52 @@
@@ -40,29 +68,26 @@

Total packages

-

{licenses.total}

+

{total()}

Generated

-

{new Date(licenses.generatedAt).toLocaleString()}

+

{formatDate(licenses.generatedAt)}

-
- {#each Object.entries(licenses.byLicense).sort( (a, b) => a[0].localeCompare(b[0]) ) as [lic, count] (lic)} - - {lic} - {count} - - {/each} -
Packages
-
- +
+ +
{:else}
    - {#each filtered() as p (p.name + "@" + p.version)} + {#each filtered() as p (p.id)}
  • @@ -90,7 +115,7 @@

    {p.license}{p.license ?? "N/A"}
    @@ -102,18 +127,33 @@ rel="noopener noreferrer">repo {/if} - {#if p.url} + {#if p.homepage} homepage {/if} - {#if p.publisher} - by {p.publisher} + {#if p.author} + by {p.author} + {/if} + {#if p.publisherEmail} + {p.publisherEmail} + {/if} + {#if countIfArray(p.maintainers) !== null} + maintainers: {countIfArray(p.maintainers)} + {:else if p.maintainers} + maintainers {/if}
    + {#if p.description} +

    {p.description}

    + {/if}
  • {/each} diff --git a/src/routes/licenses/+page.ts b/src/routes/licenses/+page.ts index 8b38552..2b73b65 100644 --- a/src/routes/licenses/+page.ts +++ b/src/routes/licenses/+page.ts @@ -1,31 +1,46 @@ import type { PageLoad } from "./$types"; -export type LicensePackage = { +export type LicensePkg = { + id: string; name: string; version: string; - license: string; - repository?: string | null; - url?: string | null; - publisher?: string | null; - email?: string | null; + path?: string; + license?: string; + author?: string; + description?: string; + homepage?: string; + repository?: string; + publisherEmail?: string; + funding?: unknown; + maintainers?: unknown; + contributors?: unknown; }; -export type LicensesPayload = { +export type LicensesFile = { generatedAt: string; - total: number; - byLicense: Record; - packages: LicensePackage[]; + root?: string; + stats?: { + usedVirtualStoreFallback?: boolean; + usedLockfile?: boolean; + counts?: { + pnpmList?: number; + virtualStore?: number; + rootNodeModules?: number; + installedUnion?: number; + lockfile?: number; + final?: number; + }; + }; + packages: LicensePkg[]; }; export const load: PageLoad = async ({ fetch }) => { try { - const res = await fetch("/licenses.json", { - headers: { "cache-control": "no-cache" } - }); - if (!res.ok) return { licenses: null as LicensesPayload | null }; - const data: LicensesPayload = await res.json(); + const res = await fetch("/licenses.json", { headers: { "cache-control": "no-cache" } }); + if (!res.ok) return { licenses: null as LicensesFile | null }; + const data: LicensesFile = await res.json(); return { licenses: data }; } catch { - return { licenses: null as LicensesPayload | null }; + return { licenses: null as LicensesFile | null }; } }; From 69f51d1e71b148186f1675a0471096a392aca4e1 Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:29:01 +0200 Subject: [PATCH 05/10] ci: enhance CI workflow for PR validation and quality checks --- .../build-and-publish-docker-image.yml | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-and-publish-docker-image.yml b/.github/workflows/build-and-publish-docker-image.yml index 9eaf7e7..d1bb41b 100644 --- a/.github/workflows/build-and-publish-docker-image.yml +++ b/.github/workflows/build-and-publish-docker-image.yml @@ -3,7 +3,9 @@ name: Build and Publish Docker Image on: push: - branches: [main] # Trigger when commits land on main + branches: [ main ] # Trigger when commits land on main + pull_request: + branches: [ main ] # Validate PRs targeting main workflow_dispatch: # Allow manual runs from the Actions tab # Least-privilege permissions required for this workflow @@ -26,13 +28,72 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # --- Quality gates: install and run pnpm tasks before any Docker build --- + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.15.0 + run_install: false + + - name: Install dependencies + shell: bash + run: | + echo "::group::pnpm install" + pnpm --version + pnpm install --frozen-lockfile + echo "::endgroup::" + + - name: Run format + shell: bash + run: | + echo "::group::pnpm format" + pnpm format + echo "::endgroup::" + + - name: Run lint + shell: bash + run: | + echo "::group::pnpm lint" + pnpm lint + echo "::endgroup::" + + - name: Run type check + shell: bash + run: | + echo "::group::pnpm check" + pnpm check + echo "::endgroup::" + + - name: Generate licenses + shell: bash + run: | + echo "::group::pnpm gen:licenses" + pnpm run gen:licenses + echo "::endgroup::" + + - name: Build app + shell: bash + run: | + echo "::group::pnpm build" + pnpm build + echo "::endgroup::" + - name: Set up QEMU (for multi-arch builds) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx + if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/setup-buildx-action@v3 - name: Log in to GHCR + if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -40,6 +101,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # auto-provided token with package:write - name: Extract version from package.json + if: github.event_name == 'push' && github.ref == 'refs/heads/main' id: version shell: bash run: | @@ -62,6 +124,7 @@ jobs: echo "patch=$PATCH" >> $GITHUB_OUTPUT - name: Compute tags + if: github.event_name == 'push' && github.ref == 'refs/heads/main' id: meta shell: bash run: | @@ -99,12 +162,13 @@ jobs: echo "Computed tags CSV: ${TAGS_CSV}" echo "::endgroup::" - - name: Build and push + - name: Build (and push on main) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile - push: true + push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} provenance: true # generate provenance (SLSA) metadata sbom: true # generate SBOM and attach to the image tags: ${{ steps.meta.outputs.tags }} @@ -117,12 +181,16 @@ jobs: cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}:buildcache,mode=max - name: Summarize publish details - if: always() + if: github.event_name == 'push' && github.ref == 'refs/heads/main' shell: bash run: | - echo "## Docker Image Publish Summary" >> "$GITHUB_STEP_SUMMARY" + echo "## Docker Image Build Summary" >> "$GITHUB_STEP_SUMMARY" echo "Repository: ${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}" >> "$GITHUB_STEP_SUMMARY" echo "Version: ${{ steps.version.outputs.version }}" >> "$GITHUB_STEP_SUMMARY" - echo "Tags pushed:" >> "$GITHUB_STEP_SUMMARY" - IFS=',' read -ra TAGS_ARR <<< "${{ steps.meta.outputs.tags }}" - for t in "${TAGS_ARR[@]}"; do echo "- $t" >> "$GITHUB_STEP_SUMMARY"; done + if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then + echo "Action: Pushed the following tags:" >> "$GITHUB_STEP_SUMMARY" + IFS=',' read -ra TAGS_ARR <<< "${{ steps.meta.outputs.tags }}" + for t in "${TAGS_ARR[@]}"; do echo "- $t" >> "$GITHUB_STEP_SUMMARY"; done + else + echo "Action: Build completed without publishing (pull_request or non-main)." >> "$GITHUB_STEP_SUMMARY" + fi From 19ba8a74d259276bbb905cd80766f08271906266 Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:36:36 +0200 Subject: [PATCH 06/10] ci: add GitHub release creation in Docker CI workflow --- .github/workflows/build-and-publish-docker-image.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish-docker-image.yml b/.github/workflows/build-and-publish-docker-image.yml index d1bb41b..c8f36d4 100644 --- a/.github/workflows/build-and-publish-docker-image.yml +++ b/.github/workflows/build-and-publish-docker-image.yml @@ -10,7 +10,7 @@ on: # Least-privilege permissions required for this workflow permissions: - contents: read # read repo contents (needed for checkout) + contents: write # needed to create GitHub releases (read is sufficient for checkout) packages: write # push images to GitHub Container Registry id-token: write # enable provenance/SLSA attestation by build-push-action @@ -180,6 +180,14 @@ jobs: cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}:buildcache cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}:buildcache,mode=max + - name: Create GitHub Release + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ steps.version.outputs.version }} + name: "${{ steps.version.outputs.version }} version" + generate_release_notes: true + - name: Summarize publish details if: github.event_name == 'push' && github.ref == 'refs/heads/main' shell: bash From ed59cd062e116da56d6fe1efb66e926e18c00455 Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:42:22 +0200 Subject: [PATCH 07/10] ci: consolidate workflows for CI, Docker publish, and release creation --- ...sh-docker-image.yml => ci-and-publish.yml} | 67 ++++++++----------- 1 file changed, 28 insertions(+), 39 deletions(-) rename .github/workflows/{build-and-publish-docker-image.yml => ci-and-publish.yml} (69%) diff --git a/.github/workflows/build-and-publish-docker-image.yml b/.github/workflows/ci-and-publish.yml similarity index 69% rename from .github/workflows/build-and-publish-docker-image.yml rename to .github/workflows/ci-and-publish.yml index c8f36d4..a694e91 100644 --- a/.github/workflows/build-and-publish-docker-image.yml +++ b/.github/workflows/ci-and-publish.yml @@ -1,39 +1,38 @@ -# GitHub Actions workflow: Build and publish a Docker image to GHCR on push to main -name: Build and Publish Docker Image +# GitHub Actions workflow: CI on PRs; Publish Docker image and Release on main +name: CI and Publish (Docker + Release) on: push: - branches: [ main ] # Trigger when commits land on main + branches: [main] # Run CI and publish when commits land on main pull_request: - branches: [ main ] # Validate PRs targeting main + branches: [main] # Run CI for PRs targeting main workflow_dispatch: # Allow manual runs from the Actions tab -# Least-privilege permissions required for this workflow +# Least-privilege permissions required permissions: contents: write # needed to create GitHub releases (read is sufficient for checkout) packages: write # push images to GitHub Container Registry id-token: write # enable provenance/SLSA attestation by build-push-action -# Reusable values available to all jobs/steps +# Shared values env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} # e.g. owner/repo (may be mixed-case) jobs: - build-and-push: - name: Build and push to GHCR + ci: + name: CI (format, lint, check, gen:licenses, build) runs-on: ubuntu-latest - steps: - name: Checkout uses: actions/checkout@v4 - # --- Quality gates: install and run pnpm tasks before any Docker build --- + # Quality gates with pnpm - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 22 - cache: 'pnpm' + cache: "pnpm" - name: Set up pnpm uses: pnpm/action-setup@v4 @@ -84,16 +83,22 @@ jobs: pnpm build echo "::endgroup::" + publish: + name: Publish Docker image and GitHub Release + needs: ci + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up QEMU (for multi-arch builds) - if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/setup-buildx-action@v3 - name: Log in to GHCR - if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -101,12 +106,10 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # auto-provided token with package:write - name: Extract version from package.json - if: github.event_name == 'push' && github.ref == 'refs/heads/main' id: version shell: bash run: | echo "::group::Read version from package.json" - # Read the semver (x.y.z) from package.json and split into parts VERSION=$(jq -r .version package.json) echo "Detected version: ${VERSION}" if [[ -z "$VERSION" || "$VERSION" == "null" ]]; then @@ -117,20 +120,16 @@ jobs: IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" echo "Parsed parts => MAJOR=${MAJOR}, MINOR=${MINOR}, PATCH=${PATCH}" echo "::endgroup::" - # Expose outputs for downstream steps echo "version=$VERSION" >> $GITHUB_OUTPUT echo "major=$MAJOR" >> $GITHUB_OUTPUT echo "minor=$MINOR" >> $GITHUB_OUTPUT echo "patch=$PATCH" >> $GITHUB_OUTPUT - name: Compute tags - if: github.event_name == 'push' && github.ref == 'refs/heads/main' id: meta shell: bash run: | echo "::group::Compute image reference and tags" - # Compose the full image reference and tag set - # Lowercase the owner/repo to satisfy Docker/OCI naming rules IMAGE_NAME_LOWER=$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]') IMAGE_REF=${{ env.REGISTRY }}/${IMAGE_NAME_LOWER} MAJOR=${{ steps.version.outputs.major }} @@ -144,31 +143,28 @@ jobs: echo "Using version parts => MAJOR=${MAJOR}, MINOR=${MINOR}, PATCH=${PATCH}" TAGS=( - "${IMAGE_REF}:latest" # always update latest - "${IMAGE_REF}:${MAJOR}" # major tag, e.g. 1 - "${IMAGE_REF}:${MAJOR}.${MINOR}" # minor tag, e.g. 1.2 - "${IMAGE_REF}:${VERSION}" # full semver, e.g. 1.2.3 + "${IMAGE_REF}:latest" + "${IMAGE_REF}:${MAJOR}" + "${IMAGE_REF}:${MAJOR}.${MINOR}" + "${IMAGE_REF}:${VERSION}" ) echo "Planned tags:" for t in "${TAGS[@]}"; do echo " - ${t}"; done - # Join tags with comma for build-push-action TAGS_CSV=$(IFS=, ; echo "${TAGS[*]}") echo "tags=$TAGS_CSV" >> $GITHUB_OUTPUT - # Also expose normalized values for later steps echo "image_name_lower=${IMAGE_NAME_LOWER}" >> $GITHUB_OUTPUT echo "image_ref=${IMAGE_REF}" >> $GITHUB_OUTPUT echo "Computed tags CSV: ${TAGS_CSV}" echo "::endgroup::" - - name: Build (and push on main) - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + - name: Build and push uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile - push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + push: true provenance: true # generate provenance (SLSA) metadata sbom: true # generate SBOM and attach to the image tags: ${{ steps.meta.outputs.tags }} @@ -176,12 +172,10 @@ jobs: org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} org.opencontainers.image.version=${{ steps.version.outputs.version }} - # Enable build cache stored in GHCR for faster rebuilds cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}:buildcache cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}:buildcache,mode=max - name: Create GitHub Release - if: github.event_name == 'push' && github.ref == 'refs/heads/main' uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.version.outputs.version }} @@ -189,16 +183,11 @@ jobs: generate_release_notes: true - name: Summarize publish details - if: github.event_name == 'push' && github.ref == 'refs/heads/main' shell: bash run: | echo "## Docker Image Build Summary" >> "$GITHUB_STEP_SUMMARY" echo "Repository: ${{ env.REGISTRY }}/${{ steps.meta.outputs.image_name_lower }}" >> "$GITHUB_STEP_SUMMARY" echo "Version: ${{ steps.version.outputs.version }}" >> "$GITHUB_STEP_SUMMARY" - if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then - echo "Action: Pushed the following tags:" >> "$GITHUB_STEP_SUMMARY" - IFS=',' read -ra TAGS_ARR <<< "${{ steps.meta.outputs.tags }}" - for t in "${TAGS_ARR[@]}"; do echo "- $t" >> "$GITHUB_STEP_SUMMARY"; done - else - echo "Action: Build completed without publishing (pull_request or non-main)." >> "$GITHUB_STEP_SUMMARY" - fi + echo "Action: Pushed the following tags:" >> "$GITHUB_STEP_SUMMARY" + IFS=',' read -ra TAGS_ARR <<< "${{ steps.meta.outputs.tags }}" + for t in "${TAGS_ARR[@]}"; do echo "- $t" >> "$GITHUB_STEP_SUMMARY"; done From 957858e88cc338246d0d8f9b29e28e528099507c Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:42:55 +0200 Subject: [PATCH 08/10] chore: bump version to 1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30a9774..dbfe432 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cron-builder-parser", "private": true, - "version": "1.3.0", + "version": "1.4.0", "description": "Strict POSIX cron: Builder & Parser", "author": "Daschi (https://github.com/Daschi1)", "repository": { From 3a36d878e7c3211daab61df5ad3fe816c50d1b2d Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:50:28 +0200 Subject: [PATCH 09/10] ci: dynamically determine Node.js and pnpm versions in CI workflow --- .github/workflows/ci-and-publish.yml | 40 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-and-publish.yml b/.github/workflows/ci-and-publish.yml index a694e91..e39d298 100644 --- a/.github/workflows/ci-and-publish.yml +++ b/.github/workflows/ci-and-publish.yml @@ -27,17 +27,51 @@ jobs: - name: Checkout uses: actions/checkout@v4 - # Quality gates with pnpm + - name: Read tool versions from package.json + id: tool-versions + shell: bash + run: | + echo "::group::Read Node and pnpm versions from package.json" + NODE_ENGINE=$(jq -r '.engines.node // ""' package.json) + if [[ -z "$NODE_ENGINE" || "$NODE_ENGINE" == "null" ]]; then + NODE_VERSION="22" + else + NODE_VERSION=$(echo "$NODE_ENGINE" | grep -oE '[0-9]+' | head -1) + if [[ -z "$NODE_VERSION" ]]; then NODE_VERSION="22"; fi + fi + PM=$(jq -r '.packageManager // ""' package.json) + if [[ -z "$PM" || "$PM" == "null" ]]; then + PNPM_VERSION="10.15.0" + else + PNPM_VERSION=$(echo "$PM" | sed -n 's/^pnpm@\([0-9][^ ]*\).*$/\1/p') + if [[ -z "$PNPM_VERSION" ]]; then PNPM_VERSION="10.15.0"; fi + fi + echo "Node engines: $NODE_ENGINE -> using $NODE_VERSION" + echo "packageManager: $PM -> pnpm $PNPM_VERSION" + echo "node=$NODE_VERSION" >> $GITHUB_OUTPUT + echo "pnpm=$PNPM_VERSION" >> $GITHUB_OUTPUT + echo "::endgroup::" + + - name: Enable Corepack and pnpm + shell: bash + run: | + echo "::group::Enable Corepack and prepare pnpm" + corepack enable + corepack prepare pnpm@${{ steps.tool-versions.outputs.pnpm }} --activate + pnpm --version + echo "::endgroup::" + - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version: ${{ steps.tool-versions.outputs.node }} cache: "pnpm" + cache-dependency-path: pnpm-lock.yaml - name: Set up pnpm uses: pnpm/action-setup@v4 with: - version: 10.15.0 + version: ${{ steps.tool-versions.outputs.pnpm }} run_install: false - name: Install dependencies From e4d1e9d3837ef420f6ff2c587b0ed3a8bebba592 Mon Sep 17 00:00:00 2001 From: Daschi <50054971+Daschi1@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:54:50 +0200 Subject: [PATCH 10/10] ci: configure pnpm store path for actions cache in CI workflow --- .github/workflows/ci-and-publish.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci-and-publish.yml b/.github/workflows/ci-and-publish.yml index e39d298..a41d14f 100644 --- a/.github/workflows/ci-and-publish.yml +++ b/.github/workflows/ci-and-publish.yml @@ -61,6 +61,16 @@ jobs: pnpm --version echo "::endgroup::" + - name: Configure pnpm store for actions cache + shell: bash + run: | + echo "::group::Configure pnpm store path (~/.pnpm-store)" + PNPM_STORE_DIR="$HOME/.pnpm-store" + mkdir -p "$PNPM_STORE_DIR" + pnpm config set store-dir "$PNPM_STORE_DIR" + echo "pnpm store-dir => $(pnpm config get store-dir)" + echo "::endgroup::" + - name: Set up Node.js uses: actions/setup-node@v4 with: