From 2d0a3b613a59b31845016319002fb2fd1edf9dc9 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 16 Aug 2026 23:20:59 +0530 Subject: [PATCH] chore: expose Fleet-discoverable quality scripts Add format/lint/typecheck/test/coverage and unused/complexity/duplication/cycles wrappers so Fleet inventory is not empty. Landing types stay on astro check; native tests stay on scripts/test.sh. Closes #16 --- AGENTS.md | 3 + README.md | 5 + biome.json | 34 ++ knip.jsonc | 16 + package.json | 17 +- pnpm-lock.yaml | 687 +++++++++++++++++++++++++++++++++- scripts/check-code-health.mjs | 235 ++++++++++++ scripts/check-site.mjs | 30 +- src/content.ts | 3 +- src/pages/api/ai.ts | 21 +- src/pages/index.md.ts | 4 +- src/pages/robots.txt.ts | 9 +- src/pages/sitemap.xml.ts | 19 +- 13 files changed, 1041 insertions(+), 42 deletions(-) create mode 100644 biome.json create mode 100644 knip.jsonc create mode 100644 scripts/check-code-health.mjs diff --git a/AGENTS.md b/AGENTS.md index d08065e..3e73888 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,6 +7,9 @@ Follow `/Users/sarthak/Desktop/fleet/AGENTS.md` and the project truth in - Generate the Xcode project: `xcodegen generate` - Build the app: `./scripts/build.sh` +- Native tests: `./scripts/test.sh` or `pnpm test` +- Landing types: `pnpm typecheck` (`astro check`) +- Quality inventory: `pnpm format:check`, `pnpm lint`, `pnpm quality` - Validate the active change: `openspec validate build-indulge-visual-proof --strict` ## Native boundaries diff --git a/README.md b/README.md index 6904727..392299c 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,11 @@ pnpm build pnpm dev ``` +Fleet-facing quality scripts live in the root `package.json`: `format:check`, +`lint`, `typecheck`, `test`, `test:coverage`, `knip`, and the `quality:*` +wrappers. Landing types stay on `astro check`. Native tests and coverage stay +on `./scripts/test.sh` / XCTest. + When a verified public TestFlight URL exists, set `PUBLIC_TESTFLIGHT_URL` only in the build environment. Without it, the site deliberately shows the honest invite-only beta state. diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..f716c81 --- /dev/null +++ b/biome.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.5.5/schema.json", + "files": { + "includes": [ + "astro.config.mjs", + "scripts/**/*.mjs", + "src/**/*.ts" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "semicolons": "always", + "trailingCommas": "none" + } + }, + "json": { + "formatter": { + "enabled": false + } + }, + "linter": { + "enabled": true, + "rules": { + "preset": "recommended" + } + } +} diff --git a/knip.jsonc b/knip.jsonc new file mode 100644 index 0000000..2413d94 --- /dev/null +++ b/knip.jsonc @@ -0,0 +1,16 @@ +{ + "$schema": "https://unpkg.com/knip@6/schema.json", + "entry": [ + "scripts/**/*.mjs", + "src/pages/**/*.{astro,ts}" + ], + "project": [ + "scripts/**/*.mjs", + "src/**/*.{astro,ts}" + ], + "ignoreDependencies": [ + "@biomejs/biome", + "jscpd", + "lightningcss" + ] +} diff --git a/package.json b/package.json index 3c7229e..a6d72a8 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,26 @@ "dev": "astro dev", "build": "astro build", "check": "astro check && astro build && node scripts/check-site.mjs", - "preview": "astro preview" + "preview": "astro preview", + "format:check": "node scripts/check-code-health.mjs format", + "lint": "node scripts/check-code-health.mjs lint", + "typecheck": "node scripts/check-code-health.mjs types", + "test": "node scripts/check-code-health.mjs tests", + "test:coverage": "node scripts/check-code-health.mjs coverage", + "knip": "knip --no-exit-code --reporter symbols", + "knip:strict": "knip --reporter symbols", + "quality:unused": "node scripts/check-code-health.mjs unused", + "quality:complexity": "node scripts/check-code-health.mjs complexity", + "quality:duplication": "node scripts/check-code-health.mjs duplication", + "quality:cycles": "node scripts/check-code-health.mjs cycles", + "quality": "node scripts/check-code-health.mjs all" }, "devDependencies": { "@astrojs/check": "0.9.10", + "@biomejs/biome": "2.5.5", "astro": "7.2.0", + "jscpd": "5.0.14", + "knip": "6.32.2", "lightningcss": "1.33.0", "typescript": "6.0.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 786beea..a93e4fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,18 @@ importers: '@astrojs/check': specifier: 0.9.10 version: 0.9.10(prettier@3.9.6)(typescript@6.0.3) + '@biomejs/biome': + specifier: 2.5.5 + version: 2.5.5 astro: specifier: 7.2.0 - version: 7.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(yaml@2.9.0) + version: 7.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(jiti@2.7.0)(yaml@2.9.0) + jscpd: + specifier: 5.0.14 + version: 5.0.14 + knip: + specifier: 6.32.2 + version: 6.32.2 lightningcss: specifier: 1.33.0 version: 1.33.0 @@ -143,6 +152,63 @@ packages: resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} + '@biomejs/biome@2.5.5': + resolution: {integrity: sha512-r1S8nFsAG1MY+vJFZALzIvwXAJv6ejDQ0mxP21Tgr9YK3ZFtjrvbBwDdNhx1rUqvccEIeNg20cYCNzl6Cr69pQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.5.5': + resolution: {integrity: sha512-kUrAhXVWUrwmAUnV2iXSK7umxKFysTwvqK+Ty6ptUcLY/7T3SnCAjUowE4uvwaEej6nXZ7hu/dTtbokKdsPeag==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.5.5': + resolution: {integrity: sha512-DamiYc5bUYZ2uxlfc+RLEPtz1Abb6PO5eTbOkufLpSGwd/7AMQAdxhFYiXmwwkJL8IsT8S7GvdgwDHqaMFAvKw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.5.5': + resolution: {integrity: sha512-U4WMl/sy/E/Q73vf15VspakLRRs2LDFcCeBxJnQfXzssb88zpV6PJPaQ3ezhQ7H6Ht2/8bvuZeHgJWzmoxllZg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@2.5.5': + resolution: {integrity: sha512-lRKF/pH/1RiYiBKExi3TCZVAtvzEm77aifrvcNiDFrR9WxeAnDUjDnseb6y2XV85mjitLs6SILGm2XG77cHtSQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@2.5.5': + resolution: {integrity: sha512-m7wC7tjX5Lrmo69dc4md8FeKpPU1NTCY1v7xUoQQ2vadWwNnBS0KZOG8471otFPHrTHihQJAjQPgMObpLvDe6A==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.5.5': + resolution: {integrity: sha512-H/O39nJEw/2Zm/fm7hrmxxoF8kK/aU1uCoPp70ruXVbomaAdLpJJnCmL11Q2JotT8QVHH06So04Oq53lCSwSwQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@2.5.5': + resolution: {integrity: sha512-7BryINPuYypLUAH3o/o5ZdgomJ4zn3EDR0ChZJst7n32S6ZhKbgHXuYydLu+YAnx59ehGFR0z/MG6qnzQi3Yyw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.5.5': + resolution: {integrity: sha512-bIBFo+n6MIxdNcVFy5CrurbKiZQiUciK3bt8+O9I4wjFZNTfXLpi+giq47522eXqW5NBc9ulx7dR1SlZKi2J5g==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@bruits/satteri-darwin-arm64@0.9.5': resolution: {integrity: sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==} cpu: [arm64] @@ -228,9 +294,15 @@ packages: '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.11.3': resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} @@ -568,9 +640,234 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@oxc-parser/binding-android-arm-eabi@0.143.0': + resolution: {integrity: sha512-n9uozULWflPqBtdmI8lAabLqGKNgLVNN0ZH8HfgCwpKGNtzRzauB76jTiW/3YLkcA7N1zskpi9GdVnZuu1SAvg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.143.0': + resolution: {integrity: sha512-9BbdjHETk6O3zH/DDid9IgBtF0GlpLabNKN231uraXpRDSfY+iiZxTP5bk1Z63GBownVdhdINFIeddmMz4MzpQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.143.0': + resolution: {integrity: sha512-gh+6ecoHUy4/sUcolBl/1qPXKBbYNxFY0Pk0ujgQvINTMSftJY7o4yb8gOkDJPeZeB8+a+u7xTe6umoP8N5HFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.143.0': + resolution: {integrity: sha512-qd1hl2d+lXgHv/VQ/M9qm8TrMC5T4RqDBwtOnl+1D0QMjwcz+8AaB4JSg8STgeag0GP6a6L74XEGAsrTSJWNzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.143.0': + resolution: {integrity: sha512-M5XXcNa7aOqLPKTR41msfghKu2yQ4xWvCm11/gwU0JzOzHNk5sgW//rVEjJ+LO48+VDAMzXTSzurUVxIDKwozw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.143.0': + resolution: {integrity: sha512-T/GXusuOkPNQhCQCSBbcU/N8j0rAypuDBl1IyFK+lyYT594XsVz80clPC/OtbSSpBGyJxj8uYEfctxVuxVYoww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.143.0': + resolution: {integrity: sha512-oKu4RcBlXSqo3OC62dp6YTnQaZIurNDpCX3BnAM3+bJxt7s8J2TJKMnC0UYer1qhlRaDCg6wkTaTw+2IlsZ12w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.143.0': + resolution: {integrity: sha512-WJBbD186AZmMGaSIhlktC+rPl8L3peCTXAh88Ih9uEvK0en2mPojGyCGYiL6mHtV1RPV3JyfJW5t6n5hh0lXhA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.143.0': + resolution: {integrity: sha512-t1AcYOwEzgceadT4v5e+vaCCb0AncCA3v5AyzfBAz/tMq11qzVccXKzNHtkWdjBsgvTKwRkaUF3QvT4kot8vcQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.143.0': + resolution: {integrity: sha512-RsnO/NoD8376LMJq8JS8TwI0ieNaFRTuNe2GVJntQg6gwZNMENZsEbknHdVwjpOmxdGLGodcwaGSbAeRr5Bgjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.143.0': + resolution: {integrity: sha512-48fSVfR9TZi5CASZFyv0VC6z6BCoeihFsX031mAD/oSH7d9PYsPgIqza7d9mjP7Z2KTEpTFyH6SIu0Ui6R1vdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.143.0': + resolution: {integrity: sha512-T8CpdD+SfE01DnIOD4HpVxu0ZJOfMJ/VhCvikKfaXAxkZ+9veyLM/D2hpi7Y2hFUyPmVQO3FNZHmYzV/WlVR4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.143.0': + resolution: {integrity: sha512-QLdeMsCcacenPEFsfxnBUDF1y6opyz5+fmOz9bfD5Y7fiGCMupUCuB3KTPQhNwshIG1P9fPqar9MHxuBDd4bwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.143.0': + resolution: {integrity: sha512-659ujfqLy6k7cuH3sbzhd8b+ztSq+i6E2E9pG78Q0BmHjAExfGIdgc8cGgMdwAozDXeZFHkJ+LXYJdWsaGdgyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.143.0': + resolution: {integrity: sha512-/Mw/9j4TfZcnKphPrzOE6t4MMknXadcAAuVUlDRTF/ETWB5xOgQvOJV2Mh9We/bWxZdoxaGAdc+hy4GuYwQ2yQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.143.0': + resolution: {integrity: sha512-8rIKWR2BFuifbIK/1XB9wTaSdtuJ25dlE7ZQYDnEwj/2xH2vHsxnvIjHT3ZjSVuLLwGGlSslIG/fbOJ8TV8rTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-win32-arm64-msvc@0.143.0': + resolution: {integrity: sha512-5U9kQYMfRRI6Zq7KDxgbIP0RMnKrfn3gLepRMgJuRkPSUALTiRCk9d/uyhb4lGDjUdzwK7mBkKqhLgzBPCmLpQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.143.0': + resolution: {integrity: sha512-25P7AaHk4R88Yv2XH4gToDVmh0cOu+bEURQU10CRrmvgabfRArSGAP5osmwUKeSUHj0VS50upbpbRWWW/m7mHA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.143.0': + resolution: {integrity: sha512-ORMh3JE1s6V7ySicdRK7vgaDQnn5o+UHg9ct989PlWHbel8O9ARrmWXM6kZjrBMtNucxNayQ8g69G0VfWzhANw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-project/types@0.143.0': resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.24.2': + resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.24.2': + resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.24.2': + resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.24.2': + resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==} + cpu: [x64] + os: [win32] + '@rolldown/binding-android-arm64@1.2.3': resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -961,6 +1258,9 @@ packages: fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -981,6 +1281,11 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + formatly@0.3.0: + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + engines: {node: '>=18.3.0'} + hasBin: true + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -994,6 +1299,9 @@ packages: resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} + get-tsconfig@4.14.1: + resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==} + get-tsconfig@5.0.0-beta.4: resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} engines: {node: '>=20.20.0'} @@ -1043,10 +1351,52 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-yaml@4.3.1: resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true + jscpd-darwin-arm64@5.0.14: + resolution: {integrity: sha512-Ojjl79SBuj9tEW6WbjZ1a/1ZOR89dneH9yLQYQu8WyWaQownttnx7RYFEHU6aGhS4jIvwUEbr+1wxzFTb37cwg==} + cpu: [arm64] + os: [darwin] + + jscpd-darwin-x64@5.0.14: + resolution: {integrity: sha512-DxFg5XvjMZ81iVeqillnM5apqcGCfNTbroNF+mPLr7RkHLGH6mudLgtO+ILL/hfpZXy1bF9oIY5BSudPmN/k9A==} + cpu: [x64] + os: [darwin] + + jscpd-linux-arm64-gnu@5.0.14: + resolution: {integrity: sha512-1uw+XBHEt9pONXNICSp5HpaVWPjG6mQ6deDXaq9Yb0xCNJkX4/8gmn0vhzekIyZD2DspRYKPUolbDsqm/HEdYg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + jscpd-linux-x64-gnu@5.0.14: + resolution: {integrity: sha512-dFTbyyrm+Z9pcXIVzJQCw8QAgiNqIiO69sm4AfA7/wFdPoizoVzjhaXsYXcSV4bs0aoPiWbNazg0J0HgslT/5A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + jscpd-linux-x64-musl@5.0.14: + resolution: {integrity: sha512-SayS7qQJvixyy9eR0+UjepkTsUUwqvlsiuSxfIdHgG2qzqoh/thnkgiu4By8fsiiDpQONsQrRrZDwHRQ3GDrBQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + jscpd-windows-x64-msvc@5.0.14: + resolution: {integrity: sha512-DqjxlVkUanlahGgY2lY7Zkrau4BUTI+AwWky+bPGK4kSK2AIOaUziY9Q19u8b58idXmJA9FKK98Fuu4ajNXVjQ==} + cpu: [x64] + os: [win32] + + jscpd@5.0.14: + resolution: {integrity: sha512-zge+FPZZAymt2Do5Z0+QHyIn4/XcUhrO/W7of9HcHZfx2AK8++dYhLA1uWtwXj47ml3Of8PbcUW4wUWvYMCc3w==} + engines: {node: '>=18'} + hasBin: true + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -1060,6 +1410,11 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + knip@6.32.2: + resolution: {integrity: sha512-WXTXbmocrw7gqm1A1TQvFN0OgJ7hUSU6E1g6SPRIzzHFogUBhXByc7cYeOFVtJ2uODg7DP4VbESYBYnfbtBYsg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + lightningcss-android-arm64@1.33.0: resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} engines: {node: '>= 12.0.0'} @@ -1216,6 +1571,13 @@ packages: oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + oxc-parser@0.143.0: + resolution: {integrity: sha512-ov0NzaDCOInknS7mP1cwKdJERt3utPW8ldjtdUXQ8Ty0GEFD08wk422vCUN0d7pST6kqtV7dxoI9w1Zi0l/9TA==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.24.2: + resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} + p-limit@7.3.1: resolution: {integrity: sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==} engines: {node: '>=20'} @@ -1366,6 +1728,10 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + svgo@4.0.2: resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==} engines: {node: '>=16'} @@ -1412,6 +1778,10 @@ packages: ultrahtml@1.7.0: resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==} + unbash@4.0.10: + resolution: {integrity: sha512-b7zoBQvpWp0vuN5q2vK2RRBR2SvuruQAs50DApdDveBSn3eSYd84IaHodFqQIMlvY9K2VnyBUEXgwOBuGU9GBg==} + engines: {node: '>=14'} + uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} @@ -1660,6 +2030,10 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -1737,9 +2111,9 @@ snapshots: '@astrojs/compiler-binding-linux-x64-musl@0.3.2': optional: true - '@astrojs/compiler-binding-wasm32-wasi@0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + '@astrojs/compiler-binding-wasm32-wasi@0.3.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': dependencies: - '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -1751,7 +2125,7 @@ snapshots: '@astrojs/compiler-binding-win32-x64-msvc@0.3.2': optional: true - '@astrojs/compiler-binding@0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + '@astrojs/compiler-binding@0.3.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': optionalDependencies: '@astrojs/compiler-binding-darwin-arm64': 0.3.2 '@astrojs/compiler-binding-darwin-x64': 0.3.2 @@ -1759,16 +2133,16 @@ snapshots: '@astrojs/compiler-binding-linux-arm64-musl': 0.3.2 '@astrojs/compiler-binding-linux-x64-gnu': 0.3.2 '@astrojs/compiler-binding-linux-x64-musl': 0.3.2 - '@astrojs/compiler-binding-wasm32-wasi': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + '@astrojs/compiler-binding-wasm32-wasi': 0.3.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) '@astrojs/compiler-binding-win32-arm64-msvc': 0.3.2 '@astrojs/compiler-binding-win32-x64-msvc': 0.3.2 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - '@astrojs/compiler-rs@0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + '@astrojs/compiler-rs@0.3.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': dependencies: - '@astrojs/compiler-binding': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + '@astrojs/compiler-binding': 0.3.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -1847,6 +2221,41 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@biomejs/biome@2.5.5': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.5.5 + '@biomejs/cli-darwin-x64': 2.5.5 + '@biomejs/cli-linux-arm64': 2.5.5 + '@biomejs/cli-linux-arm64-musl': 2.5.5 + '@biomejs/cli-linux-x64': 2.5.5 + '@biomejs/cli-linux-x64-musl': 2.5.5 + '@biomejs/cli-win32-arm64': 2.5.5 + '@biomejs/cli-win32-x64': 2.5.5 + + '@biomejs/cli-darwin-arm64@2.5.5': + optional: true + + '@biomejs/cli-darwin-x64@2.5.5': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.5.5': + optional: true + + '@biomejs/cli-linux-arm64@2.5.5': + optional: true + + '@biomejs/cli-linux-x64-musl@2.5.5': + optional: true + + '@biomejs/cli-linux-x64@2.5.5': + optional: true + + '@biomejs/cli-win32-arm64@2.5.5': + optional: true + + '@biomejs/cli-win32-x64@2.5.5': + optional: true + '@bruits/satteri-darwin-arm64@0.9.5': optional: true @@ -1923,11 +2332,22 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.3': dependencies: tslib: 2.8.1 @@ -2132,17 +2552,142 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: - '@emnapi/core': 1.11.1 + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': + dependencies: + '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.3 '@tybys/wasm-util': 0.10.3 optional: true '@oslojs/encoding@1.1.0': {} + '@oxc-parser/binding-android-arm-eabi@0.143.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.143.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.143.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.143.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.143.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.143.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.143.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.143.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.143.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.143.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.143.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.143.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.143.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.143.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.143.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.143.0': + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.143.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.143.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.143.0': + optional: true + '@oxc-project/types@0.143.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + optional: true + + '@oxc-resolver/binding-android-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.24.2': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + optional: true + '@rolldown/binding-android-arm64@1.2.3': optional: true @@ -2336,9 +2881,9 @@ snapshots: aria-query@5.3.2: {} - astro@7.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(yaml@2.9.0): + astro@7.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(jiti@2.7.0)(yaml@2.9.0): dependencies: - '@astrojs/compiler-rs': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + '@astrojs/compiler-rs': 0.3.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) '@astrojs/internal-helpers': 0.10.2 '@astrojs/markdown-satteri': 0.3.5 '@astrojs/telemetry': 3.3.3 @@ -2385,8 +2930,8 @@ snapshots: ultrahtml: 1.7.0 unifont: 0.7.4 unstorage: 1.17.5 - vite: 8.2.1(esbuild@0.28.2)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.2.1(esbuild@0.28.2)(yaml@2.9.0)) + vite: 8.2.1(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.2.1(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -2592,6 +3137,10 @@ snapshots: dependencies: fast-string-width: 3.0.2 + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 @@ -2606,6 +3155,10 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + formatly@0.3.0: + dependencies: + fd-package-json: 2.0.0 + fsevents@2.3.3: optional: true @@ -2613,6 +3166,10 @@ snapshots: get-east-asian-width@1.6.0: {} + get-tsconfig@4.14.1: + dependencies: + resolve-pkg-maps: 1.0.0 + get-tsconfig@5.0.0-beta.4: dependencies: resolve-pkg-maps: 1.0.0 @@ -2693,10 +3250,39 @@ snapshots: is-plain-obj@4.1.0: {} + jiti@2.7.0: {} + js-yaml@4.3.1: dependencies: argparse: 2.0.1 + jscpd-darwin-arm64@5.0.14: + optional: true + + jscpd-darwin-x64@5.0.14: + optional: true + + jscpd-linux-arm64-gnu@5.0.14: + optional: true + + jscpd-linux-x64-gnu@5.0.14: + optional: true + + jscpd-linux-x64-musl@5.0.14: + optional: true + + jscpd-windows-x64-msvc@5.0.14: + optional: true + + jscpd@5.0.14: + optionalDependencies: + jscpd-darwin-arm64: 5.0.14 + jscpd-darwin-x64: 5.0.14 + jscpd-linux-arm64-gnu: 5.0.14 + jscpd-linux-x64-gnu: 5.0.14 + jscpd-linux-x64-musl: 5.0.14 + jscpd-windows-x64-msvc: 5.0.14 + json-schema-traverse@1.0.0: {} jsonc-parser@2.3.1: {} @@ -2705,6 +3291,22 @@ snapshots: kleur@4.1.5: {} + knip@6.32.2: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + formatly: 0.3.0 + get-tsconfig: 4.14.1 + jiti: 2.7.0 + oxc-parser: 0.143.0 + oxc-resolver: 11.24.2 + picomatch: 4.0.5 + smol-toml: 1.7.1 + strip-json-comments: 5.0.3 + tinyglobby: 0.2.17 + unbash: 4.0.10 + yaml: 2.9.0 + zod: 4.4.3 + lightningcss-android-arm64@1.33.0: optional: true @@ -2839,6 +3441,52 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 + oxc-parser@0.143.0: + dependencies: + '@oxc-project/types': 0.143.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.143.0 + '@oxc-parser/binding-android-arm64': 0.143.0 + '@oxc-parser/binding-darwin-arm64': 0.143.0 + '@oxc-parser/binding-darwin-x64': 0.143.0 + '@oxc-parser/binding-freebsd-x64': 0.143.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.143.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.143.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.143.0 + '@oxc-parser/binding-linux-arm64-musl': 0.143.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.143.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.143.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.143.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.143.0 + '@oxc-parser/binding-linux-x64-gnu': 0.143.0 + '@oxc-parser/binding-linux-x64-musl': 0.143.0 + '@oxc-parser/binding-openharmony-arm64': 0.143.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.143.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.143.0 + '@oxc-parser/binding-win32-x64-msvc': 0.143.0 + + oxc-resolver@11.24.2: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.24.2 + '@oxc-resolver/binding-android-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-x64': 11.24.2 + '@oxc-resolver/binding-freebsd-x64': 11.24.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-arm64-musl': 11.24.2 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-musl': 11.24.2 + '@oxc-resolver/binding-linux-s390x-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-musl': 11.24.2 + '@oxc-resolver/binding-openharmony-arm64': 11.24.2 + '@oxc-resolver/binding-wasm32-wasi': 11.24.2 + '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 + '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 + p-limit@7.3.1: dependencies: yocto-queue: 1.2.2 @@ -3023,6 +3671,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-json-comments@5.0.3: {} + svgo@4.0.2: dependencies: commander: 11.1.0 @@ -3063,6 +3713,8 @@ snapshots: ultrahtml@1.7.0: {} + unbash@4.0.10: {} + uncrypto@0.1.3: {} unified@11.0.5: @@ -3130,7 +3782,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.2.1(esbuild@0.28.2)(yaml@2.9.0): + vite@8.2.1(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -3140,11 +3792,12 @@ snapshots: optionalDependencies: esbuild: 0.28.2 fsevents: 2.3.3 + jiti: 2.7.0 yaml: 2.9.0 - vitefu@1.1.3(vite@8.2.1(esbuild@0.28.2)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.2.1(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)): optionalDependencies: - vite: 8.2.1(esbuild@0.28.2)(yaml@2.9.0) + vite: 8.2.1(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0) volar-service-css@0.0.71(@volar/language-service@2.4.28): dependencies: @@ -3252,6 +3905,8 @@ snapshots: vscode-uri@3.1.0: {} + walk-up-path@4.0.0: {} + web-namespaces@2.0.1: {} wrap-ansi@9.0.2: diff --git a/scripts/check-code-health.mjs b/scripts/check-code-health.mjs new file mode 100644 index 0000000..99e6aff --- /dev/null +++ b/scripts/check-code-health.mjs @@ -0,0 +1,235 @@ +#!/usr/bin/env node + +import { spawnSync } from "node:child_process"; +import { mkdtempSync, readFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join, resolve } from "node:path"; +import process from "node:process"; +import { fileURLToPath } from "node:url"; + +const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const productionPaths = ["Indulge", "src"]; +const baselines = { + unused: { + files: 0, + exports: 0, + types: 0, + dependencies: 0, + devDependencies: 0, + unlisted: 0, + unresolved: 0 + }, + // Measured on current Indulge + landing sources; ratchet via #16. + complexity: { violations: 6, maxCcn: 18, maxLength: 163, maxParams: 9 }, + // jscpd percentage jitters in later digits between runs. + duplication: { clones: 1, duplicatedLines: 15, percentage: 0.22 } +}; + +function log(message) { + process.stdout.write(`${message}\n`); +} + +function run(command, args, options = {}) { + const result = spawnSync(command, args, { + cwd: projectRoot, + encoding: "utf8", + env: { ...process.env, ...(options.env ?? {}) }, + maxBuffer: 64 * 1024 * 1024 + }); + if (result.error) throw result.error; + if (result.status !== 0 && !options.allowFailure) { + process.stdout.write(result.stdout ?? ""); + process.stderr.write(result.stderr ?? ""); + throw new Error(`${command} exited with status ${result.status}`); + } + return { + status: result.status ?? 1, + stdout: result.stdout ?? "", + stderr: result.stderr ?? "" + }; +} + +function parseJson(result, label) { + try { + return JSON.parse(result.stdout); + } catch (error) { + process.stderr.write(result.stderr); + throw new Error(`${label} did not return valid JSON`, { cause: error }); + } +} + +function commandWithUvx(command, uvxArgs) { + const probe = spawnSync(command, ["--version"], { encoding: "utf8" }); + return probe.status === 0 ? { command, prefix: [] } : { command: "uvx", prefix: uvxArgs }; +} + +function issueCount(issues, key) { + return issues.reduce((sum, issue) => sum + (issue[key]?.length ?? 0), 0); +} + +function failRegressions(label, observed, baseline) { + const regressions = Object.entries(baseline).filter(([key, maximum]) => observed[key] > maximum); + if (regressions.length > 0) { + throw new Error( + regressions + .map(([key, maximum]) => `${label} ${key} regressed: ${observed[key]} > ${maximum}`) + .join("\n") + ); + } + if (Object.entries(baseline).some(([key, maximum]) => observed[key] < maximum)) { + log(`${label} improved; lower the checked-in baseline intentionally.`); + } +} + +function checkFormat() { + run("pnpm", ["exec", "biome", "format", "."]); + log("Landing format: biome clean. Native Swift stays on Xcode; no repo swift-format config."); +} + +function checkLint() { + run("pnpm", ["exec", "biome", "check", "."]); + log("Landing lint: biome clean. Native compile/lint evidence stays on xcodebuild."); +} + +function checkTypes() { + run("pnpm", ["exec", "astro", "check"]); + log("Types: astro check is the landing source of truth."); +} + +function checkTests() { + run("bash", ["scripts/test.sh"]); + log("Tests: scripts/test.sh / XCTest is the native source of truth."); +} + +function checkCoverage() { + run("bash", ["scripts/test.sh"]); + log("Coverage: the Indulge scheme gathers coverage during scripts/test.sh."); +} + +function checkUnused() { + const report = parseJson( + run("pnpm", ["exec", "knip", "--reporter", "json", "--no-exit-code", "--no-progress"], { + allowFailure: true + }), + "Knip" + ); + const issues = report.issues ?? []; + const observed = Object.fromEntries( + Object.keys(baselines.unused).map((key) => [key, issueCount(issues, key)]) + ); + log( + `Unused: files=${observed.files}, exports=${observed.exports}, types=${observed.types}, ` + + `dependencies=${observed.dependencies}, devDependencies=${observed.devDependencies}, ` + + `unlisted=${observed.unlisted}, unresolved=${observed.unresolved}.` + ); + failRegressions("Unused", observed, baselines.unused); +} + +function checkComplexity() { + const lizard = commandWithUvx("lizard", ["--from", "lizard==1.23.0", "lizard"]); + const result = run(lizard.command, [ + ...lizard.prefix, + ...productionPaths, + "-x", + "**/Resources/**", + "-x", + "**/*.test.*", + "--csv" + ]); + const rows = result.stdout + .trim() + .split("\n") + .map((line) => line.match(/^(\d+),(\d+),(\d+),(\d+),(\d+),/u)) + .filter(Boolean) + .map((match) => match.slice(1).map(Number)); + const observed = { + functions: rows.length, + nloc: rows.reduce((sum, row) => sum + row[0], 0), + violations: rows.filter((row) => row[1] > 15 || row[4] > 100 || row[3] > 7).length, + maxCcn: Math.max(0, ...rows.map((row) => row[1])), + maxLength: Math.max(0, ...rows.map((row) => row[4])), + maxParams: Math.max(0, ...rows.map((row) => row[3])) + }; + log( + `Complexity: ${observed.functions} functions, ${observed.nloc} NLOC, ` + + `${observed.violations} violations; max CCN ${observed.maxCcn}, ` + + `max length ${observed.maxLength}, max params ${observed.maxParams}.` + ); + failRegressions("Complexity", observed, baselines.complexity); +} + +function checkDuplication() { + const outputDirectory = mkdtempSync(join(tmpdir(), "indulge-jscpd-")); + run("pnpm", [ + "exec", + "jscpd", + ...productionPaths, + "--format", + "javascript,typescript,swift", + "--min-lines", + "8", + "--min-tokens", + "60", + "--mode", + "strict", + "--ignore", + "**/Resources/**,**/*.test.*,**/node_modules/**,**/dist/**", + "--reporters", + "json", + "--output", + outputDirectory, + "--silent", + "--no-tips" + ]); + const observed = JSON.parse(readFileSync(join(outputDirectory, "jscpd-report.json"), "utf8")) + .statistics.total; + log( + `Duplication: ${observed.duplicatedLines}/${observed.lines} lines ` + + `(${observed.percentage.toFixed(4)}%), ${observed.clones} groups across ${observed.sources} files.` + ); + failRegressions("Duplication", observed, baselines.duplication); +} + +function checkCycles() { + const report = parseJson( + run( + "pnpm", + ["exec", "knip", "--cycles", "--reporter", "json", "--no-exit-code", "--no-progress"], + { allowFailure: true } + ), + "Knip cycle analysis" + ); + const cycles = (report.issues ?? []).flatMap((issue) => issue.cycles ?? []); + if (cycles.length > 0) { + throw new Error(`Landing dependency cycles detected: ${cycles.length}`); + } + log("Cycles: zero landing import cycles; native target graph stays on Xcode."); +} + +const checks = { + complexity: checkComplexity, + coverage: checkCoverage, + cycles: checkCycles, + duplication: checkDuplication, + format: checkFormat, + lint: checkLint, + tests: checkTests, + types: checkTypes, + unused: checkUnused +}; + +const selected = process.argv[2]; +const allChecks = ["format", "lint", "types", "unused", "complexity", "duplication", "cycles"]; + +try { + if (selected === "all") { + for (const name of allChecks) checks[name](); + } else if (Object.hasOwn(checks, selected)) { + checks[selected](); + } else { + throw new Error(`Usage: check-code-health.mjs `); + } +} catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exit(1); +} diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs index 2f6edc1..6e75f35 100644 --- a/scripts/check-site.mjs +++ b/scripts/check-site.mjs @@ -19,7 +19,8 @@ await Promise.all(requiredFiles.map((file) => access(file))); const home = await readFile("dist/index.html", "utf8"); const requiredHomeCopy = ["Enjoy on", "Life", "Trade", "History", "private", "TestFlight"]; for (const fragment of requiredHomeCopy) { - if (!home.includes(fragment)) throw new Error(`Landing page is missing required copy: ${fragment}`); + if (!home.includes(fragment)) + throw new Error(`Landing page is missing required copy: ${fragment}`); } for (const fragment of [ @@ -28,15 +29,22 @@ for (const fragment of [ 'name="twitter:card" content="summary_large_image"', "See TestFlight status" ]) { - if (!home.includes(fragment)) throw new Error(`Landing metadata or fallback is missing: ${fragment}`); + if (!home.includes(fragment)) + throw new Error(`Landing metadata or fallback is missing: ${fragment}`); } if (home.includes("testflight.apple.com")) { throw new Error("A public TestFlight URL appeared without a verified build-time configuration."); } -for (const prohibitedClaim of ["cure addiction", "treat addiction", "guaranteed recovery", "blocks every app"]) { - if (home.toLowerCase().includes(prohibitedClaim)) throw new Error(`Prohibited product claim found: ${prohibitedClaim}`); +for (const prohibitedClaim of [ + "cure addiction", + "treat addiction", + "guaranteed recovery", + "blocks every app" +]) { + if (home.toLowerCase().includes(prohibitedClaim)) + throw new Error(`Prohibited product claim found: ${prohibitedClaim}`); } const scriptCount = (home.match(/