From ff412cbc4485d0f53a84ff9809d61aeae8f37275 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Sun, 2 Aug 2026 20:14:20 -0300 Subject: [PATCH] chore: migrate toolchain to Vite+ --- .github/workflows/ci.yml | 2 +- .vite-hooks/pre-commit | 1 + README.md | 12 +- biome.json | 24 -- bun.lock | 331 ++++++++++++++++++++++++--- docker-compose.yml | 4 +- drizzle.config.ts | 10 +- lefthook.yml | 9 - package.json | 35 ++- pickled.yml | 25 +- src/backup.ts | 94 ++++---- src/classifier/segment-classifier.ts | 34 +-- src/config.ts | 4 +- src/crawler/processor.ts | 50 ++-- src/crawler/reference.test.ts | 12 +- src/crawler/reference.ts | 2 +- src/crawler/refresh.test.ts | 98 ++++---- src/crawler/refresh.ts | 42 ++-- src/db/connection.ts | 8 +- src/db/repository.ts | 8 +- src/db/schema.ts | 148 ++++++------ src/fipe/client.ts | 28 +-- src/fipe/schemas.ts | 2 +- src/fipe/types.ts | 4 +- src/index.ts | 84 +++---- vite.config.ts | 13 ++ 26 files changed, 683 insertions(+), 401 deletions(-) create mode 100755 .vite-hooks/pre-commit delete mode 100644 biome.json delete mode 100644 lefthook.yml create mode 100644 vite.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 876537b..0c241b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: uses: oven-sh/setup-bun@v2 - name: Install dependencies - run: bun install --frozen-lockfile + run: bunx --package vite-plus@0.2.7 vp install --frozen-lockfile - name: Check run: bun run check diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit new file mode 100755 index 0000000..85fb65b --- /dev/null +++ b/.vite-hooks/pre-commit @@ -0,0 +1 @@ +vp staged diff --git a/README.md b/README.md index 4236ed7..d145610 100644 --- a/README.md +++ b/README.md @@ -152,12 +152,12 @@ Schema SQL completo em [`initial.sql`](./initial.sql). Estes dados são **públicos e oficiais**, disponibilizados pela Fundação Instituto de Pesquisas Econômicas (FIPE). -| | | -|---|---| -| **Fonte** | [veiculos.fipe.org.br](https://veiculos.fipe.org.br) | -| **Atualização** | Mensal (desde 2001) | -| **Cobertura** | Carros, motos, caminhões e utilitários | -| **Uso** | Referência para seguros, financiamentos, IPVA e negociação de veículos | +| | | +| --------------- | ---------------------------------------------------------------------- | +| **Fonte** | [veiculos.fipe.org.br](https://veiculos.fipe.org.br) | +| **Atualização** | Mensal (desde 2001) | +| **Cobertura** | Carros, motos, caminhões e utilitários | +| **Uso** | Referência para seguros, financiamentos, IPVA e negociação de veículos | A Tabela FIPE é a referência de preço médio de veículos mais utilizada no Brasil. Os dados são coletados mensalmente junto a concessionárias, revendedoras e fabricantes em todo o país. diff --git a/biome.json b/biome.json deleted file mode 100644 index b652d48..0000000 --- a/biome.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", - "files": { - "ignore": ["dist", "node_modules"] - }, - "formatter": { - "lineWidth": 100, - "indentStyle": "space", - "indentWidth": 2 - }, - "javascript": { - "formatter": { - "quoteStyle": "single" - } - }, - "linter": { - "rules": { - "recommended": true - } - }, - "organizeImports": { - "enabled": true - } -} diff --git a/bun.lock b/bun.lock index 3e8e32a..b3588be 100644 --- a/bun.lock +++ b/bun.lock @@ -13,18 +13,21 @@ "zod": "^4.1.13", }, "devDependencies": { - "@biomejs/biome": "^1.9.4", "@semantic-release/git": "^10.0.1", "@types/bun": "^1.3.0", "@types/cli-progress": "^3.11.6", "@types/node": "^24.10.2", "drizzle-kit": "^0.31.8", - "lefthook": "^1.11.5", "semantic-release": "^25.0.2", "typescript": "7.0.2", + "vite": "npm:@voidzero-dev/vite-plus-core@0.2.7", + "vite-plus": "0.2.7", }, }, }, + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@0.2.7", + }, "packages": { "@actions/core": ["@actions/core@3.0.0", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg=="], @@ -42,23 +45,7 @@ "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], - "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], - - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], - - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], - - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], - - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], - - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], - - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], - - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], - - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + "@blazediff/core": ["@blazediff/core@1.9.1", "", {}, "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA=="], "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], @@ -120,6 +107,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], "@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], @@ -142,12 +131,108 @@ "@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@oxc-project/runtime": ["@oxc-project/runtime@0.141.0", "", {}, "sha512-Z/6jQ0dyvE2fVjMUO7GoD4h+3q0G4lI4BWQNjaPhC4RPxaS4hF1b7/QYKB/Tl+KAQwqqKgMF54ukR/VvV5FILg=="], + + "@oxc-project/types": ["@oxc-project/types@0.141.0", "", {}, "sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ=="], + + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.60.0", "", { "os": "android", "cpu": "arm" }, "sha512-1q4q4Jc8FlOMVojEisyFAVyl8h1yawNv6phjgmhGVEDeyeOdsSnSr9x0+D4mOnEKvpO5L4mxKZ/DP9X6U3A/Mw=="], + + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.60.0", "", { "os": "android", "cpu": "arm64" }, "sha512-tD41I6nCt9k8SQXft0CSjjU9jg6SwG7uMu7PxodSEHXl+GDW0868oy6tTtoJkyUze8YKFgTpz/k5LuPUnFiGLw=="], + + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.60.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TTpzPug96Zxdyb46KvTyIUQDdsqbumXh2TKG9C23PCT0kF7JkW56Z/quPuG9rqOFKQIi1gpRNZ7DX18LwxXPnw=="], + + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.60.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-CnOoWgQ7L+JL/YQaRJ+NyATciSfcftncm7y3kqyte1cGtFEGnStaCd1TAyrinkfQ7nRBfHrTs1/vTwUJr3WF2Q=="], + + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.60.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ychJo7S3hZxdO6eDZ9zM6F2lM9fpJS3EKS5CAUSWyprdLYxTu4gbaUKV/VBPTcMJwQa2Bpo+643y3OJ537pihA=="], + + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.60.0", "", { "os": "linux", "cpu": "arm" }, "sha512-36IH5o55T2Fx7E0feDttt+mifxN6yk9pWv4KfhAIsP0dFnUq27331OwbpOsZdoXF9soOLWm7mQUz5+UUmyec4g=="], + + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.60.0", "", { "os": "linux", "cpu": "arm" }, "sha512-G1Ve7lAa6sFBolVI2LWHfEAqy0YKh4vnioH8uYO9kAEdgM7mR40IksIx9/Zk4+vbYew/sGa4J9Q4tZ3n9gXDHA=="], + + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.60.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-LTQdRBf6uzj/h7Xk6lKzbGD2hrF/fK4YI9LIN1c0509tPUn8wRa3mCmrFQpEWJPLYGFrLFFMTYW1Ljj6VqW2Hw=="], + + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.60.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-2JMo3XPxMPx3hiqddSZYyaH+fKJm6cz0u8n1naYjP/CdOQOZW34i8lKBUfmbWiuFvd6KoYXLmhAyBuvojsYS7Q=="], + + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.60.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-L3C+nBD13lr306tr/PjM3RMll+BVqgFrIgUyoeHuai5oueJrRLgO3j+GO5/Cbhtkf5PSlHYTI1JY7iqBd1qa6A=="], + + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.60.0", "", { "os": "linux", "cpu": "none" }, "sha512-M4MsmvqlxFiPtSRGyBYQSZxchEf463AOyd+Dh4/9xDpjWBsRtDUTDMFN5EdHinjVK1/eDJQ8MLpcYjpYayaCnA=="], + + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.60.0", "", { "os": "linux", "cpu": "none" }, "sha512-OH+9UskYuxRB+GxqdGkVN8f5UpwhqG8YscNo1wl8+KJ62cd7wZdGga6iGLJIf8kibF1WBwvlfDUx3cez/VXwFg=="], + + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.60.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-y7AAFutt9wFWBFOAn6+BHaV39usZmcr3YYH2385f+NHgPNpIF9HpqKp0jgUxPaUOCyG3oaX5VhJduL1Nw164rw=="], + + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.60.0", "", { "os": "linux", "cpu": "x64" }, "sha512-yKZ9+CXAI+1RO5nH/4Z/9M6DAsfOzd5bw/gtWk81KB4mpalMaRRSXfouc5/tHxazDmBek55HNPepNYBgaCew0Q=="], + + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.60.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bCUGaF6hJOYnQzLJdHLZbvGsOd5oSvGAyJhPAKum2uyLYUuXmP8vqg690DWi2hqcnIoYpqSqCrjzE5aiUAgwQg=="], + + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.60.0", "", { "os": "none", "cpu": "arm64" }, "sha512-GrUeZOvzP30ExxfCuQiyofuUGI+OmvAgFwOO5w5p9mGPlxcyuqI+6Sy9fAKFFfLQrqKYWFgc5sYA2Unj/29nPg=="], + + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.60.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-WD4Q954kUl2TDJV/6q7UnE2rlKk047kXLJsr4bJ2mXRaAqNXcmV3nwKUsGCc3mz/jYDBnXtJEaBErJEybK8iQQ=="], + + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.60.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-HqDekjr8JXzVDUP1YthDZ1Y3CBEcuZT4WX3B+1kaxj8CvZA8Y2YhcEsXqoSop3tVsgjACxjnFQFDkBo0r/jq1Q=="], + + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.60.0", "", { "os": "win32", "cpu": "x64" }, "sha512-tz78yhmGPKboTMHCHSaUqXK8JrmoSejgDcWeqAtg2s07ZGKQ3rH5Jn8NuXPGNG33CDbY2e9NoQWXIVEmKO21Rw=="], + + "@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@7.0.2001", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w=="], + + "@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@7.0.2001", "", { "os": "darwin", "cpu": "x64" }, "sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw=="], + + "@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@7.0.2001", "", { "os": "linux", "cpu": "arm64" }, "sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A=="], + + "@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@7.0.2001", "", { "os": "linux", "cpu": "x64" }, "sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ=="], + + "@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@7.0.2001", "", { "os": "win32", "cpu": "arm64" }, "sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q=="], + + "@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@7.0.2001", "", { "os": "win32", "cpu": "x64" }, "sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog=="], + + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.75.0", "", { "os": "android", "cpu": "arm" }, "sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g=="], + + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.75.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hXI0hDgHkw4w5nfru72aG7y+2iQJmC4waH/KV6H/hbgA6yAP5jYNx0P9yug15Hs0tWl/+mda3Jjn/2gmDT48tw=="], + + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.75.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-D91BWbK/dMYfCcrghspPIuKs2D9LF4Z/OabVSQjw1AO6PWxArD7teDA48bm0ySFqWDaPVqmQRl5GMWNglTXyrQ=="], + + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.75.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-02mpwzf12BonZ6PT0TuQoomvEh2kVl2WGBIKWezCyToIS+rYkQZ6GXnARBAl9A4Ovm2V+Xe7M4KretyqmmcnJQ=="], + + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.75.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-qZJgLnDaBsiL5YESx2t/TZ8eXkL9fEkKoXEdzegROhlz9A0lgyGnZ0dAzJrh7LJAHQl2K9RdRueN2s/9N7+odg=="], + + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.75.0", "", { "os": "linux", "cpu": "arm" }, "sha512-7XlaWA5BJD3XpCfrEqjEe6Zseeb14S7QGa304XfwKignRaKQ+eIj775BQ7nIslggWickl4IsPUFqJ+/gAyNHVg=="], + + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.75.0", "", { "os": "linux", "cpu": "arm" }, "sha512-av6Tpv8yrcMMMOadOqENBhlsLRcGFXXwoQ0hzHhsmS9FJ4Wioy8we427GbcMe2XTxmL2e60T67H1Dyr3up+tAA=="], + + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.75.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-WcUhd8fHT5plrA14lANevl+hOl815mVI5t2hU21oFWrZKFXIVV/Sr4rWQV0NzSvzBupbMLNc5ErEA6Ehxh5jMg=="], + + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.75.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UWzp5wRHFe/ESO3+eEaxXsTkYTGLYjnTsi/I5neEacXSItQ6WNleapfOAeA4x2b8nyhJ4uQxqvtv9pHv8kWJtQ=="], + + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.75.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-XEVRwGMLKCUKrvhLAz4F6AIh8MJrQVdSZtAmPpRZt9tGPsUnamPOcl3dS/ZQzJnar/Ymgc//+xho0L60Emzuxg=="], + + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.75.0", "", { "os": "linux", "cpu": "none" }, "sha512-mAG4DUXqfLC8cTjMD2kt3jDmVzFREYtDyeLNdLdsCcBc4Zbl2EMuiFektGBilQwkNjYnMvCqJs55U+Hyb+b+jw=="], + + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.75.0", "", { "os": "linux", "cpu": "none" }, "sha512-95hrAvriAlI+pekSomTFIn0+bawMDlDwTNVmdjsFusTHyL2JWh7TWvRNG/Lkim72uN8OiCcO9wcaC6omLP5E3w=="], + + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.75.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-4b6f2+FrtruAESrCqIKcrarzfrSx+wk2QNcp+RT91/Prc+pMQMAfyZ1rG1c3tFQNl8Bc616tx40uNXyxNBRPbQ=="], + + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.75.0", "", { "os": "linux", "cpu": "x64" }, "sha512-nshAhrUvXFUWOvqQ2soIw7HFNWvpvEV4o0cYSqPtzLiPF5gKyYTDOOTJ6Rn8g8K/iGvPIrbDA4v8+5MvnjJrrg=="], + + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.75.0", "", { "os": "linux", "cpu": "x64" }, "sha512-e4jNxLKnxLC6sYBQRxrI2pgIIxnmMtF8U/VwNYcjTT/CLS+spH624cYVnj07bTKwaEWT37/e025isOs6j/0xqA=="], + + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.75.0", "", { "os": "none", "cpu": "arm64" }, "sha512-hZ2lH+1qLf/DiEP9UWuQTK2JWj/BgvMB4jhIV4SmNU1wfEiYYX4TynQyAZXx0j9X4qRYizAL042SKaV+8ynh4w=="], + + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.75.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Ilj6PNzGDS3bCU0MSJH7Msh0NhH+T/mRp2shwg+q+GHeVlPwP5LEboW96aW+3kVKFk6zYZy1Xi5pZkqZh6X8KQ=="], + + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.75.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-QVit2nOEOiPhkmsrksPSkoGCdnZRNkspt8fwoYyP09te1VEbnSj4LAxua4rc8FKTmWkySVe05j8iz9GXYfF1AQ=="], + + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.75.0", "", { "os": "win32", "cpu": "x64" }, "sha512-DSxnNkBUAYARPwJtR12Ig3deWr8w0H997xP6jy33i+e0SyYJw8FKuz4+cZtpmPEhQmvlPJE3X/2vNxDmLkd/rA=="], + + "@oxlint/plugins": ["@oxlint/plugins@1.73.0", "", {}, "sha512-OhgMQeMmZA0dcFcX4/priaJZWdFECxiClgq6mRX6aatZEcV9PbKC3P3/v8U1hVjviT1i5U+vR8lAtBV6m4FXAA=="], + "@pnpm/config.env-replace": ["@pnpm/config.env-replace@1.1.0", "", {}, "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="], "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="], "@pnpm/npm-conf": ["@pnpm/npm-conf@3.0.2", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA=="], + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], "@semantic-release/commit-analyzer": ["@semantic-release/commit-analyzer@13.0.1", "", { "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", "micromatch": "^4.0.2" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ=="], @@ -166,10 +251,24 @@ "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + "@types/cli-progress": ["@types/cli-progress@3.11.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + "@types/node": ["@types/node@24.10.12", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-68e+T28EbdmLSTkPgs3+UacC6rzmqrcWFPQs1C8mwJhI/r5Uxr0yEuQotczNRROd1gq30NGxee+fo0rSIxpyAw=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], @@ -214,6 +313,88 @@ "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + "@vitest/browser": ["@vitest/browser@4.1.10", "", { "dependencies": { "@blazediff/core": "1.9.1", "@vitest/mocker": "4.1.10", "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", "tinyrainbow": "^3.1.0", "ws": "^8.19.0" }, "peerDependencies": { "vitest": "4.1.10" } }, "sha512-UDwuWGwXj646CBx/bQHOaJSX7np0I8JL/UKQYa1e4QrVHH8VdWtx8eaOuf8sy0ShwDgR6NjJAsp5eF6vjF6qng=="], + + "@vitest/browser-preview": ["@vitest/browser-preview@4.1.10", "", { "dependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/user-event": "^14.6.1", "@vitest/browser": "4.1.10" }, "peerDependencies": { "vitest": "4.1.10" } }, "sha512-14MJrL59ZFkqXLjwfSk6RzTDy5Czf9UG4+8q8L6Gxjs2aPjEce/cVNYV14bXAc2BvMjUNu904+ZEZA1Xc1wtvQ=="], + + "@vitest/expect": ["@vitest/expect@4.1.10", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA=="], + + "@vitest/mocker": ["@vitest/mocker@4.1.10", "", { "dependencies": { "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.10", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q=="], + + "@vitest/runner": ["@vitest/runner@4.1.10", "", { "dependencies": { "@vitest/utils": "4.1.10", "pathe": "^2.0.3" } }, "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.1.10", "", { "dependencies": { "@vitest/pretty-format": "4.1.10", "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw=="], + + "@vitest/spy": ["@vitest/spy@4.1.10", "", {}, "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw=="], + + "@vitest/utils": ["@vitest/utils@4.1.10", "", { "dependencies": { "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA=="], + + "@voidzero-dev/vite-plus-core": ["@voidzero-dev/vite-plus-core@0.2.7", "", { "dependencies": { "@oxc-project/runtime": "=0.141.0", "@oxc-project/types": "=0.141.0", "lightningcss": "^1.33.0", "postcss": "^8.5.6", "yuku-codegen": "^0.5.44", "yuku-parser": "^0.5.44" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "publint": "^0.3.8", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", "unplugin-unused": "^0.5.0", "unrun": "*", "yaml": "^2.4.2" }, "optionalPeers": ["@arethetypeswrong/core", "@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "publint", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "typescript", "unplugin-unused", "unrun", "yaml"] }, "sha512-zqaBkM1XZ4IHvFJlG2uv7kQF+dqtUcxXSPGyn15BXRfQb101HPpXXe6PHi7YOmZmbQd+hnwNr5+aklXSriZhVQ=="], + + "@voidzero-dev/vite-plus-darwin-arm64": ["@voidzero-dev/vite-plus-darwin-arm64@0.2.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1DsMuysinq4aDiM4212sp5vdDeBPZPdiWwOOZcAl2AVq25hhqDEHdken/PXKHmiXg8M2FGdydpcUtxR5kwdetg=="], + + "@voidzero-dev/vite-plus-darwin-x64": ["@voidzero-dev/vite-plus-darwin-x64@0.2.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZYSau3poUyX1qPmxYzFuHtGIE7T0zI8KDY/oAXoDBb5M2HSd+uu0ov6jNETkBphMlcSmQ/lT+FFlAkCBZklpqg=="], + + "@voidzero-dev/vite-plus-linux-arm64-gnu": ["@voidzero-dev/vite-plus-linux-arm64-gnu@0.2.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-7cpZOsHb6nuX6kRCXwWtzBxxJmCVrPcBe/H4q6n1yhWDtqZp/YY05CdFh458V7qUmIKIl70iA5vQ4M9qLPgYcA=="], + + "@voidzero-dev/vite-plus-linux-arm64-musl": ["@voidzero-dev/vite-plus-linux-arm64-musl@0.2.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-Aaqm3rlIr6qV2CdQjkyBceQqUlBpvvhGjKfpdAWhPaur4nN44gmjky88vMqL/1R6pieCdfAtUw4a6IVQ/Dq5VQ=="], + + "@voidzero-dev/vite-plus-linux-x64-gnu": ["@voidzero-dev/vite-plus-linux-x64-gnu@0.2.7", "", { "os": "linux", "cpu": "x64" }, "sha512-LSdD1ID4Mdufckk/Oc+IzhyXCFFUVGR+teiToQNzhD3aoEhrJQMKarx/Ea+dInRdBTNmioAL2xT1R2HP9a+HOA=="], + + "@voidzero-dev/vite-plus-linux-x64-musl": ["@voidzero-dev/vite-plus-linux-x64-musl@0.2.7", "", { "os": "linux", "cpu": "x64" }, "sha512-0SwXa8XyagGcyCfjYr6vrOCcEJ37VCvO8MV1y0HVjcO+eqH399NgWNvHRUHiA2baiWQeBBBB+B9WyCpdpKSiag=="], + + "@voidzero-dev/vite-plus-win32-arm64-msvc": ["@voidzero-dev/vite-plus-win32-arm64-msvc@0.2.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-99spn9NEABFyn2lCrZL1nfpX06HhDNyWfZvX+e8zvSqV4LP2td2O4jI6St6KjuwVrK0VveNI/vwzJAe3CSGbzQ=="], + + "@voidzero-dev/vite-plus-win32-x64-msvc": ["@voidzero-dev/vite-plus-win32-x64-msvc@0.2.7", "", { "os": "win32", "cpu": "x64" }, "sha512-Qe5XZLntwivz2fc1POrJnN6vIYGPcNks5q+GHPaCmYUtIBmAx53ME9OC/tLPpfno2jlZAHLy1vLAirkMVzvrwA=="], + + "@yuku-codegen/binding-darwin-arm64": ["@yuku-codegen/binding-darwin-arm64@0.5.48", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yo96Oef12WzqnphInfz/eexVse3+kWgfGS5g2S3rFS3dcGn1ENW9xLFDZUP9rh+yP76DOq38wBoFi1+I9+6qBg=="], + + "@yuku-codegen/binding-darwin-x64": ["@yuku-codegen/binding-darwin-x64@0.5.48", "", { "os": "darwin", "cpu": "x64" }, "sha512-aRCTw0EZC4bVosmw//0OMYP5tGWFE0Cu5yUBFkUbhXx/iBzvORcJ2xPNlOp/vtCCo9Ys4vp8b0DigJV6uOVb2g=="], + + "@yuku-codegen/binding-freebsd-x64": ["@yuku-codegen/binding-freebsd-x64@0.5.48", "", { "os": "freebsd", "cpu": "x64" }, "sha512-CA0AQAEApDkbw51PdLWMtKPJ41/7rvXsS3SJs+phG7fHJI+MuFzWuLbkucZfZoEOiDscmcsfYIdgL8BsfuyKKQ=="], + + "@yuku-codegen/binding-linux-arm-gnu": ["@yuku-codegen/binding-linux-arm-gnu@0.5.48", "", { "os": "linux", "cpu": "arm" }, "sha512-DuSQlk8bH4gpmW3/00P0NLagAcMv8jOxjT40cQmxKRkktr+SUOALCfkT89tdDq3qtY95NR2GXOZ7AjNh7KKqCw=="], + + "@yuku-codegen/binding-linux-arm-musl": ["@yuku-codegen/binding-linux-arm-musl@0.5.48", "", { "os": "linux", "cpu": "arm" }, "sha512-bxj4Ee+wlaJcWJwft2ReJXWw5sfl1qavDz6+dlRdU1xfTEtjPSNiAWhiCHnJR0R4Ygd57DnzSQmAVGvFv6RcGw=="], + + "@yuku-codegen/binding-linux-arm64-gnu": ["@yuku-codegen/binding-linux-arm64-gnu@0.5.48", "", { "os": "linux", "cpu": "arm64" }, "sha512-mk5JVWh+0JOe5ue8k17kbYX8uGBoKt3ZqoCyxNh4nYAAcX7+X1tFUiU7jbjctu4vHeejCBFSTdQ021+V31cUCQ=="], + + "@yuku-codegen/binding-linux-arm64-musl": ["@yuku-codegen/binding-linux-arm64-musl@0.5.48", "", { "os": "linux", "cpu": "arm64" }, "sha512-4q3vkrNghbllyxOm2KesFLxCPKHF7r3JyQ7BWZccY1j2Y05yKoIFhoWCqIuQ2W/dpte9RI0+OVfwyxnrKg6fkA=="], + + "@yuku-codegen/binding-linux-x64-gnu": ["@yuku-codegen/binding-linux-x64-gnu@0.5.48", "", { "os": "linux", "cpu": "x64" }, "sha512-csd4M1EVrGaohM8acM6gq1zpUA/Rwe2ulUMBKUcwQXm/k6n7cq1A++qdew78SOVb4do3JH1WE+WFwoGQAcWc1w=="], + + "@yuku-codegen/binding-linux-x64-musl": ["@yuku-codegen/binding-linux-x64-musl@0.5.48", "", { "os": "linux", "cpu": "x64" }, "sha512-KcDuEOT+GFoVKdvAWOv1v9iYjwnmvMZlO+j1Rw+5PYdeFLGWGzv/DD11y4SAAdwXIFcil4T0hibeIaF82WStMg=="], + + "@yuku-codegen/binding-win32-arm64": ["@yuku-codegen/binding-win32-arm64@0.5.48", "", { "os": "win32", "cpu": "arm64" }, "sha512-HI8qNrI8dWM5BuqIMKsqornRvTNFrE6sm5zToIJ9YIa9zt5+29P7fJ7Nr39EVf6dAWSb6q7JSpScJnRsQ+FgZA=="], + + "@yuku-codegen/binding-win32-x64": ["@yuku-codegen/binding-win32-x64@0.5.48", "", { "os": "win32", "cpu": "x64" }, "sha512-X5YWJLO6EfBZpeBqO0AYESnUizbpFDWArcvVD61w0PEWQ3CaFRLnbQXs+kpM4ZZfGMfIE22zfA08QSY67q7TNQ=="], + + "@yuku-parser/binding-darwin-arm64": ["@yuku-parser/binding-darwin-arm64@0.5.48", "", { "os": "darwin", "cpu": "arm64" }, "sha512-If8mb7HH3vqghJ2NNZ8SuHfhsnjVzOxJpB8xcNOXS5WjYrs2mUhHIh5KOIvK13hDOzh0htGeGK3A6MsiEqE7HQ=="], + + "@yuku-parser/binding-darwin-x64": ["@yuku-parser/binding-darwin-x64@0.5.48", "", { "os": "darwin", "cpu": "x64" }, "sha512-EimvPXfspzxf1K11eB6tCW5oiQEXB8g84T2wP1TwzQagdDKo33bkmmVF0B32vTIpXnk/Ifu5IB61izZ1MylljA=="], + + "@yuku-parser/binding-freebsd-x64": ["@yuku-parser/binding-freebsd-x64@0.5.48", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0GcUMrumLHheThY9r5Tp46gaZYzn0irWPS1Zba6WY+vVQfhUtzGiWgXxI6tuXX0N32kEaaEVRpkKctvo6Kx3aQ=="], + + "@yuku-parser/binding-linux-arm-gnu": ["@yuku-parser/binding-linux-arm-gnu@0.5.48", "", { "os": "linux", "cpu": "arm" }, "sha512-8S5T5wjCC73dmmpQeZ49aYsSunIUM3D4Fc6rdK96c+Ayg/p3FmeSPF3xuLZHejcTmqJIIvnbfPlUF+rB6DITjQ=="], + + "@yuku-parser/binding-linux-arm-musl": ["@yuku-parser/binding-linux-arm-musl@0.5.48", "", { "os": "linux", "cpu": "arm" }, "sha512-tTmbxvnUHcK2/crS9547vk2SMmsajH1yqJ8ltXhIuHJgqR1v+d9n9KT+kSayo/5CS76LegeYxhMFjEivBH2hFA=="], + + "@yuku-parser/binding-linux-arm64-gnu": ["@yuku-parser/binding-linux-arm64-gnu@0.5.48", "", { "os": "linux", "cpu": "arm64" }, "sha512-KGYCBMqI2zfwyhgq5tpPVNe7jpUeYTBm8DhjdS+zqWNumde/PEC170QE5RHxcOAlsirIDeIUk0jqx+r/axoFSw=="], + + "@yuku-parser/binding-linux-arm64-musl": ["@yuku-parser/binding-linux-arm64-musl@0.5.48", "", { "os": "linux", "cpu": "arm64" }, "sha512-2wTSMsCSXLTc2lZUjMAuU5X4cje55u205WJqfV5NWNF6j9pW/tXyxr15dJeekj8ziLqBXzIsj4DbRh4sY/WcjA=="], + + "@yuku-parser/binding-linux-x64-gnu": ["@yuku-parser/binding-linux-x64-gnu@0.5.48", "", { "os": "linux", "cpu": "x64" }, "sha512-d/6v9UnGglVu1WC2JQyv/5aWSi5fXZeGSlidCfmHp4+N65N1GDKUnFtys5MK5eAPeAjTgSHGGtOc/yCcKTlv3A=="], + + "@yuku-parser/binding-linux-x64-musl": ["@yuku-parser/binding-linux-x64-musl@0.5.48", "", { "os": "linux", "cpu": "x64" }, "sha512-gX19gw6u4ApPy7SYMPKfFlEkrtj6WlORvrTKK3sBQqjyV+8+mUAkQgxXNjHw4RnOiAmVYg7TOlZcg8d+Qqod9A=="], + + "@yuku-parser/binding-win32-arm64": ["@yuku-parser/binding-win32-arm64@0.5.48", "", { "os": "win32", "cpu": "arm64" }, "sha512-w6cQQLbqj3Jcom5Q7ifm103NUOQ9d+Cb4VU5lkrZDjMnwVJ9Hzzg1vCQR7miJuF44vhCXldbme5UryE3giEKlA=="], + + "@yuku-parser/binding-win32-x64": ["@yuku-parser/binding-win32-x64@0.5.48", "", { "os": "win32", "cpu": "x64" }, "sha512-4gO0HmG7fzFxrw1rs0dUdnnaY9YgennjETqDWrTSp7x9fmTUOAoN4VsMfP7YyliQeG1WJJHc55O+rOhmsLppow=="], + + "@yuku-toolchain/types": ["@yuku-toolchain/types@0.5.43", "", {}, "sha512-kSpvPntnXw5+lYjO71ffBEnQ5ycQ74KGIYknh0TS4xeyCuBkOqxyJumxZkMhLBBUCLjDAbx2+Icnr3Zh4ftjpQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], @@ -230,8 +411,12 @@ "argv-formatter": ["argv-formatter@1.0.0", "", {}, "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw=="], + "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + "array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], "bottleneck": ["bottleneck@2.19.5", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="], @@ -244,6 +429,8 @@ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="], @@ -278,6 +465,8 @@ "convert-hrtime": ["convert-hrtime@5.0.0", "", {}, "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], "cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="], @@ -290,8 +479,14 @@ "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], "drizzle-kit": ["drizzle-kit@0.31.9", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg=="], @@ -312,6 +507,8 @@ "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + "es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], @@ -320,8 +517,12 @@ "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "expect-type": ["expect-type@1.4.0", "", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="], + "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], @@ -340,6 +541,8 @@ "fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "function-timeout": ["function-timeout@1.0.2", "", {}, "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], @@ -420,27 +623,29 @@ "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], - "lefthook": ["lefthook@1.13.6", "", { "optionalDependencies": { "lefthook-darwin-arm64": "1.13.6", "lefthook-darwin-x64": "1.13.6", "lefthook-freebsd-arm64": "1.13.6", "lefthook-freebsd-x64": "1.13.6", "lefthook-linux-arm64": "1.13.6", "lefthook-linux-x64": "1.13.6", "lefthook-openbsd-arm64": "1.13.6", "lefthook-openbsd-x64": "1.13.6", "lefthook-windows-arm64": "1.13.6", "lefthook-windows-x64": "1.13.6" }, "bin": { "lefthook": "bin/index.js" } }, "sha512-ojj4/4IJ29Xn4drd5emqVgilegAPN3Kf0FQM2p/9+lwSTpU+SZ1v4Ig++NF+9MOa99UKY8bElmVrLhnUUNFh5g=="], + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], - "lefthook-darwin-arm64": ["lefthook-darwin-arm64@1.13.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-m6Lb77VGc84/Qo21Lhq576pEvcgFCnvloEiP02HbAHcIXD0RTLy9u2yAInrixqZeaz13HYtdDaI7OBYAAdVt8A=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], - "lefthook-darwin-x64": ["lefthook-darwin-x64@1.13.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-CoRpdzanu9RK3oXR1vbEJA5LN7iB+c7hP+sONeQJzoOXuq4PNKVtEaN84Gl1BrVtCNLHWFAvCQaZPPiiXSy8qg=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], - "lefthook-freebsd-arm64": ["lefthook-freebsd-arm64@1.13.6", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-X4A7yfvAJ68CoHTqP+XvQzdKbyd935sYy0bQT6Ajz7FL1g7hFiro8dqHSdPdkwei9hs8hXeV7feyTXbYmfjKQQ=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], - "lefthook-freebsd-x64": ["lefthook-freebsd-x64@1.13.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-ai2m+Sj2kGdY46USfBrCqLKe9GYhzeq01nuyDYCrdGISePeZ6udOlD1k3lQKJGQCHb0bRz4St0r5nKDSh1x/2A=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], - "lefthook-linux-arm64": ["lefthook-linux-arm64@1.13.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-cbo4Wtdq81GTABvikLORJsAWPKAJXE8Q5RXsICFUVznh5PHigS9dFW/4NXywo0+jfFPCT6SYds2zz4tCx6DA0Q=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], - "lefthook-linux-x64": ["lefthook-linux-x64@1.13.6", "", { "os": "linux", "cpu": "x64" }, "sha512-uJl9vjCIIBTBvMZkemxCE+3zrZHlRO7Oc+nZJ+o9Oea3fu+W82jwX7a7clw8jqNfaeBS+8+ZEQgiMHWCloTsGw=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], - "lefthook-openbsd-arm64": ["lefthook-openbsd-arm64@1.13.6", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7r153dxrNRQ9ytRs2PmGKKkYdvZYFPre7My7XToSTiRu5jNCq++++eAKVkoyWPduk97dGIA+YWiEr5Noe0TK2A=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], - "lefthook-openbsd-x64": ["lefthook-openbsd-x64@1.13.6", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Z+UhLlcg1xrXOidK3aLLpgH7KrwNyWYE3yb7ITYnzJSEV8qXnePtVu8lvMBHs/myzemjBzeIr/U/+ipjclR06g=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], - "lefthook-windows-arm64": ["lefthook-windows-arm64@1.13.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-Uxef6qoDxCmUNQwk8eBvddYJKSBFglfwAY9Y9+NnnmiHpWTjjYiObE9gT2mvGVpEgZRJVAatBXc+Ha5oDD/OgQ=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], - "lefthook-windows-x64": ["lefthook-windows-x64@1.13.6", "", { "os": "win32", "cpu": "x64" }, "sha512-mOZoM3FQh3o08M8PQ/b3IYuL5oo36D9ehczIw1dAgp1Ly+Tr4fJ96A+4SEJrQuYeRD4mex9bR7Ps56I73sBSZA=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], @@ -464,6 +669,10 @@ "lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "make-asynchronous": ["make-asynchronous@1.0.1", "", { "dependencies": { "p-event": "^6.0.0", "type-fest": "^4.6.0", "web-worker": "1.2.0" } }, "sha512-T9BPOmEOhp6SmV25SwLVcHK4E6JyG/coH3C6F1NjNXSziv/fd4GmsqMk8YR6qpPOswfaOCApSNkZv6fxoaYFcQ=="], "marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], @@ -482,10 +691,14 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], "nerf-dart": ["nerf-dart@1.0.0", "", {}, "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g=="], @@ -502,8 +715,16 @@ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "obug": ["obug@2.1.4", "", {}, "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "oxfmt": ["oxfmt@0.60.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.60.0", "@oxfmt/binding-android-arm64": "0.60.0", "@oxfmt/binding-darwin-arm64": "0.60.0", "@oxfmt/binding-darwin-x64": "0.60.0", "@oxfmt/binding-freebsd-x64": "0.60.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.60.0", "@oxfmt/binding-linux-arm-musleabihf": "0.60.0", "@oxfmt/binding-linux-arm64-gnu": "0.60.0", "@oxfmt/binding-linux-arm64-musl": "0.60.0", "@oxfmt/binding-linux-ppc64-gnu": "0.60.0", "@oxfmt/binding-linux-riscv64-gnu": "0.60.0", "@oxfmt/binding-linux-riscv64-musl": "0.60.0", "@oxfmt/binding-linux-s390x-gnu": "0.60.0", "@oxfmt/binding-linux-x64-gnu": "0.60.0", "@oxfmt/binding-linux-x64-musl": "0.60.0", "@oxfmt/binding-openharmony-arm64": "0.60.0", "@oxfmt/binding-win32-arm64-msvc": "0.60.0", "@oxfmt/binding-win32-ia32-msvc": "0.60.0", "@oxfmt/binding-win32-x64-msvc": "0.60.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-fViX6i+gJuZWY+jI/fnR6WRbRj70GZ9RlCd30MygJrHTUNc4DxvKHWw8vBjMjffv3PgU5qWDR0AzmojQByqaZA=="], + + "oxlint": ["oxlint@1.75.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.75.0", "@oxlint/binding-android-arm64": "1.75.0", "@oxlint/binding-darwin-arm64": "1.75.0", "@oxlint/binding-darwin-x64": "1.75.0", "@oxlint/binding-freebsd-x64": "1.75.0", "@oxlint/binding-linux-arm-gnueabihf": "1.75.0", "@oxlint/binding-linux-arm-musleabihf": "1.75.0", "@oxlint/binding-linux-arm64-gnu": "1.75.0", "@oxlint/binding-linux-arm64-musl": "1.75.0", "@oxlint/binding-linux-ppc64-gnu": "1.75.0", "@oxlint/binding-linux-riscv64-gnu": "1.75.0", "@oxlint/binding-linux-riscv64-musl": "1.75.0", "@oxlint/binding-linux-s390x-gnu": "1.75.0", "@oxlint/binding-linux-x64-gnu": "1.75.0", "@oxlint/binding-linux-x64-musl": "1.75.0", "@oxlint/binding-openharmony-arm64": "1.75.0", "@oxlint/binding-win32-arm64-msvc": "1.75.0", "@oxlint/binding-win32-ia32-msvc": "1.75.0", "@oxlint/binding-win32-x64-msvc": "1.75.0" }, "peerDependencies": { "oxlint-tsgolint": ">=7.0.2001", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-m9WzjRcRYA/uqIZDa9tclrieoPJ/ln1QYTKdFx6NUOs8uY5DiHlIwRQoCrHT6OM6O3ww3l2skY5gO7G7ZphE7g=="], + + "oxlint-tsgolint": ["oxlint-tsgolint@7.0.2001", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "7.0.2001", "@oxlint-tsgolint/darwin-x64": "7.0.2001", "@oxlint-tsgolint/linux-arm64": "7.0.2001", "@oxlint-tsgolint/linux-x64": "7.0.2001", "@oxlint-tsgolint/win32-arm64": "7.0.2001", "@oxlint-tsgolint/win32-x64": "7.0.2001" }, "bin": { "tsgolint": "./bin/tsgolint.js" } }, "sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg=="], + "p-each-series": ["p-each-series@3.0.0", "", {}, "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw=="], "p-event": ["p-event@6.0.1", "", { "dependencies": { "p-timeout": "^6.1.2" } }, "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w=="], @@ -540,6 +761,8 @@ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -548,8 +771,14 @@ "pkg-conf": ["pkg-conf@2.1.0", "", { "dependencies": { "find-up": "^2.0.0", "load-json-file": "^4.0.0" } }, "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g=="], + "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="], + + "postcss": ["postcss@8.5.25", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw=="], + "postgres": ["postgres@3.4.8", "", {}, "sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg=="], + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], @@ -558,6 +787,8 @@ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "read-package-up": ["read-package-up@12.0.0", "", { "dependencies": { "find-up-simple": "^1.0.1", "read-pkg": "^10.0.0", "type-fest": "^5.2.0" } }, "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw=="], "read-pkg": ["read-pkg@10.1.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.4", "normalize-package-data": "^8.0.0", "parse-json": "^8.3.0", "type-fest": "^5.4.4", "unicorn-magic": "^0.4.0" } }, "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg=="], @@ -584,14 +815,20 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "signale": ["signale@1.4.0", "", { "dependencies": { "chalk": "^2.3.2", "figures": "^2.0.0", "pkg-conf": "^2.1.0" } }, "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w=="], + "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], + "skin-tone": ["skin-tone@2.0.0", "", { "dependencies": { "unicode-emoji-modifier-base": "^1.0.0" } }, "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA=="], "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], "spawn-error-forwarder": ["spawn-error-forwarder@1.0.0", "", {}, "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g=="], @@ -606,6 +843,10 @@ "split2": ["split2@1.0.0", "", { "dependencies": { "through2": "~2.0.0" } }, "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@4.2.0", "", {}, "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw=="], + "stream-combiner2": ["stream-combiner2@1.1.1", "", { "dependencies": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" } }, "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -640,10 +881,20 @@ "time-span": ["time-span@5.1.0", "", { "dependencies": { "convert-hrtime": "^5.0.0" } }, "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA=="], + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + + "tinyrainbow": ["tinyrainbow@3.1.1", "", {}, "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + "traverse": ["traverse@0.6.8", "", {}, "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA=="], "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], @@ -676,14 +927,24 @@ "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], + "vite": ["@voidzero-dev/vite-plus-core@0.2.7", "", { "dependencies": { "@oxc-project/runtime": "=0.141.0", "@oxc-project/types": "=0.141.0", "lightningcss": "^1.33.0", "postcss": "^8.5.6", "yuku-codegen": "^0.5.44", "yuku-parser": "^0.5.44" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "publint": "^0.3.8", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", "unplugin-unused": "^0.5.0", "unrun": "*", "yaml": "^2.4.2" }, "optionalPeers": ["@arethetypeswrong/core", "@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "publint", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "typescript", "unplugin-unused", "unrun", "yaml"] }, "sha512-zqaBkM1XZ4IHvFJlG2uv7kQF+dqtUcxXSPGyn15BXRfQb101HPpXXe6PHi7YOmZmbQd+hnwNr5+aklXSriZhVQ=="], + + "vite-plus": ["vite-plus@0.2.7", "", { "dependencies": { "@oxc-project/types": "=0.141.0", "@oxlint/plugins": "=1.73.0", "@vitest/browser": "4.1.10", "@vitest/browser-preview": "4.1.10", "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", "@vitest/pretty-format": "4.1.10", "@vitest/runner": "4.1.10", "@vitest/snapshot": "4.1.10", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "@voidzero-dev/vite-plus-core": "0.2.7", "oxfmt": "=0.60.0", "oxlint": "=1.75.0", "oxlint-tsgolint": "=7.0.2001", "vitest": "4.1.10" }, "optionalDependencies": { "@voidzero-dev/vite-plus-darwin-arm64": "0.2.7", "@voidzero-dev/vite-plus-darwin-x64": "0.2.7", "@voidzero-dev/vite-plus-linux-arm64-gnu": "0.2.7", "@voidzero-dev/vite-plus-linux-arm64-musl": "0.2.7", "@voidzero-dev/vite-plus-linux-x64-gnu": "0.2.7", "@voidzero-dev/vite-plus-linux-x64-musl": "0.2.7", "@voidzero-dev/vite-plus-win32-arm64-msvc": "0.2.7", "@voidzero-dev/vite-plus-win32-x64-msvc": "0.2.7" }, "peerDependencies": { "@vitest/browser-playwright": "4.1.10", "@vitest/browser-webdriverio": "4.1.10" }, "optionalPeers": ["@vitest/browser-playwright", "@vitest/browser-webdriverio"], "bin": { "oxfmt": "bin/oxfmt", "oxlint": "bin/oxlint", "vp": "bin/vp", "vpr": "bin/vpr" } }, "sha512-4xFo1nDtH/isWBdJaCgmctMiNohbuxa0uiKA/cbpL8jT18Ax73Lt6rK20/LkqPG5a/HpvPNhDCS1QK58pH4txA=="], + + "vitest": ["vitest@4.1.10", "", { "dependencies": { "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", "@vitest/pretty-format": "4.1.10", "@vitest/runner": "4.1.10", "@vitest/snapshot": "4.1.10", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.10", "@vitest/browser-preview": "4.1.10", "@vitest/browser-webdriverio": "4.1.10", "@vitest/coverage-istanbul": "4.1.10", "@vitest/coverage-v8": "4.1.10", "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw=="], + "web-worker": ["web-worker@1.2.0", "", {}, "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + "ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -694,6 +955,10 @@ "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + "yuku-codegen": ["yuku-codegen@0.5.48", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" }, "optionalDependencies": { "@yuku-codegen/binding-darwin-arm64": "0.5.48", "@yuku-codegen/binding-darwin-x64": "0.5.48", "@yuku-codegen/binding-freebsd-x64": "0.5.48", "@yuku-codegen/binding-linux-arm-gnu": "0.5.48", "@yuku-codegen/binding-linux-arm-musl": "0.5.48", "@yuku-codegen/binding-linux-arm64-gnu": "0.5.48", "@yuku-codegen/binding-linux-arm64-musl": "0.5.48", "@yuku-codegen/binding-linux-x64-gnu": "0.5.48", "@yuku-codegen/binding-linux-x64-musl": "0.5.48", "@yuku-codegen/binding-win32-arm64": "0.5.48", "@yuku-codegen/binding-win32-x64": "0.5.48" } }, "sha512-p7HxD5Xl4jzDzqMrGePAOeSHmRY4g58h4HuGq15weQFPxuPWd/W6e7nqp/+Lea6JfpOdBwJOAyXFqIZ/J9Zfnw=="], + + "yuku-parser": ["yuku-parser@0.5.48", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" }, "optionalDependencies": { "@yuku-parser/binding-darwin-arm64": "0.5.48", "@yuku-parser/binding-darwin-x64": "0.5.48", "@yuku-parser/binding-freebsd-x64": "0.5.48", "@yuku-parser/binding-linux-arm-gnu": "0.5.48", "@yuku-parser/binding-linux-arm-musl": "0.5.48", "@yuku-parser/binding-linux-arm64-gnu": "0.5.48", "@yuku-parser/binding-linux-arm64-musl": "0.5.48", "@yuku-parser/binding-linux-x64-gnu": "0.5.48", "@yuku-parser/binding-linux-x64-musl": "0.5.48", "@yuku-parser/binding-win32-arm64": "0.5.48", "@yuku-parser/binding-win32-x64": "0.5.48" } }, "sha512-OWBfhrpgK9+/4+IXG9oT8Bao4AhViQA7vdyNNH7EUg8dQYgwa70XtIBWTpCEme1P1ECyoDNYkn0wT63f8XRcVA=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "@actions/http-client/undici": ["undici@6.23.0", "", {}, "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g=="], @@ -1048,6 +1313,10 @@ "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], + "pretty-format/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "read-pkg/parse-json": ["parse-json@8.3.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "index-to-position": "^1.1.0", "type-fest": "^4.39.1" } }, "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ=="], "semantic-release/@semantic-release/error": ["@semantic-release/error@4.0.0", "", {}, "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ=="], @@ -1070,6 +1339,8 @@ "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "vitest/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], diff --git a/docker-compose.yml b/docker-compose.yml index 3e41adc..d91f35a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,11 +7,11 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: fipe ports: - - '5433:5432' + - "5433:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: ['CMD-SHELL', 'pg_isready -U postgres'] + test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 diff --git a/drizzle.config.ts b/drizzle.config.ts index 6405413..4b52a91 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,10 +1,10 @@ -import { defineConfig } from 'drizzle-kit'; +import { defineConfig } from "drizzle-kit"; export default defineConfig({ - schema: './src/db/schema.ts', - out: './drizzle', - dialect: 'postgresql', + schema: "./src/db/schema.ts", + out: "./drizzle", + dialect: "postgresql", dbCredentials: { - url: process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/fipe', + url: process.env.DATABASE_URL || "postgres://postgres:postgres@localhost:5432/fipe", }, }); diff --git a/lefthook.yml b/lefthook.yml deleted file mode 100644 index 97d453f..0000000 --- a/lefthook.yml +++ /dev/null @@ -1,9 +0,0 @@ -pre-commit: - parallel: true - commands: - lint-and-format: - glob: "*.{js,ts,json}" - run: bunx biome check --write {staged_files} - stage_fixed: true - typecheck: - run: bunx tsc --noEmit diff --git a/package.json b/package.json index 2e8a796..e6d11a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { "name": "fipe-data-pipeline", "version": "1.14.1", + "description": "Data pipeline for FIPE vehicle pricing data", + "license": "MIT", "type": "module", "scripts": { "crawl": "bun src/index.ts crawl", @@ -8,17 +10,16 @@ "classify": "bun src/index.ts classify", "backup": "bun src/index.ts backup", "restore-drill": "bun src/index.ts restore-drill", - "test": "bun test", - "check": "bun run format:check && bun run lint && bun run typecheck && bun test", + "prepare": "test ! -x node_modules/.bin/vp || vp config --no-agent", + "test": "vp test run", + "check": "vp check && vp test run", "db:push": "drizzle-kit push", - "lint": "biome check .", - "lint:fix": "biome check --write .", - "format": "biome format --write .", - "format:check": "biome format .", - "typecheck": "bunx tsc --noEmit" + "lint": "vp lint", + "lint:fix": "vp lint --fix", + "format": "vp fmt", + "format:check": "vp fmt --check", + "typecheck": "vp check --no-fmt --no-lint" }, - "license": "MIT", - "description": "Data pipeline for FIPE vehicle pricing data", "dependencies": { "@anthropic-ai/sdk": "^0.71.2", "cli-progress": "^3.12.0", @@ -28,14 +29,24 @@ "zod": "^4.1.13" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", "@semantic-release/git": "^10.0.1", "@types/bun": "^1.3.0", "@types/cli-progress": "^3.11.6", "@types/node": "^24.10.2", "drizzle-kit": "^0.31.8", - "lefthook": "^1.11.5", "semantic-release": "^25.0.2", - "typescript": "7.0.2" + "typescript": "7.0.2", + "vite": "npm:@voidzero-dev/vite-plus-core@0.2.7", + "vite-plus": "0.2.7" + }, + "overrides": { + "vite": "npm:@voidzero-dev/vite-plus-core@0.2.7" + }, + "devEngines": { + "packageManager": { + "name": "bun", + "version": "1.3.14", + "onFail": "download" + } } } diff --git a/pickled.yml b/pickled.yml index 12ad13b..5e862ef 100644 --- a/pickled.yml +++ b/pickled.yml @@ -40,11 +40,25 @@ facts: quickstart: statement: Mostra os comandos de início rápido. match: - allOf: ["cp .env.example .env", "docker compose up -d", "bun install", "bun run db:push", "bun run crawl"] + allOf: + [ + "cp .env.example .env", + "docker compose up -d", + "bun install", + "bun run db:push", + "bun run crawl", + ] project_commands: statement: Lista comandos operacionais principais. match: - allOf: ["bun run status", "bun run classify", "bun run backup", "bun run restore-drill", "bun run typecheck"] + allOf: + [ + "bun run status", + "bun run classify", + "bun run backup", + "bun run restore-drill", + "bun run typecheck", + ] crawl_year_month: statement: Mostra filtros de ano e mês, incluindo ranges e listas. match: @@ -69,7 +83,12 @@ facts: docker_usage: statement: Mostra build, run e docker exec para crawler. match: - allOf: ["docker build -t fipe-crawler .", "docker run -d --name fipe --env-file .env fipe-crawler", "docker exec fipe bun src/index.ts crawl"] + allOf: + [ + "docker build -t fipe-crawler .", + "docker run -d --name fipe --env-file .env fipe-crawler", + "docker exec fipe bun src/index.ts crawl", + ] config_env: statement: Nomeia variáveis principais de banco e rate limit. match: diff --git a/src/backup.ts b/src/backup.ts index 1a34b41..aa27bf7 100644 --- a/src/backup.ts +++ b/src/backup.ts @@ -1,5 +1,5 @@ -import { unlink } from 'node:fs/promises'; -import { env } from './config.js'; +import { unlink } from "node:fs/promises"; +import { env } from "./config.js"; // Disaster-recovery backups: pg_dump -Fc uploaded to S3-compatible object // storage (e.g. Cloudflare R2) via aws-cli, with daily + monthly retention and @@ -9,11 +9,11 @@ import { env } from './config.js'; // predefined pg_read_all_data role works) and CREATEDB for the restore drill's // throwaway scratch database, which it always drops afterward. -const DAILY_PREFIX = 'daily/'; -const MONTHLY_PREFIX = 'monthly/'; +const DAILY_PREFIX = "daily/"; +const MONTHLY_PREFIX = "monthly/"; const DAILY_KEEP = 14; const MONTHLY_KEEP = 12; -const SCRATCH_DB = 'fipe_restore_drill'; +const SCRATCH_DB = "fipe_restore_drill"; interface R2Config { accessKeyId: string; @@ -26,7 +26,7 @@ function requireR2(): R2Config { const { R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_ENDPOINT, R2_BUCKET } = env; if (!R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY || !R2_ENDPOINT || !R2_BUCKET) { throw new Error( - 'R2 not configured: set R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_ENDPOINT, R2_BUCKET', + "R2 not configured: set R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_ENDPOINT, R2_BUCKET", ); } return { @@ -42,12 +42,12 @@ function awsEnv(r2: R2Config): Record { ...(process.env as Record), AWS_ACCESS_KEY_ID: r2.accessKeyId, AWS_SECRET_ACCESS_KEY: r2.secretAccessKey, - AWS_DEFAULT_REGION: 'auto', + AWS_DEFAULT_REGION: "auto", }; } async function run(cmd: string[], runEnv?: Record): Promise { - const proc = Bun.spawn(cmd, { stdout: 'pipe', stderr: 'pipe', env: runEnv }); + const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe", env: runEnv }); const [stdout, stderr, code] = await Promise.all([ new Response(proc.stdout).text(), new Response(proc.stderr).text(), @@ -55,7 +55,7 @@ async function run(cmd: string[], runEnv?: Record): Promise): Promise { const out = await run( [ - 'aws', - 's3api', - 'list-objects-v2', - '--bucket', + "aws", + "s3api", + "list-objects-v2", + "--bucket", r2.bucket, - '--prefix', + "--prefix", prefix, - '--endpoint-url', + "--endpoint-url", r2.endpoint, - '--output', - 'json', + "--output", + "json", ], awsEnv(r2), ); if (!out.trim()) return []; const parsed = JSON.parse(out) as { Contents?: { Key: string }[] }; - return (parsed.Contents ?? []).map((o) => o.Key).filter((k) => k.endsWith('.dump')); + return (parsed.Contents ?? []).map((o) => o.Key).filter((k) => k.endsWith(".dump")); } async function s3Copy(r2: R2Config, from: string, to: string): Promise { await run( - ['aws', 's3', 'cp', from, to, '--endpoint-url', r2.endpoint, '--only-show-errors'], + ["aws", "s3", "cp", from, to, "--endpoint-url", r2.endpoint, "--only-show-errors"], awsEnv(r2), ); } @@ -105,14 +105,14 @@ async function s3Copy(r2: R2Config, from: string, to: string): Promise { async function s3Delete(r2: R2Config, key: string): Promise { await run( [ - 'aws', - 's3api', - 'delete-object', - '--bucket', + "aws", + "s3api", + "delete-object", + "--bucket", r2.bucket, - '--key', + "--key", key, - '--endpoint-url', + "--endpoint-url", r2.endpoint, ], awsEnv(r2), @@ -136,7 +136,7 @@ export async function runBackup(): Promise { try { console.log(`[backup] pg_dump -> ${tmp}`); - await run(['pg_dump', '-Fc', '--no-owner', '--no-privileges', '-f', tmp, env.DATABASE_URL]); + await run(["pg_dump", "-Fc", "--no-owner", "--no-privileges", "-f", tmp, env.DATABASE_URL]); console.log(`[backup] dump ${(Bun.file(tmp).size / 1e6).toFixed(1)} MB`); const dailyKey = `${DAILY_PREFIX}fipe-${stamp}.dump`; @@ -146,7 +146,7 @@ export async function runBackup(): Promise { // Keep one dump per month, created by the first successful run of the month // (checked by existence, not the calendar day) so a missed run on the 1st // does not skip the monthly point. - const ym = `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, '0')}`; + const ym = `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}`; const monthlyKey = `${MONTHLY_PREFIX}fipe-${ym}.dump`; if (!(await s3DumpKeys(r2, MONTHLY_PREFIX)).includes(monthlyKey)) { await s3Copy(r2, tmp, `s3://${r2.bucket}/${monthlyKey}`); @@ -156,7 +156,7 @@ export async function runBackup(): Promise { const prunedDaily = await prune(r2, DAILY_PREFIX, DAILY_KEEP); const prunedMonthly = await prune(r2, MONTHLY_PREFIX, MONTHLY_KEEP); console.log(`[backup] pruned daily=${prunedDaily} monthly=${prunedMonthly}`); - console.log('[backup] done'); + console.log("[backup] done"); } finally { await unlink(tmp).catch(() => {}); } @@ -168,51 +168,51 @@ export async function runRestoreDrill(): Promise { const r2 = requireR2(); const keys = (await s3DumpKeys(r2, DAILY_PREFIX)).sort(); if (keys.length === 0) { - throw new Error('[restore-drill] no daily backups found'); + throw new Error("[restore-drill] no daily backups found"); } const latest = keys[keys.length - 1]; - const tmp = '/tmp/restore-drill.dump'; + const tmp = "/tmp/restore-drill.dump"; console.log(`[restore-drill] downloading ${latest}`); await s3Copy(r2, `s3://${r2.bucket}/${latest}`, tmp); - const toc = await run(['pg_restore', '-l', tmp]); + const toc = await run(["pg_restore", "-l", tmp]); const tocEntries = toc - .split('\n') + .split("\n") .filter((l) => /TABLE DATA|MATERIALIZED VIEW|INDEX|SEQUENCE/.test(l)).length; console.log(`[restore-drill] archive TOC entries: ${tocEntries}`); - const adminUrl = withDatabase(env.DATABASE_URL, 'postgres'); + const adminUrl = withDatabase(env.DATABASE_URL, "postgres"); const scratchUrl = withDatabase(env.DATABASE_URL, SCRATCH_DB); await run([ - 'psql', + "psql", adminUrl, - '-v', - 'ON_ERROR_STOP=1', - '-c', + "-v", + "ON_ERROR_STOP=1", + "-c", `DROP DATABASE IF EXISTS ${SCRATCH_DB};`, ]); - await run(['psql', adminUrl, '-v', 'ON_ERROR_STOP=1', '-c', `CREATE DATABASE ${SCRATCH_DB};`]); + await run(["psql", adminUrl, "-v", "ON_ERROR_STOP=1", "-c", `CREATE DATABASE ${SCRATCH_DB};`]); try { - await run(['pg_restore', '--no-owner', '--no-privileges', '-d', scratchUrl, tmp]); + await run(["pg_restore", "--no-owner", "--no-privileges", "-d", scratchUrl, tmp]); const count = Number.parseInt( - (await run(['psql', scratchUrl, '-tAc', 'SELECT count(*) FROM prices;'])).trim(), + (await run(["psql", scratchUrl, "-tAc", "SELECT count(*) FROM prices;"])).trim(), 10, ); console.log(`[restore-drill] restored prices rows: ${count}`); if (!(count > 0)) { - throw new Error('[restore-drill] restored database has 0 prices'); + throw new Error("[restore-drill] restored database has 0 prices"); } } finally { await run([ - 'psql', + "psql", adminUrl, - '-v', - 'ON_ERROR_STOP=1', - '-c', + "-v", + "ON_ERROR_STOP=1", + "-c", `DROP DATABASE IF EXISTS ${SCRATCH_DB};`, ]).catch(() => {}); await unlink(tmp).catch(() => {}); } - console.log('[restore-drill] OK'); + console.log("[restore-drill] OK"); } diff --git a/src/classifier/segment-classifier.ts b/src/classifier/segment-classifier.ts index 231e0a8..1b55d0c 100644 --- a/src/classifier/segment-classifier.ts +++ b/src/classifier/segment-classifier.ts @@ -1,11 +1,11 @@ -import Anthropic from '@anthropic-ai/sdk'; -import { env } from '../config.js'; -import { SEGMENTS, type Segment } from '../db/schema.js'; +import Anthropic from "@anthropic-ai/sdk"; +import { env } from "../config.js"; +import { SEGMENTS, type Segment } from "../db/schema.js"; const SYSTEM_PROMPT = `You are a Brazilian vehicle classification expert. Your task is to classify car models into segments based on their brand and model name. Available segments (use EXACTLY these values): -${SEGMENTS.map((s) => `- ${s}`).join('\n')} +${SEGMENTS.map((s) => `- ${s}`).join("\n")} Rules: - Respond with ONLY the segment name, nothing else @@ -27,7 +27,7 @@ type ClassificationResult = { export async function classifyModels(models: ModelInput[]): Promise { if (!env.ANTHROPIC_API_KEY) { - console.warn('ANTHROPIC_API_KEY not set, skipping classification'); + console.warn("ANTHROPIC_API_KEY not set, skipping classification"); return models.map((m) => ({ id: m.id, segment: null })); } @@ -42,32 +42,32 @@ export async function classifyModels(models: ModelInput[]): Promise `${idx + 1}. Brand: ${m.brandName}, Model: ${m.modelName}`) - .join('\n'); + .join("\n"); try { const response = await client.messages.create({ - model: 'claude-sonnet-4-5', + model: "claude-sonnet-4-5", max_tokens: 1024, system: SYSTEM_PROMPT, messages: [ { - role: 'user', + role: "user", content: `Classify each vehicle. Respond with one segment per line, numbered to match:\n\n${prompt}`, }, ], }); const content = response.content[0]; - if (content.type !== 'text') { - throw new Error('Unexpected response type'); + if (content.type !== "text") { + throw new Error("Unexpected response type"); } - const lines = content.text.trim().split('\n'); + const lines = content.text.trim().split("\n"); for (let j = 0; j < batch.length; j++) { - const line = lines[j] || ''; + const line = lines[j] || ""; // Extract segment from response (handle numbered format like "1. SUV") - const segmentMatch = line.replace(/^\d+\.\s*/, '').trim(); + const segmentMatch = line.replace(/^\d+\.\s*/, "").trim(); const segment = SEGMENTS.find((s) => s.toLowerCase() === segmentMatch.toLowerCase()); results.push({ @@ -103,7 +103,7 @@ export async function classifySingleModel( modelName: string, ): Promise { if (!env.ANTHROPIC_API_KEY) { - console.warn('ANTHROPIC_API_KEY not set, skipping classification'); + console.warn("ANTHROPIC_API_KEY not set, skipping classification"); return null; } @@ -111,19 +111,19 @@ export async function classifySingleModel( try { const response = await client.messages.create({ - model: 'claude-sonnet-4-5', + model: "claude-sonnet-4-5", max_tokens: 50, system: SYSTEM_PROMPT, messages: [ { - role: 'user', + role: "user", content: `Classify: Brand: ${brandName}, Model: ${modelName}`, }, ], }); const content = response.content[0]; - if (content.type !== 'text') { + if (content.type !== "text") { return null; } diff --git a/src/config.ts b/src/config.ts index 58cc624..f3b1faa 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import { z } from 'zod'; +import { z } from "zod"; const envSchema = z.object({ DATABASE_URL: z.string().min(1), @@ -18,7 +18,7 @@ const envSchema = z.object({ const parsed = envSchema.safeParse(process.env); if (!parsed.success) { - console.error('Invalid environment variables:', parsed.error.flatten()); + console.error("Invalid environment variables:", parsed.error.flatten()); process.exit(1); } diff --git a/src/crawler/processor.ts b/src/crawler/processor.ts index 805a7b9..0aff9e0 100644 --- a/src/crawler/processor.ts +++ b/src/crawler/processor.ts @@ -1,14 +1,14 @@ -import cliProgress from 'cli-progress'; -import { classifySingleModel } from '../classifier/segment-classifier.js'; -import * as repo from '../db/repository.js'; -import { fipeClient } from '../fipe/client.js'; -import { parseReferenceMonth } from './reference.js'; +import cliProgress from "cli-progress"; +import { classifySingleModel } from "../classifier/segment-classifier.js"; +import * as repo from "../db/repository.js"; +import { fipeClient } from "../fipe/client.js"; +import { parseReferenceMonth } from "./reference.js"; -export { parseReferenceMonth } from './reference.js'; +export { parseReferenceMonth } from "./reference.js"; function parseYearValue(value: string): { year: number; fuelCode: number } { // Format: "2020-1" (year-fuelCode) - const [yearStr, fuelCodeStr] = value.split('-'); + const [yearStr, fuelCodeStr] = value.split("-"); return { year: Number.parseInt(yearStr, 10), fuelCode: Number.parseInt(fuelCodeStr, 10), @@ -17,7 +17,7 @@ function parseYearValue(value: string): { year: number; fuelCode: number } { function parsePrice(valor: string): string { // "R$ 4.147,00" -> "4147.00" - return valor.replace('R$ ', '').replace(/\./g, '').replace(',', '.'); + return valor.replace("R$ ", "").replace(/\./g, "").replace(",", "."); } interface CrawlOptions { @@ -35,7 +35,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { const log = options.onProgress ?? console.log; // Get reference tables from API - log('Fetching reference tables...'); + log("Fetching reference tables..."); const allRefs = await fipeClient.getReferenceTables(); // Filter to specific reference, year/month, or default to current year @@ -52,7 +52,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { }); if (refs.length === 0) { - log('No reference tables found to process'); + log("No reference tables found to process"); return; } @@ -69,12 +69,12 @@ export async function crawl(options: CrawlOptions = {}): Promise { // Clear crawl status if --force if (options.force) { - log(' Force mode: clearing crawl status...'); + log(" Force mode: clearing crawl status..."); await repo.clearCrawlStatus(refRecord.id); } // Phase 1: Crawl brands (always fetch - cheap API call) - log(' Phase 1: Crawling brands...'); + log(" Phase 1: Crawling brands..."); const apiBrands = await fipeClient.getBrands(ref.Codigo); const filteredBrands = options.brandCodes ? apiBrands.filter((b) => options.brandCodes?.includes(b.Value)) @@ -110,7 +110,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { if (isNew && options.classify) { const segment = await classifySingleModel(brand.name, m.Label); if (segment) { - await repo.updateModelSegment(modelRecord.id, segment, 'ai'); + await repo.updateModelSegment(modelRecord.id, segment, "ai"); log(` Classified ${m.Label} as ${segment}`); } } @@ -126,7 +126,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { } } } else { - log(' Phase 2: Models already crawled'); + log(" Phase 2: Models already crawled"); } // Phase 3: Crawl model-years for each uncrawled model @@ -135,12 +135,12 @@ export async function crawl(options: CrawlOptions = {}): Promise { log(` Phase 3: Crawling years for ${uncrawledModels.length} models...`); const bar = new cliProgress.SingleBar({ - format: ' [{bar}] {value}/{total} | {model}', - barCompleteChar: '█', - barIncompleteChar: '░', + format: " [{bar}] {value}/{total} | {model}", + barCompleteChar: "█", + barIncompleteChar: "░", hideCursor: true, }); - bar.start(uncrawledModels.length, 0, { model: '' }); + bar.start(uncrawledModels.length, 0, { model: "" }); for (const model of uncrawledModels) { bar.update({ model: model.name.slice(0, 30) }); @@ -173,7 +173,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { bar.stop(); } else { - log(' Phase 3: Model-years already crawled'); + log(" Phase 3: Model-years already crawled"); } // Phase 4: Fetch prices for each uncrawled model-year @@ -182,9 +182,9 @@ export async function crawl(options: CrawlOptions = {}): Promise { log(` Phase 4: Fetching ${uncrawledModelYears.length} prices...`); const bar = new cliProgress.SingleBar({ - format: ' [{bar}] {value}/{total} prices', - barCompleteChar: '█', - barIncompleteChar: '░', + format: " [{bar}] {value}/{total} prices", + barCompleteChar: "█", + barIncompleteChar: "░", hideCursor: true, }); bar.start(uncrawledModelYears.length, 0); @@ -220,7 +220,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { bar.stop(); log(` Fetched ${refPrices} prices`); } else { - log(' Phase 4: All prices already crawled'); + log(" Phase 4: All prices already crawled"); } const backlog = await repo.getCrawlBacklog(refRecord.id); @@ -242,7 +242,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { } if (totalPrices > 0) { - log('\nRefreshing latest prices view...'); + log("\nRefreshing latest prices view..."); await repo.refreshLatestPrices(); } @@ -252,7 +252,7 @@ export async function crawl(options: CrawlOptions = {}): Promise { export async function status(): Promise { const stats = await repo.getStats(); - console.log('\nDatabase status:'); + console.log("\nDatabase status:"); console.log(` References: ${stats.references}`); console.log(` Brands: ${stats.brands}`); console.log(` Models: ${stats.models}`); diff --git a/src/crawler/reference.test.ts b/src/crawler/reference.test.ts index 92a4990..82b6124 100644 --- a/src/crawler/reference.test.ts +++ b/src/crawler/reference.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, test } from 'bun:test'; -import { parseReferenceMonth } from './reference.js'; +import { describe, expect, test } from "vite-plus/test"; +import { parseReferenceMonth } from "./reference.js"; -describe('parseReferenceMonth', () => { - test('parses FIPE reference month labels', () => { - expect(parseReferenceMonth('dezembro/2025 ')).toEqual({ month: 12, year: 2025 }); - expect(parseReferenceMonth('Março/2024')).toEqual({ month: 3, year: 2024 }); +describe("parseReferenceMonth", () => { + test("parses FIPE reference month labels", () => { + expect(parseReferenceMonth("dezembro/2025 ")).toEqual({ month: 12, year: 2025 }); + expect(parseReferenceMonth("Março/2024")).toEqual({ month: 3, year: 2024 }); }); }); diff --git a/src/crawler/reference.ts b/src/crawler/reference.ts index d86a399..d0b3cab 100644 --- a/src/crawler/reference.ts +++ b/src/crawler/reference.ts @@ -19,7 +19,7 @@ export function parseReferenceMonth(mes: string): ReferenceMonth { dezembro: 12, }; - const [monthName, yearStr] = mes.trim().toLowerCase().split('/'); + const [monthName, yearStr] = mes.trim().toLowerCase().split("/"); return { month: months[monthName] || 0, year: Number.parseInt(yearStr, 10), diff --git a/src/crawler/refresh.test.ts b/src/crawler/refresh.test.ts index 5febfb4..b98884c 100644 --- a/src/crawler/refresh.test.ts +++ b/src/crawler/refresh.test.ts @@ -1,12 +1,12 @@ -import { describe, expect, test } from 'bun:test'; -import type { ReferenceTable } from '../fipe/types.js'; +import { describe, expect, test } from "vite-plus/test"; +import type { ReferenceTable } from "../fipe/types.js"; import { type PublicationSideEffectRepository, completePendingPublicationSideEffects, getMinimumPriceCount, selectTargetReferences, validateRefreshCandidate, -} from './refresh.js'; +} from "./refresh.js"; const emptyBacklog = { uncrawledBrands: 0, @@ -14,14 +14,14 @@ const emptyBacklog = { uncrawledModelYears: 0, }; -describe('selectTargetReferences', () => { - test('selects official references newer than the latest published month oldest-to-newest', () => { +describe("selectTargetReferences", () => { + test("selects official references newer than the latest published month oldest-to-newest", () => { const officialReferences: ReferenceTable[] = [ - { Codigo: 304, Mes: 'abril/2026 ' }, - { Codigo: 302, Mes: 'fevereiro/2026 ' }, - { Codigo: 301, Mes: 'janeiro/2026 ' }, - { Codigo: 205, Mes: 'dezembro/2025 ' }, - { Codigo: 303, Mes: 'março/2026 ' }, + { Codigo: 304, Mes: "abril/2026 " }, + { Codigo: 302, Mes: "fevereiro/2026 " }, + { Codigo: 301, Mes: "janeiro/2026 " }, + { Codigo: 205, Mes: "dezembro/2025 " }, + { Codigo: 303, Mes: "março/2026 " }, ]; expect( @@ -31,19 +31,19 @@ describe('selectTargetReferences', () => { ).toEqual([301, 302, 303, 304]); }); - test('returns nothing when no official reference is newer', () => { + test("returns nothing when no official reference is newer", () => { const officialReferences: ReferenceTable[] = [ - { Codigo: 205, Mes: 'dezembro/2025 ' }, - { Codigo: 204, Mes: 'novembro/2025 ' }, + { Codigo: 205, Mes: "dezembro/2025 " }, + { Codigo: 204, Mes: "novembro/2025 " }, ]; expect(selectTargetReferences(officialReferences, { month: 12, year: 2025 })).toEqual([]); }); - test('selects every official reference when nothing has been published', () => { + test("selects every official reference when nothing has been published", () => { const officialReferences: ReferenceTable[] = [ - { Codigo: 302, Mes: 'fevereiro/2026 ' }, - { Codigo: 301, Mes: 'janeiro/2026 ' }, + { Codigo: 302, Mes: "fevereiro/2026 " }, + { Codigo: 301, Mes: "janeiro/2026 " }, ]; expect(selectTargetReferences(officialReferences).map((reference) => reference.code)).toEqual([ @@ -52,8 +52,8 @@ describe('selectTargetReferences', () => { }); }); -describe('validateRefreshCandidate', () => { - test('passes with zero backlog and at least 90 percent of previous price count', () => { +describe("validateRefreshCandidate", () => { + test("passes with zero backlog and at least 90 percent of previous price count", () => { expect(getMinimumPriceCount(101)).toBe(91); expect( validateRefreshCandidate({ @@ -64,7 +64,7 @@ describe('validateRefreshCandidate', () => { ).toBe(true); }); - test('fails below the 90 percent price floor', () => { + test("fails below the 90 percent price floor", () => { const decision = validateRefreshCandidate({ backlog: emptyBacklog, priceCount: 899, @@ -76,7 +76,7 @@ describe('validateRefreshCandidate', () => { expect(decision.minimumPriceCount).toBe(900); }); - test('fails when checkpoint backlog remains', () => { + test("fails when checkpoint backlog remains", () => { const decision = validateRefreshCandidate({ backlog: { ...emptyBacklog, uncrawledModels: 1 }, priceCount: 1000, @@ -87,7 +87,7 @@ describe('validateRefreshCandidate', () => { expect(decision.backlogTotal).toBe(1); }); - test('skips the price floor when there is no previous published reference', () => { + test("skips the price floor when there is no previous published reference", () => { const decision = validateRefreshCandidate({ backlog: emptyBacklog, priceCount: 0, @@ -105,32 +105,32 @@ class FakePublicationSideEffectRepository implements PublicationSideEffectReposi calls: string[] = []; async getPublishedReferencesPendingLatestPricesRefreshCount(): Promise { - this.calls.push('count-latest-prices-refresh'); + this.calls.push("count-latest-prices-refresh"); return this.pendingLatestPricesRefreshCount; } async refreshLatestPrices(): Promise { - this.calls.push('refresh-latest-prices'); + this.calls.push("refresh-latest-prices"); } async markPublishedReferencesLatestPricesRefreshed(): Promise { - this.calls.push('mark-latest-prices-refreshed'); + this.calls.push("mark-latest-prices-refreshed"); this.pendingLatestPricesRefreshCount = 0; } async getPublishedReferencesPendingBackupCount(): Promise { - this.calls.push('count-backup'); + this.calls.push("count-backup"); return this.pendingBackupCount; } async markPublishedReferencesBackedUp(): Promise { - this.calls.push('mark-backed-up'); + this.calls.push("mark-backed-up"); this.pendingBackupCount = 0; } } -describe('completePendingPublicationSideEffects', () => { - test('refreshes latest_prices before considering backup work', async () => { +describe("completePendingPublicationSideEffects", () => { + test("refreshes latest_prices before considering backup work", async () => { const repo = new FakePublicationSideEffectRepository(); repo.pendingLatestPricesRefreshCount = 1; repo.pendingBackupCount = 1; @@ -139,29 +139,29 @@ describe('completePendingPublicationSideEffects', () => { repo, true, async () => { - repo.calls.push('run-backup'); + repo.calls.push("run-backup"); }, () => {}, ); expect(repo.calls).toEqual([ - 'count-latest-prices-refresh', - 'refresh-latest-prices', - 'mark-latest-prices-refreshed', - 'count-backup', - 'run-backup', - 'mark-backed-up', + "count-latest-prices-refresh", + "refresh-latest-prices", + "mark-latest-prices-refreshed", + "count-backup", + "run-backup", + "mark-backed-up", ]); expect(repo.pendingLatestPricesRefreshCount).toBe(0); expect(repo.pendingBackupCount).toBe(0); }); - test('does not mark latest_prices refresh complete when refresh fails', async () => { + test("does not mark latest_prices refresh complete when refresh fails", async () => { const repo = new FakePublicationSideEffectRepository(); repo.pendingLatestPricesRefreshCount = 1; repo.refreshLatestPrices = async () => { - repo.calls.push('refresh-latest-prices'); - throw new Error('refresh unavailable'); + repo.calls.push("refresh-latest-prices"); + throw new Error("refresh unavailable"); }; await expect( @@ -171,13 +171,13 @@ describe('completePendingPublicationSideEffects', () => { async () => {}, () => {}, ), - ).rejects.toThrow('refresh unavailable'); + ).rejects.toThrow("refresh unavailable"); - expect(repo.calls).toEqual(['count-latest-prices-refresh', 'refresh-latest-prices']); + expect(repo.calls).toEqual(["count-latest-prices-refresh", "refresh-latest-prices"]); expect(repo.pendingLatestPricesRefreshCount).toBe(1); }); - test('does not mark backup complete when backup fails, so it can be retried', async () => { + test("does not mark backup complete when backup fails, so it can be retried", async () => { const repo = new FakePublicationSideEffectRepository(); repo.pendingBackupCount = 1; @@ -186,30 +186,30 @@ describe('completePendingPublicationSideEffects', () => { repo, true, async () => { - repo.calls.push('run-backup'); - throw new Error('backup unavailable'); + repo.calls.push("run-backup"); + throw new Error("backup unavailable"); }, () => {}, ), - ).rejects.toThrow('backup unavailable'); + ).rejects.toThrow("backup unavailable"); - expect(repo.calls).toEqual(['count-latest-prices-refresh', 'count-backup', 'run-backup']); + expect(repo.calls).toEqual(["count-latest-prices-refresh", "count-backup", "run-backup"]); expect(repo.pendingBackupCount).toBe(1); await completePendingPublicationSideEffects( repo, true, async () => { - repo.calls.push('run-backup-retry'); + repo.calls.push("run-backup-retry"); }, () => {}, ); expect(repo.pendingBackupCount).toBe(0); - expect(repo.calls).toContain('mark-backed-up'); + expect(repo.calls).toContain("mark-backed-up"); }); - test('leaves pending backup untouched when backup was not requested', async () => { + test("leaves pending backup untouched when backup was not requested", async () => { const repo = new FakePublicationSideEffectRepository(); repo.pendingBackupCount = 1; @@ -220,7 +220,7 @@ describe('completePendingPublicationSideEffects', () => { () => {}, ); - expect(repo.calls).toEqual(['count-latest-prices-refresh']); + expect(repo.calls).toEqual(["count-latest-prices-refresh"]); expect(repo.pendingBackupCount).toBe(1); }); }); diff --git a/src/crawler/refresh.ts b/src/crawler/refresh.ts index de48f04..8c0c77b 100644 --- a/src/crawler/refresh.ts +++ b/src/crawler/refresh.ts @@ -1,8 +1,8 @@ -import type { CrawlBacklog } from '../db/repository.js'; -import type { ReferenceTable } from '../fipe/types.js'; -import { parseReferenceMonth } from './reference.js'; +import type { CrawlBacklog } from "../db/repository.js"; +import type { ReferenceTable } from "../fipe/types.js"; +import { parseReferenceMonth } from "./reference.js"; -const REFRESH_LOCK_KEY = 'fipe_refresh'; +const REFRESH_LOCK_KEY = "fipe_refresh"; export interface ReferenceCursor { month: number; @@ -111,14 +111,14 @@ function errorMessage(error: unknown): string { async function pingHealthcheck( healthcheckUrl: string | undefined, - event: 'start' | 'success' | 'fail', + event: "start" | "success" | "fail", ): Promise { if (!healthcheckUrl) return; const url = - event === 'start' + event === "start" ? `${healthcheckUrl}/start` - : event === 'fail' + : event === "fail" ? `${healthcheckUrl}/fail` : healthcheckUrl; @@ -130,7 +130,7 @@ async function pingHealthcheck( } async function acquireRefreshLock(): Promise { - const { postgresClient } = await import('../db/connection.js'); + const { postgresClient } = await import("../db/connection.js"); const reservedSql = await postgresClient.reserve(); try { @@ -140,7 +140,7 @@ async function acquireRefreshLock(): Promise { `; if (lock?.acquired !== true) { - await reservedSql.release(); + reservedSql.release(); return undefined; } @@ -151,12 +151,12 @@ async function acquireRefreshLock(): Promise { SELECT pg_advisory_unlock(hashtext(${REFRESH_LOCK_KEY})) `; } finally { - await reservedSql.release(); + reservedSql.release(); } }, }; } catch (error) { - await reservedSql.release(); + reservedSql.release(); throw error; } } @@ -224,37 +224,37 @@ export async function runRefresh(options: RefreshOptions = {}): Promise { - const backupModule = await import('../backup.js'); + const backupModule = await import("../backup.js"); await backupModule.runBackup(); }; await completePendingPublicationSideEffects(repo, options.backup === true, runBackup, log); if (targets.length === 0) { - log('[refresh] nothing new'); + log("[refresh] nothing new"); exitCode = 0; return exitCode; } @@ -331,7 +331,7 @@ export async function runRefresh(options: RefreshOptions = {}): Promise [index('idx_reference_year_month').on(table.year, table.month)], + (table) => [index("idx_reference_year_month").on(table.year, table.month)], ); export const brands = pgTable( - 'brands', + "brands", { - id: serial('id').primaryKey(), - fipeCode: varchar('fipe_code', { length: 10 }).unique().notNull(), - name: varchar('name', { length: 100 }).notNull(), + id: serial("id").primaryKey(), + fipeCode: varchar("fipe_code", { length: 10 }).unique().notNull(), + name: varchar("name", { length: 100 }).notNull(), }, - (table) => [index('idx_brands_name').on(table.name)], + (table) => [index("idx_brands_name").on(table.name)], ); export const SEGMENTS = [ - 'Buggy', - 'Caminhão Leve', - 'Conversível', - 'Coupé', - 'Hatch', - 'Perua', - 'Pick-up', - 'Sedã', - 'SUV', - 'Van/Utilitário', + "Buggy", + "Caminhão Leve", + "Conversível", + "Coupé", + "Hatch", + "Perua", + "Pick-up", + "Sedã", + "SUV", + "Van/Utilitário", ] as const; export type Segment = (typeof SEGMENTS)[number]; export const models = pgTable( - 'models', + "models", { - id: serial('id').primaryKey(), - brandId: integer('brand_id') + id: serial("id").primaryKey(), + brandId: integer("brand_id") .references(() => brands.id) .notNull(), - fipeCode: varchar('fipe_code', { length: 20 }).notNull(), - name: varchar('name', { length: 200 }).notNull(), - segment: varchar('segment', { length: 20 }), - segmentSource: varchar('segment_source', { length: 10 }), + fipeCode: varchar("fipe_code", { length: 20 }).notNull(), + name: varchar("name", { length: 200 }).notNull(), + segment: varchar("segment", { length: 20 }), + segmentSource: varchar("segment_source", { length: 10 }), }, (table) => [ unique().on(table.brandId, table.fipeCode), - index('idx_models_brand_id').on(table.brandId), - index('idx_models_segment').on(table.segment), + index("idx_models_brand_id").on(table.brandId), + index("idx_models_segment").on(table.segment), ], ); export const modelYears = pgTable( - 'model_years', + "model_years", { - id: serial('id').primaryKey(), - modelId: integer('model_id') + id: serial("id").primaryKey(), + modelId: integer("model_id") .references(() => models.id) .notNull(), - year: integer('year').notNull(), - fuelCode: integer('fuel_code').notNull(), - fuelName: varchar('fuel_name', { length: 50 }), + year: integer("year").notNull(), + fuelCode: integer("fuel_code").notNull(), + fuelName: varchar("fuel_name", { length: 50 }), }, (table) => [ unique().on(table.modelId, table.year, table.fuelCode), - index('idx_model_years_model_id').on(table.modelId), - index('idx_model_years_year').on(table.year), + index("idx_model_years_model_id").on(table.modelId), + index("idx_model_years_year").on(table.year), ], ); export const prices = pgTable( - 'prices', + "prices", { - id: serial('id').primaryKey(), - modelYearId: integer('model_year_id') + id: serial("id").primaryKey(), + modelYearId: integer("model_year_id") .references(() => modelYears.id) .notNull(), - referenceTableId: integer('reference_table_id') + referenceTableId: integer("reference_table_id") .references(() => referenceTables.id) .notNull(), - fipeCode: varchar('fipe_code', { length: 20 }).notNull(), - priceBrl: decimal('price_brl', { precision: 12, scale: 2 }).notNull(), - crawledAt: timestamp('crawled_at').defaultNow(), + fipeCode: varchar("fipe_code", { length: 20 }).notNull(), + priceBrl: decimal("price_brl", { precision: 12, scale: 2 }).notNull(), + crawledAt: timestamp("crawled_at").defaultNow(), }, (table) => [ unique().on(table.modelYearId, table.referenceTableId), - index('idx_prices_reference').on(table.referenceTableId), - index('idx_prices_fipe_code').on(table.fipeCode), - index('idx_prices_model_year_id').on(table.modelYearId), + index("idx_prices_reference").on(table.referenceTableId), + index("idx_prices_fipe_code").on(table.fipeCode), + index("idx_prices_model_year_id").on(table.modelYearId), ], ); // Crawl status tracking tables (per reference) export const referenceBrands = pgTable( - 'reference_brands', + "reference_brands", { - id: serial('id').primaryKey(), - referenceTableId: integer('reference_table_id') + id: serial("id").primaryKey(), + referenceTableId: integer("reference_table_id") .references(() => referenceTables.id) .notNull(), - brandId: integer('brand_id') + brandId: integer("brand_id") .references(() => brands.id) .notNull(), - modelsCrawledAt: timestamp('models_crawled_at'), + modelsCrawledAt: timestamp("models_crawled_at"), }, (table) => [ unique().on(table.referenceTableId, table.brandId), - index('idx_reference_brands_ref').on(table.referenceTableId), - index('idx_reference_brands_brand').on(table.brandId), + index("idx_reference_brands_ref").on(table.referenceTableId), + index("idx_reference_brands_brand").on(table.brandId), ], ); export const referenceModels = pgTable( - 'reference_models', + "reference_models", { - id: serial('id').primaryKey(), - referenceTableId: integer('reference_table_id') + id: serial("id").primaryKey(), + referenceTableId: integer("reference_table_id") .references(() => referenceTables.id) .notNull(), - modelId: integer('model_id') + modelId: integer("model_id") .references(() => models.id) .notNull(), - yearsCrawledAt: timestamp('years_crawled_at'), + yearsCrawledAt: timestamp("years_crawled_at"), }, (table) => [ unique().on(table.referenceTableId, table.modelId), - index('idx_reference_models_ref').on(table.referenceTableId), - index('idx_reference_models_model').on(table.modelId), + index("idx_reference_models_ref").on(table.referenceTableId), + index("idx_reference_models_model").on(table.modelId), ], ); export const referenceModelYears = pgTable( - 'reference_model_years', + "reference_model_years", { - id: serial('id').primaryKey(), - referenceTableId: integer('reference_table_id') + id: serial("id").primaryKey(), + referenceTableId: integer("reference_table_id") .references(() => referenceTables.id) .notNull(), - modelYearId: integer('model_year_id') + modelYearId: integer("model_year_id") .references(() => modelYears.id) .notNull(), - priceCrawledAt: timestamp('price_crawled_at'), + priceCrawledAt: timestamp("price_crawled_at"), }, (table) => [ unique().on(table.referenceTableId, table.modelYearId), - index('idx_reference_model_years_ref').on(table.referenceTableId), - index('idx_reference_model_years_my').on(table.modelYearId), + index("idx_reference_model_years_ref").on(table.referenceTableId), + index("idx_reference_model_years_my").on(table.modelYearId), ], ); diff --git a/src/fipe/client.ts b/src/fipe/client.ts index a1f7b4f..c7324ed 100644 --- a/src/fipe/client.ts +++ b/src/fipe/client.ts @@ -1,4 +1,4 @@ -import { env } from '../config.js'; +import { env } from "../config.js"; import { brandsSchema, fipeErrorSchema, @@ -6,10 +6,10 @@ import { priceSchema, referenceTablesSchema, yearsSchema, -} from './schemas.js'; -import type { Brand, ModelsResponse, Price, PriceParams, ReferenceTable, Year } from './types.js'; +} from "./schemas.js"; +import type { Brand, ModelsResponse, Price, PriceParams, ReferenceTable, Year } from "./types.js"; -const BASE_URL = 'https://veiculos.fipe.org.br/api/veiculos'; +const BASE_URL = "https://veiculos.fipe.org.br/api/veiculos"; const VEHICLE_TYPE_CAR = 1; async function sleep(ms: number): Promise { @@ -74,9 +74,9 @@ export class FipeClient { let response: Response; try { response = await fetch(`${BASE_URL}/${endpoint}`, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify(body), ...(env.FIPE_PROXY ? { proxy: env.FIPE_PROXY } : {}), @@ -85,7 +85,7 @@ export class FipeClient { if (retries > 0) { const waitTime = this.calculateBackoff(attempt); console.log( - `Network error (${error instanceof Error ? error.message : error}), waiting ${waitTime}ms before retry (${retries} retries left)`, + `Network error (${error instanceof Error ? error.message : String(error)}), waiting ${waitTime}ms before retry (${retries} retries left)`, ); await sleep(waitTime); return this.request(endpoint, body, retries - 1, attempt + 1); @@ -98,7 +98,7 @@ export class FipeClient { this.increaseThrottle(); // Check for Retry-After header - const retryAfter = response.headers.get('Retry-After'); + const retryAfter = response.headers.get("Retry-After"); const waitTime = retryAfter ? Number.parseInt(retryAfter, 10) * 1000 : this.calculateBackoff(attempt, true); @@ -136,12 +136,12 @@ export class FipeClient { } async getReferenceTables(): Promise { - const data = await this.request('ConsultarTabelaDeReferencia', {}); + const data = await this.request("ConsultarTabelaDeReferencia", {}); return referenceTablesSchema.parse(data); } async getBrands(referenceCode: number): Promise { - const data = await this.request('ConsultarMarcas', { + const data = await this.request("ConsultarMarcas", { codigoTipoVeiculo: VEHICLE_TYPE_CAR, codigoTabelaReferencia: referenceCode, }); @@ -149,7 +149,7 @@ export class FipeClient { } async getModels(referenceCode: number, brandCode: string): Promise { - const data = await this.request('ConsultarModelos', { + const data = await this.request("ConsultarModelos", { codigoTipoVeiculo: VEHICLE_TYPE_CAR, codigoTabelaReferencia: referenceCode, codigoMarca: brandCode, @@ -158,7 +158,7 @@ export class FipeClient { } async getYears(referenceCode: number, brandCode: string, modelCode: string): Promise { - const data = await this.request('ConsultarAnoModelo', { + const data = await this.request("ConsultarAnoModelo", { codigoTipoVeiculo: VEHICLE_TYPE_CAR, codigoTabelaReferencia: referenceCode, codigoMarca: brandCode, @@ -168,14 +168,14 @@ export class FipeClient { } async getPrice(params: PriceParams): Promise { - const data = await this.request('ConsultarValorComTodosParametros', { + const data = await this.request("ConsultarValorComTodosParametros", { codigoTipoVeiculo: VEHICLE_TYPE_CAR, codigoTabelaReferencia: params.referenceCode, codigoMarca: params.brandCode, codigoModelo: params.modelCode, anoModelo: params.year, codigoTipoCombustivel: params.fuelCode, - tipoConsulta: 'tradicional', + tipoConsulta: "tradicional", }); return priceSchema.parse(data); } diff --git a/src/fipe/schemas.ts b/src/fipe/schemas.ts index f4bc98f..42aa36f 100644 --- a/src/fipe/schemas.ts +++ b/src/fipe/schemas.ts @@ -1,4 +1,4 @@ -import { z } from 'zod'; +import { z } from "zod"; export const referenceTableSchema = z.object({ Codigo: z.number(), diff --git a/src/fipe/types.ts b/src/fipe/types.ts index c7bd200..72d0e00 100644 --- a/src/fipe/types.ts +++ b/src/fipe/types.ts @@ -1,4 +1,4 @@ -import type { z } from 'zod'; +import type { z } from "zod"; import type { brandSchema, fipeErrorSchema, @@ -7,7 +7,7 @@ import type { priceSchema, referenceTableSchema, yearSchema, -} from './schemas.js'; +} from "./schemas.js"; export type ReferenceTable = z.infer; export type Brand = z.infer; diff --git a/src/index.ts b/src/index.ts index f30c16e..614f927 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ #!/usr/bin/env node -import { Command } from 'commander'; -import { runBackup, runRestoreDrill } from './backup.js'; -import { classifyModels } from './classifier/segment-classifier.js'; -import { crawl, status } from './crawler/processor.js'; -import { runRefresh } from './crawler/refresh.js'; -import { closeConnection } from './db/connection.js'; -import { getModelsWithoutSegment, updateModelSegment } from './db/repository.js'; +import { Command } from "commander"; +import { runBackup, runRestoreDrill } from "./backup.js"; +import { classifyModels } from "./classifier/segment-classifier.js"; +import { crawl, status } from "./crawler/processor.js"; +import { runRefresh } from "./crawler/refresh.js"; +import { closeConnection } from "./db/connection.js"; +import { getModelsWithoutSegment, updateModelSegment } from "./db/repository.js"; const program = new Command(); @@ -15,9 +15,9 @@ const program = new Command(); */ function parseNumberList(value: string): number[] { const results: number[] = []; - for (const part of value.split(',')) { - if (part.includes('-')) { - const [start, end] = part.split('-').map((v) => Number.parseInt(v.trim(), 10)); + for (const part of value.split(",")) { + if (part.includes("-")) { + const [start, end] = part.split("-").map((v) => Number.parseInt(v.trim(), 10)); for (let i = start; i <= end; i++) { results.push(i); } @@ -32,7 +32,7 @@ function parseCommaSeparated(value: string): string[] { return [ ...new Set( value - .split(',') + .split(",") .map((v) => v.trim()) .filter(Boolean), ), @@ -44,15 +44,15 @@ function markCommandFailed(): void { } program - .command('crawl') - .description('Crawl FIPE data and store in database') - .option('-r, --reference ', 'Specific reference table code') - .option('-y, --year ', 'Year(s) to crawl (e.g., 2023, 2020-2023, or 2020,2022,2023)') - .option('-M, --month ', 'Month(s) to crawl (e.g., 6, 1-6, or 1,3,6)') - .option('-b, --brand ', 'Brand code(s), comma-separated') - .option('-m, --model ', 'Model code(s), comma-separated (requires --brand)') - .option('-c, --classify', 'Classify new models by segment using AI') - .option('-f, --force', 'Re-fetch all data, ignoring sync status') + .command("crawl") + .description("Crawl FIPE data and store in database") + .option("-r, --reference ", "Specific reference table code") + .option("-y, --year ", "Year(s) to crawl (e.g., 2023, 2020-2023, or 2020,2022,2023)") + .option("-M, --month ", "Month(s) to crawl (e.g., 6, 1-6, or 1,3,6)") + .option("-b, --brand ", "Brand code(s), comma-separated") + .option("-m, --model ", "Model code(s), comma-separated (requires --brand)") + .option("-c, --classify", "Classify new models by segment using AI") + .option("-f, --force", "Re-fetch all data, ignoring sync status") .action(async (options) => { try { await crawl({ @@ -65,41 +65,41 @@ program force: options.force, }); } catch (err) { - console.error('Crawl failed:', err); + console.error("Crawl failed:", err); markCommandFailed(); } }); program - .command('status') - .description('Show database statistics') + .command("status") + .description("Show database statistics") .action(async () => { try { await status(); } catch (err) { - console.error('Status failed:', err); + console.error("Status failed:", err); markCommandFailed(); } }); program - .command('classify') - .description('Classify models by segment using AI') - .option('-n, --dry-run', 'Show what would be classified without making changes') + .command("classify") + .description("Classify models by segment using AI") + .option("-n, --dry-run", "Show what would be classified without making changes") .action(async (options) => { try { // Batch classification const modelsToClassify = await getModelsWithoutSegment(); if (modelsToClassify.length === 0) { - console.log('All models are already classified.'); + console.log("All models are already classified."); return; } console.log(`Found ${modelsToClassify.length} models without segment.`); if (options.dryRun) { - console.log('\nDry run - would classify:'); + console.log("\nDry run - would classify:"); for (const model of modelsToClassify.slice(0, 20)) { console.log(` - ${model.brandName} ${model.modelName}`); } @@ -109,7 +109,7 @@ program return; } - console.log('\nClassifying models...'); + console.log("\nClassifying models..."); const results = await classifyModels(modelsToClassify); let classified = 0; @@ -117,7 +117,7 @@ program for (const result of results) { if (result.segment) { - await updateModelSegment(result.id, result.segment, 'ai'); + await updateModelSegment(result.id, result.segment, "ai"); classified++; } else { failed++; @@ -126,15 +126,15 @@ program console.log(`\nDone! Classified: ${classified}, Failed: ${failed}`); } catch (err) { - console.error('Classification failed:', err); + console.error("Classification failed:", err); markCommandFailed(); } }); program - .command('refresh') - .description('Crawl and publish new complete FIPE reference tables') - .option('--backup', 'Run backup after publishing at least one reference') + .command("refresh") + .description("Crawl and publish new complete FIPE reference tables") + .option("--backup", "Run backup after publishing at least one reference") .action(async (options: { backup?: boolean }) => { const exitCode = await runRefresh({ backup: options.backup === true }); if (exitCode !== 0) { @@ -143,25 +143,25 @@ program }); program - .command('backup') - .description('pg_dump the database and upload to R2 with retention') + .command("backup") + .description("pg_dump the database and upload to R2 with retention") .action(async () => { try { await runBackup(); } catch (err) { - console.error('Backup failed:', err); + console.error("Backup failed:", err); markCommandFailed(); } }); program - .command('restore-drill') - .description('Download the latest R2 backup and verify it restores into a scratch database') + .command("restore-drill") + .description("Download the latest R2 backup and verify it restores into a scratch database") .action(async () => { try { await runRestoreDrill(); } catch (err) { - console.error('Restore drill failed:', err); + console.error("Restore drill failed:", err); markCommandFailed(); } }); @@ -175,6 +175,6 @@ async function main() { } main().catch((err) => { - console.error('Command failed:', err); + console.error("Command failed:", err); markCommandFailed(); }); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..3038952 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + fmt: {}, + lint: { + jsPlugins: [{ name: "vite-plus", specifier: "vite-plus/oxlint-plugin" }], + rules: { "vite-plus/prefer-vite-plus-imports": "error" }, + options: { typeAware: true, typeCheck: true }, + }, + staged: { + "*": "vp check --fix", + }, +});