From 528620e9ae2f3ed26f3cba8fc15191d19c32df49 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Mon, 3 Nov 2025 20:36:22 -0800 Subject: [PATCH] Add postinstall optimizer with native binlink support and script skipping (#24283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR introduces a new postinstall optimization system that significantly reduces the need to run lifecycle scripts for certain packages by intelligently handling their requirements at install time. ## Key Features ### 1. Native Binlink Optimization When packages like `esbuild` ship platform-specific binaries as optional dependencies, we now: - Detect the native binlink pattern (enabled by default for `esbuild`) - Find the matching platform-specific dependency based on target CPU/OS - Link binaries directly from the platform-specific package (e.g., `@esbuild/darwin-arm64`) - Fall back gracefully if the platform-specific package isn't found **Result**: No postinstall scripts needed for esbuild and similar packages. ### 2. Lifecycle Script Skipping For packages like `sharp` that run heavy postinstall scripts: - Skip lifecycle scripts entirely (enabled by default for `sharp`) - Prevents downloading large binaries or compiling native code unnecessarily - Reduces install time and potential failures in restricted environments ## Configuration Both features can be configured via `package.json`: ```json { "nativeDependencies": ["esbuild", "my-custom-package"], "ignoreScripts": ["sharp", "another-package"] } ``` Set to empty arrays to disable defaults: ```json { "nativeDependencies": [], "ignoreScripts": [] } ``` Environment variable overrides: - `BUN_FEATURE_FLAG_DISABLE_NATIVE_DEPENDENCY_LINKER=1` - disable native binlink - `BUN_FEATURE_FLAG_DISABLE_IGNORE_SCRIPTS=1` - disable script ignoring ## Implementation Details ### Core Components - **`postinstall_optimizer.zig`**: New file containing the optimizer logic - `PostinstallOptimizer` enum with `native_binlink` and `ignore` variants - `List` type to track optimization strategies per package hash - Defaults for `esbuild` (native binlink) and `sharp` (ignore) - **`Bin.Linker` changes**: Extended to support separate target paths - `target_node_modules_path`: Where to find the actual binary - `target_package_name`: Name of the package containing the binary - Fallback logic when native binlink optimization fails ### Modified Components - **PackageInstaller.zig**: Checks optimizer before: - Enqueueing lifecycle scripts - Linking binaries (with platform-specific package resolution) - **isolated_install/Installer.zig**: Similar checks for isolated linker mode - `maybeReplaceNodeModulesPath()` resolves platform-specific packages - Retry logic without optimization on failure - **Lockfile**: Added `postinstall_optimizer` field to persist configuration ## Changes Included - Updated `esbuild` from 0.21.5 to 0.25.11 (testing with latest) - VS Code launch config updates for debugging install with new flags - New feature flags in `env_var.zig` ## Test Plan - [x] Existing install tests pass - [ ] Test esbuild install without postinstall scripts running - [ ] Test sharp install with scripts skipped - [ ] Test custom package.json configuration - [ ] Test fallback when platform-specific package not found - [ ] Test feature flag overrides 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Summary by CodeRabbit * **New Features** * Native binlink optimization: installs platform-specific binaries when available, with a safe retry fallback and verbose logging option. * Per-package postinstall controls to optionally skip lifecycle scripts. * New feature flags to disable native binlink optimization and to disable lifecycle-script ignoring. * **Tests** * End-to-end tests and test packages added to validate native binlink behavior across install scenarios and linker modes. * **Documentation** * Bench README and sample app migrated to a Next.js-based setup. --------- Co-authored-by: Claude Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Dylan Conway --- bench/install/README.md | 47 +- bench/install/app/entry.client.tsx | 18 - bench/install/app/entry.server.tsx | 101 ---- bench/install/app/root.tsx | 20 - bench/install/app/routes/_index.tsx | 30 -- bench/install/bun.lock | 488 ++++++++++++++++++ bench/install/next-env.d.ts | 5 + bench/install/next.config.js | 10 + bench/install/package.json | 63 ++- bench/install/public/favicon.ico | Bin 16958 -> 0 bytes bench/install/remix.config.js | 14 - bench/install/remix.env.d.ts | 2 - bench/install/tsconfig.json | 22 - src/cli/link_command.zig | 2 + src/cli/unlink_command.zig | 2 + src/env_var.zig | 7 + src/install/PackageInstaller.zig | 211 ++++++-- src/install/PackageManager.zig | 2 + src/install/bin.zig | 14 +- src/install/install.zig | 1 + src/install/isolated_install/Installer.zig | 154 +++++- src/install/lockfile/Package.zig | 2 + src/install/postinstall_optimizer.zig | 170 ++++++ .../bun-install-native-binlink.test.ts | 152 ++++++ test/cli/install/multi-tool-pkg-1.0.0.tgz | Bin 784 -> 353 bytes .../create-native-binlink-packages.ts | 155 ++++++ .../test-native-binlink-target/package.json | 28 + .../test-native-binlink-target-1.0.0.tgz | Bin 0 -> 374 bytes .../packages/test-native-binlink/package.json | 25 + .../test-native-binlink-1.0.0.tgz | Bin 0 -> 381 bytes test/integration/esbuild/esbuild.test.ts | 69 ++- 31 files changed, 1454 insertions(+), 360 deletions(-) delete mode 100644 bench/install/app/entry.client.tsx delete mode 100644 bench/install/app/entry.server.tsx delete mode 100644 bench/install/app/root.tsx delete mode 100644 bench/install/app/routes/_index.tsx create mode 100644 bench/install/bun.lock create mode 100644 bench/install/next-env.d.ts create mode 100644 bench/install/next.config.js delete mode 100644 bench/install/public/favicon.ico delete mode 100644 bench/install/remix.config.js delete mode 100644 bench/install/remix.env.d.ts delete mode 100644 bench/install/tsconfig.json create mode 100644 src/install/postinstall_optimizer.zig create mode 100644 test/cli/install/bun-install-native-binlink.test.ts create mode 100644 test/cli/install/registry/packages/create-native-binlink-packages.ts create mode 100644 test/cli/install/registry/packages/test-native-binlink-target/package.json create mode 100644 test/cli/install/registry/packages/test-native-binlink-target/test-native-binlink-target-1.0.0.tgz create mode 100644 test/cli/install/registry/packages/test-native-binlink/package.json create mode 100644 test/cli/install/registry/packages/test-native-binlink/test-native-binlink-1.0.0.tgz diff --git a/bench/install/README.md b/bench/install/README.md index e94ce66976..67943c7fb4 100644 --- a/bench/install/README.md +++ b/bench/install/README.md @@ -1,40 +1,29 @@ -# `install` benchmark +# Create T3 App -Requires [`hyperfine`](https://github.com/sharkdp/hyperfine). The goal of this benchmark is to compare installation performance of Bun with other package managers _when caches are hot_. +This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`. -### With lockfile, online mode +## What's next? How do I make an app with this? -To run the benchmark with the standard "install" command for each package manager: +We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary. -```sh -$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install' -``` +If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help. -### With lockfile, offline mode +- [Next.js](https://nextjs.org) +- [NextAuth.js](https://next-auth.js.org) +- [Prisma](https://prisma.io) +- [Drizzle](https://orm.drizzle.team) +- [Tailwind CSS](https://tailwindcss.com) +- [tRPC](https://trpc.io) -Even though all packages are cached, some tools may hit the npm API during the version resolution step. (This is not the same as re-downloading a package.) To entirely avoid network calls, the other package managers require `--prefer-offline/--offline` flag. To run the benchmark using "offline" mode: +## Learn More -```sh -$ hyperfine --prepare 'rm -rf node_modules' --runs 1 'bun install' 'pnpm install --prefer-offline' 'yarn --offline' 'npm install --prefer-offline' -``` +To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources: -### Without lockfile, offline mode +- [Documentation](https://create.t3.gg/) +- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials -To run the benchmark with offline mode but without lockfiles: +You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome! -```sh -$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 'rm bun.lock && bun install' 'rm pnpm-lock.yaml && pnpm install --prefer-offline' 'rm yarn.lock && yarn --offline' 'rm package-lock.json && npm install --prefer-offline' -``` +## How do I deploy this? -## - -To check that the app is working as expected: - -``` -$ bun run dev -$ npm run dev -$ yarn dev -$ pnpm dev -``` - -Then visit [http://localhost:3000](http://localhost:3000). +Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information. diff --git a/bench/install/app/entry.client.tsx b/bench/install/app/entry.client.tsx deleted file mode 100644 index 186cd93449..0000000000 --- a/bench/install/app/entry.client.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/** - * By default, Remix will handle hydrating your app on the client for you. - * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ - * For more information, see https://remix.run/docs/en/main/file-conventions/entry.client - */ - -import { RemixBrowser } from "@remix-run/react"; -import { startTransition, StrictMode } from "react"; -import { hydrateRoot } from "react-dom/client"; - -startTransition(() => { - hydrateRoot( - document, - - - , - ); -}); diff --git a/bench/install/app/entry.server.tsx b/bench/install/app/entry.server.tsx deleted file mode 100644 index a83df79c87..0000000000 --- a/bench/install/app/entry.server.tsx +++ /dev/null @@ -1,101 +0,0 @@ -/** - * By default, Remix will handle generating the HTTP Response for you. - * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ - * For more information, see https://remix.run/docs/en/main/file-conventions/entry.server - */ - -import type { EntryContext } from "@remix-run/node"; -import { Response } from "@remix-run/node"; -import { RemixServer } from "@remix-run/react"; -import isbot from "isbot"; -import { PassThrough } from "node:stream"; -import { renderToPipeableStream } from "react-dom/server"; - -const ABORT_DELAY = 5_000; - -export default function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, -) { - return isbot(request.headers.get("user-agent")) - ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) - : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); -} - -function handleBotRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, -) { - return new Promise((resolve, reject) => { - const { pipe, abort } = renderToPipeableStream( - , - { - onAllReady() { - const body = new PassThrough(); - - responseHeaders.set("Content-Type", "text/html"); - - resolve( - new Response(body, { - headers: responseHeaders, - status: responseStatusCode, - }), - ); - - pipe(body); - }, - onShellError(error: unknown) { - reject(error); - }, - onError(error: unknown) { - responseStatusCode = 500; - console.error(error); - }, - }, - ); - - setTimeout(abort, ABORT_DELAY); - }); -} - -function handleBrowserRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext, -) { - return new Promise((resolve, reject) => { - const { pipe, abort } = renderToPipeableStream( - , - { - onShellReady() { - const body = new PassThrough(); - - responseHeaders.set("Content-Type", "text/html"); - - resolve( - new Response(body, { - headers: responseHeaders, - status: responseStatusCode, - }), - ); - - pipe(body); - }, - onShellError(error: unknown) { - reject(error); - }, - onError(error: unknown) { - console.error(error); - responseStatusCode = 500; - }, - }, - ); - - setTimeout(abort, ABORT_DELAY); - }); -} diff --git a/bench/install/app/root.tsx b/bench/install/app/root.tsx deleted file mode 100644 index 4d0236fb21..0000000000 --- a/bench/install/app/root.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from "@remix-run/react"; - -export default function App() { - return ( - - - - - - - - - - - - - - - ); -} diff --git a/bench/install/app/routes/_index.tsx b/bench/install/app/routes/_index.tsx deleted file mode 100644 index 92fccd4ada..0000000000 --- a/bench/install/app/routes/_index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { V2_MetaFunction } from "@remix-run/node"; - -export const meta: V2_MetaFunction = () => { - return [{ title: "New Remix App" }]; -}; - -export default function Index() { - return ( - - ); -} diff --git a/bench/install/bun.lock b/bench/install/bun.lock new file mode 100644 index 0000000000..2f2b0b1451 --- /dev/null +++ b/bench/install/bun.lock @@ -0,0 +1,488 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "installbench", + "dependencies": { + "@auth/drizzle-adapter": "^1.7.2", + "@t3-oss/env-nextjs": "^0.12.0", + "@tanstack/react-query": "^5.69.0", + "@trpc/client": "^11.0.0", + "@trpc/react-query": "^11.0.0", + "@trpc/server": "^11.0.0", + "drizzle-orm": "^0.41.0", + "esbuild": "^0.25.11", + "next": "^15.2.3", + "next-auth": "5.0.0-beta.25", + "postgres": "^3.4.4", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "server-only": "^0.0.1", + "superjson": "^2.2.1", + "zod": "^3.24.2", + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@tailwindcss/postcss": "^4.0.15", + "@types/node": "^20.14.10", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "drizzle-kit": "^0.30.5", + "postcss": "^8.5.3", + "tailwindcss": "^4.0.15", + "typescript": "^5.8.2", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@auth/core": ["@auth/core@0.41.1", "", { "dependencies": { "@panva/hkdf": "1.2.1", "jose": "6.1.0", "oauth4webapi": "3.8.2", "preact": "10.24.3", "preact-render-to-string": "6.5.11" } }, "sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw=="], + + "@auth/drizzle-adapter": ["@auth/drizzle-adapter@1.11.1", "", { "dependencies": { "@auth/core": "0.41.1" } }, "sha512-cQTvDZqsyF7RPhDm/B6SvqdVP9EzQhy3oM4Muu7fjjmSYFLbSR203E6dH631ZHSKDn2b4WZkfMnjPDzRsPSAeA=="], + + "@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=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="], + + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "0.18.20", "source-map-support": "0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], + + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "3.3.2", "get-tsconfig": "4.13.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="], + + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.3" }, "os": "darwin", "cpu": "arm64" }, "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.3" }, "os": "darwin", "cpu": "x64" }, "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.3" }, "os": "linux", "cpu": "arm" }, "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.3" }, "os": "linux", "cpu": "ppc64" }, "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.3" }, "os": "linux", "cpu": "s390x" }, "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.4", "", { "dependencies": { "@emnapi/runtime": "1.6.0" }, "cpu": "none" }, "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.4", "", { "os": "win32", "cpu": "x64" }, "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "0.3.13", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@next/env": ["@next/env@15.5.6", "", {}, "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ=="], + + "@panva/hkdf": ["@panva/hkdf@1.2.1", "", {}, "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw=="], + + "@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@t3-oss/env-core": ["@t3-oss/env-core@0.12.0", "", { "optionalDependencies": { "typescript": "5.9.3", "zod": "3.25.76" } }, "sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw=="], + + "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.12.0", "", { "dependencies": { "@t3-oss/env-core": "0.12.0" }, "optionalDependencies": { "typescript": "5.9.3", "zod": "3.25.76" } }, "sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "2.3.5", "enhanced-resolve": "5.18.3", "jiti": "2.6.1", "lightningcss": "1.30.2", "magic-string": "0.30.21", "source-map-js": "1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.16", "", { "dependencies": { "@alloc/quick-lru": "5.2.0", "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "postcss": "8.5.6", "tailwindcss": "4.1.16" } }, "sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.90.5", "", {}, "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.90.5", "", { "dependencies": { "@tanstack/query-core": "5.90.5" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q=="], + + "@trpc/client": ["@trpc/client@11.7.1", "", { "peerDependencies": { "@trpc/server": "11.7.1", "typescript": "5.9.3" } }, "sha512-uOnAjElKI892/U6aQMcBHYs3x7mme3Cvv1F87ytBL56rBvs7+DyK7r43zgaXKf13+GtPEI6ex5xjVUfyDW8XcQ=="], + + "@trpc/react-query": ["@trpc/react-query@11.7.1", "", { "peerDependencies": { "@tanstack/react-query": "5.90.5", "@trpc/client": "11.7.1", "@trpc/server": "11.7.1", "react": "19.2.0", "react-dom": "19.2.0", "typescript": "5.9.3" } }, "sha512-dEHDjIqSTzO8nLlCbtiFBMBwhbSkK1QP7aYVo3nP3sYBna0b+iCtrPXdxVPCSopr9/aIqDTEh+dMRZa7yBgjfQ=="], + + "@trpc/server": ["@trpc/server@11.7.1", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/node": ["@types/node@20.19.24", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA=="], + + "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + + "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "19.2.2" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="], + + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001752", "", {}, "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], + + "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "5.5.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "drizzle-kit": ["drizzle-kit@0.30.6", "", { "dependencies": { "@drizzle-team/brocli": "0.10.2", "@esbuild-kit/esm-loader": "2.6.5", "esbuild": "0.19.12", "esbuild-register": "3.6.0", "gel": "2.1.1" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g=="], + + "drizzle-orm": ["drizzle-orm@0.41.0", "", { "optionalDependencies": { "gel": "2.1.1", "postgres": "3.4.7" } }, "sha512-7A4ZxhHk9gdlXmTdPj/lREtP+3u8KvZ4yEN6MYVxBzZGex5Wtdc+CWSbu7btgF6TB0N+MNPrvW7RKBbxJchs/Q=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "4.2.11", "tapable": "2.3.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="], + + "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="], + + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "4.4.3" }, "peerDependencies": { "esbuild": "0.19.12" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], + + "gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "3.9.3", "debug": "4.4.3", "env-paths": "3.0.0", "semver": "7.7.3", "shell-quote": "1.8.3", "which": "4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="], + + "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="], + + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="], + + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "next": ["next@15.5.6", "", { "dependencies": { "@next/env": "15.5.6", "@swc/helpers": "0.5.15", "caniuse-lite": "1.0.30001752", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.6", "@next/swc-darwin-x64": "15.5.6", "@next/swc-linux-arm64-gnu": "15.5.6", "@next/swc-linux-arm64-musl": "15.5.6", "@next/swc-linux-x64-gnu": "15.5.6", "@next/swc-linux-x64-musl": "15.5.6", "@next/swc-win32-arm64-msvc": "15.5.6", "@next/swc-win32-x64-msvc": "15.5.6", "sharp": "0.34.4" }, "peerDependencies": { "react": "19.2.0", "react-dom": "19.2.0" }, "bin": { "next": "dist/bin/next" } }, "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ=="], + + "next-auth": ["next-auth@5.0.0-beta.25", "", { "dependencies": { "@auth/core": "0.37.2" }, "peerDependencies": { "next": "15.5.6", "react": "19.2.0" } }, "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog=="], + + "oauth4webapi": ["oauth4webapi@3.8.2", "", {}, "sha512-FzZZ+bht5X0FKe7Mwz3DAVAmlH1BV5blSak/lHMBKz0/EBMhX6B10GlQYI51+oRp8ObJaX0g6pXrAxZh5s8rjw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], + + "preact": ["preact@10.24.3", "", {}, "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA=="], + + "preact-render-to-string": ["preact-render-to-string@6.5.11", "", { "peerDependencies": { "preact": "10.24.3" } }, "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw=="], + + "pretty-format": ["pretty-format@3.8.0", "", {}, "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="], + + "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], + + "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "0.27.0" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="], + + "sharp": ["sharp@0.34.4", "", { "dependencies": { "@img/colour": "1.0.0", "detect-libc": "2.1.2", "semver": "7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.4", "@img/sharp-darwin-x64": "0.34.4", "@img/sharp-libvips-darwin-arm64": "1.2.3", "@img/sharp-libvips-darwin-x64": "1.2.3", "@img/sharp-libvips-linux-arm": "1.2.3", "@img/sharp-libvips-linux-arm64": "1.2.3", "@img/sharp-libvips-linux-ppc64": "1.2.3", "@img/sharp-libvips-linux-s390x": "1.2.3", "@img/sharp-libvips-linux-x64": "1.2.3", "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", "@img/sharp-libvips-linuxmusl-x64": "1.2.3", "@img/sharp-linux-arm": "0.34.4", "@img/sharp-linux-arm64": "0.34.4", "@img/sharp-linux-ppc64": "0.34.4", "@img/sharp-linux-s390x": "0.34.4", "@img/sharp-linux-x64": "0.34.4", "@img/sharp-linuxmusl-arm64": "0.34.4", "@img/sharp-linuxmusl-x64": "0.34.4", "@img/sharp-wasm32": "0.34.4", "@img/sharp-win32-arm64": "0.34.4", "@img/sharp-win32-ia32": "0.34.4", "@img/sharp-win32-x64": "0.34.4" } }, "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA=="], + + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + + "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.1.2", "source-map": "0.6.1" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "superjson": ["superjson@2.2.5", "", { "dependencies": { "copy-anything": "4.0.5" } }, "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w=="], + + "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "which": ["which@4.0.0", "", { "dependencies": { "isexe": "3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "next-auth/@auth/core": ["@auth/core@0.37.2", "", { "dependencies": { "@panva/hkdf": "1.2.1", "@types/cookie": "0.6.0", "cookie": "0.7.1", "jose": "5.10.0", "oauth4webapi": "3.8.2", "preact": "10.11.3", "preact-render-to-string": "5.2.3" } }, "sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], + + "drizzle-kit/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="], + + "drizzle-kit/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="], + + "drizzle-kit/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="], + + "drizzle-kit/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="], + + "drizzle-kit/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="], + + "drizzle-kit/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="], + + "drizzle-kit/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="], + + "drizzle-kit/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="], + + "drizzle-kit/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="], + + "drizzle-kit/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="], + + "drizzle-kit/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="], + + "drizzle-kit/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="], + + "drizzle-kit/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="], + + "drizzle-kit/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="], + + "drizzle-kit/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="], + + "drizzle-kit/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="], + + "drizzle-kit/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="], + + "drizzle-kit/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="], + + "drizzle-kit/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="], + + "drizzle-kit/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="], + + "drizzle-kit/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="], + + "drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], + + "next-auth/@auth/core/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + + "next-auth/@auth/core/preact": ["preact@10.11.3", "", {}, "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg=="], + + "next-auth/@auth/core/preact-render-to-string": ["preact-render-to-string@5.2.3", "", { "dependencies": { "pretty-format": "3.8.0" }, "peerDependencies": { "preact": "10.11.3" } }, "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA=="], + } +} diff --git a/bench/install/next-env.d.ts b/bench/install/next-env.d.ts new file mode 100644 index 0000000000..4f11a03dc6 --- /dev/null +++ b/bench/install/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/bench/install/next.config.js b/bench/install/next.config.js new file mode 100644 index 0000000000..121c4f4c24 --- /dev/null +++ b/bench/install/next.config.js @@ -0,0 +1,10 @@ +/** + * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful + * for Docker builds. + */ +import "./src/env.js"; + +/** @type {import("next").NextConfig} */ +const config = {}; + +export default config; diff --git a/bench/install/package.json b/bench/install/package.json index 02bb8e92fe..9db93cd9c4 100644 --- a/bench/install/package.json +++ b/bench/install/package.json @@ -1,31 +1,52 @@ { + "name": "installbench", + "version": "0.1.0", "private": true, - "sideEffects": false, + "type": "module", "scripts": { - "build": "remix build", - "dev": "remix dev", - "start": "remix-serve build", - "typecheck": "tsc", - "clean": "rm -rf node_modules", - "bench": "hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install'" + "build": "next build", + "check": "biome check .", + "check:unsafe": "biome check --write --unsafe .", + "check:write": "biome check --write .", + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio", + "dev": "next dev --turbo", + "preview": "next build && next start", + "start": "next start", + "typecheck": "tsc --noEmit" }, "dependencies": { - "@remix-run/node": "^1.15.0", - "@remix-run/react": "^1.15.0", - "@remix-run/serve": "^1.15.0", - "isbot": "^3.6.5", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@auth/drizzle-adapter": "^1.7.2", + "@t3-oss/env-nextjs": "^0.12.0", + "@tanstack/react-query": "^5.69.0", + "@trpc/client": "^11.0.0", + "@trpc/react-query": "^11.0.0", + "@trpc/server": "^11.0.0", + "drizzle-orm": "^0.41.0", + "esbuild": "^0.25.11", + "next": "^15.2.3", + "next-auth": "5.0.0-beta.25", + "postgres": "^3.4.4", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "server-only": "^0.0.1", + "superjson": "^2.2.1", + "zod": "^3.24.2" }, "devDependencies": { - "@remix-run/dev": "^1.15.0", - "@remix-run/eslint-config": "^1.15.0", - "@types/react": "^18.0.25", - "@types/react-dom": "^18.0.8", - "eslint": "^8.27.0", - "typescript": "^4.8.4" + "@biomejs/biome": "1.9.4", + "@tailwindcss/postcss": "^4.0.15", + "@types/node": "^20.14.10", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "drizzle-kit": "^0.30.5", + "postcss": "^8.5.3", + "tailwindcss": "^4.0.15", + "typescript": "^5.8.2" }, - "engines": { - "node": ">=14" + "ct3aMetadata": { + "initVersion": "7.39.3" } } diff --git a/bench/install/public/favicon.ico b/bench/install/public/favicon.ico deleted file mode 100644 index 8830cf6821b354114848e6354889b8ecf6d2bc61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16958 zcmeI3+jCXb9mnJN2h^uNlXH@jlam{_a8F3W{T}Wih>9YJpaf7TUbu)A5fv|h7OMfR zR;q$lr&D!wv|c)`wcw1?>4QT1(&|jdsrI2h`Rn)dTW5t$8pz=s3_5L?#oBxAowe8R z_WfPfN?F+@`q$D@rvC?(W!uWieppskmQ~YG*>*L?{img@tWpnYXZslxeh#TSUS3{q z1Ju6JcfQSbQuORq69@YK(X-3c9vC2c2a2z~zw=F=50@pm0PUiCAm!bAT?2jpM`(^b zC|2&Ngngt^<>oCv#?P(AZ`5_84x#QBPulix)TpkIAUp=(KgGo4CVS~Sxt zVoR4>r5g9%bDh7hi0|v$={zr>CHd`?-l4^Ld(Z9PNz9piFY+llUw_x4ou7Vf-q%$g z)&)J4>6Ft~RZ(uV>dJD|`nxI1^x{X@Z5S<=vf;V3w_(*O-7}W<=e$=}CB9_R;)m9)d7`d_xx+nl^Bg|%ew=?uoKO8w zeQU7h;~8s!@9-k>7Cx}1SDQ7m(&miH zs8!l*wOJ!GHbdh)pD--&W3+w`9YJ=;m^FtMY=`mTq8pyV!-@L6smwp3(q?G>=_4v^ zn(ikLue7!y70#2uhqUVpb7fp!=xu2{aM^1P^pts#+feZv8d~)2sf`sjXLQCEj;pdI z%~f`JOO;*KnziMv^i_6+?mL?^wrE_&=IT9o1i!}Sd4Sx4O@w~1bi1)8(sXvYR-1?7~Zr<=SJ1Cw!i~yfi=4h6o3O~(-Sb2Ilwq%g$+V` z>(C&N1!FV5rWF&iwt8~b)=jIn4b!XbrWrZgIHTISrdHcpjjx=TwJXI7_%Ks4oFLl9 zNT;!%!P4~xH85njXdfqgnIxIFOOKW`W$fxU%{{5wZkVF^G=JB$oUNU5dQSL&ZnR1s z*ckJ$R`eCUJsWL>j6*+|2S1TL_J|Fl&kt=~XZF=+=iT0Xq1*KU-NuH%NAQff$LJp3 zU_*a;@7I0K{mqwux87~vwsp<}@P>KNDb}3U+6$rcZ114|QTMUSk+rhPA(b{$>pQTc zIQri{+U>GMzsCy0Mo4BfWXJlkk;RhfpWpAB{=Rtr*d1MNC+H3Oi5+3D$gUI&AjV-1 z=0ZOox+bGyHe=yk-yu%=+{~&46C$ut^ZN+ysx$NH}*F43)3bKkMsxGyIl#>7Yb8W zO{}&LUO8Ow{7>!bvSq?X{15&Y|4}0w2=o_^0ZzYgB+4HhZ4>s*mW&?RQ6&AY|CPcx z$*LjftNS|H)ePYnIKNg{ck*|y7EJ&Co0ho0K`!{ENPkASeKy-JWE}dF_%}j)Z5a&q zXAI2gPu6`s-@baW=*+keiE$ALIs5G6_X_6kgKK8n3jH2-H9`6bo)Qn1 zZ2x)xPt1=`9V|bE4*;j9$X20+xQCc$rEK|9OwH-O+Q*k`ZNw}K##SkY z3u}aCV%V|j@!gL5(*5fuWo>JFjeU9Qqk`$bdwH8(qZovE2tA7WUpoCE=VKm^eZ|vZ z(k<+j*mGJVah>8CkAsMD6#I$RtF;#57Wi`c_^k5?+KCmX$;Ky2*6|Q^bJ8+s%2MB}OH-g$Ev^ zO3uqfGjuN%CZiu<`aCuKCh{kK!dDZ+CcwgIeU2dsDfz+V>V3BDb~)~ zO!2l!_)m;ZepR~sL+-~sHS7;5ZB|~uUM&&5vDda2b z)CW8S6GI*oF><|ZeY5D^+Mcsri)!tmrM33qvwI4r9o@(GlW!u2R>>sB|E#%W`c*@5 z|0iA|`{6aA7D4Q?vc1{vT-#yytn07`H!QIO^1+X7?zG3%y0gPdIPUJ#s*DNAwd}m1_IMN1^T&be~+E z_z%1W^9~dl|Me9U6+3oNyuMDkF*z_;dOG(Baa*yq;TRiw{EO~O_S6>e*L(+Cdu(TM z@o%xTCV%hi&p)x3_inIF!b|W4|AF5p?y1j)cr9RG@v%QVaN8&LaorC-kJz_ExfVHB za!mtuee#Vb?dh&bwrfGHYAiX&&|v$}U*UBM;#F!N=x>x|G5s0zOa9{(`=k4v^6iK3 z8d&=O@xhDs{;v7JQ%eO;!Bt`&*MH&d zp^K#dkq;jnJz%%bsqwlaKA5?fy zS5JDbO#BgSAdi8NM zDo2SifX6^Z;vn>cBh-?~r_n9qYvP|3ihrnqq6deS-#>l#dV4mX|G%L8|EL;$U+w69 z;rTK3FW$ewUfH|R-Z;3;jvpfiDm?Fvyu9PeR>wi|E8>&j2Z@2h`U}|$>2d`BPV3pz#ViIzH8v6pP^L-p!GbLv<;(p>}_6u&E6XO5- zJ8JEvJ1)0>{iSd|kOQn#?0rTYL=KSmgMHCf$Qbm;7|8d(goD&T-~oCDuZf57iP#_Y zmxaoOSjQsm*^u+m$L9AMqwi=6bpdiAY6k3akjGN{xOZ`_J<~Puyzpi7yhhKrLmXV; z@ftONPy;Uw1F#{_fyGbk04yLE01v=i_5`RqQP+SUH0nb=O?l!J)qCSTdsbmjFJrTm zx4^ef@qt{B+TV_OHOhtR?XT}1Etm(f21;#qyyW6FpnM+S7*M1iME?9fe8d-`Q#InN z?^y{C_|8bxgUE@!o+Z72C)BrS&5D`gb-X8kq*1G7Uld-z19V}HY~mK#!o9MC-*#^+ znEsdc-|jj0+%cgBMy(cEkq4IQ1D*b;17Lyp>Utnsz%LRTfjQKL*vo(yJxwtw^)l|! z7jhIDdtLB}mpkOIG&4@F+9cYkS5r%%jz}I0R#F4oBMf-|Jmmk* zk^OEzF%}%5{a~kGYbFjV1n>HKC+a`;&-n*v_kD2DPP~n5(QE3C;30L<32GB*qV2z$ zWR1Kh=^1-q)P37WS6YWKlUSDe=eD^u_CV+P)q!3^{=$#b^auGS7m8zFfFS<>(e~)TG z&uwWhSoetoe!1^%)O}=6{SUcw-UQmw+i8lokRASPsbT=H|4D|( zk^P7>TUEFho!3qXSWn$m2{lHXw zD>eN6-;wwq9(?@f^F4L2Ny5_6!d~iiA^s~(|B*lbZir-$&%)l>%Q(36yOIAu|326K ztmBWz|MLA{Kj(H_{w2gd*nZ6a@ma(w==~EHIscEk|C=NGJa%Ruh4_+~f|%rt{I5v* zIX@F?|KJID56-ivb+PLo(9hn_CdK{irOcL15>JNQFY112^$+}JPyI{uQ~$&E*=ri; z`d^fH?4f=8vKHT4!p9O*fX(brB75Y9?e>T9=X#Fc@V#%@5^)~#zu5I(=>LQA-EGTS zecy*#6gG+8lapch#Hh%vl(+}J;Q!hC1OKoo;#h3#V%5Js)tQ)|>pTT@1ojd+F9Gey zg`B)zm`|Mo%tH31s4=<+`Pu|B3orXwNyIcNN>;fBkIj^X8P}RXhF= zXQK1u5RLN7k#_Q(KznJrALtMM13!vhfr025ar?@-%{l|uWt@NEd<$~n>RQL{ z+o;->n)+~0tt(u|o_9h!T`%M8%)w2awpV9b*xz9Pl-daUJm3y-HT%xg`^mFd6LBeL z!0~s;zEr)Bn9x)I(wx`;JVwvRcc^io2XX(Nn3vr3dgbrr@YJ?K3w18P*52^ieBCQP z=Up1V$N2~5ppJHRTeY8QfM(7Yv&RG7oWJAyv?c3g(29)P)u;_o&w|&)HGDIinXT~p z3;S|e$=&Tek9Wn!`cdY+d-w@o`37}x{(hl>ykB|%9yB$CGdIcl7Z?d&lJ%}QHck77 zJPR%C+s2w1_Dl_pxu6$Zi!`HmoD-%7OD@7%lKLL^Ixd9VlRSW*o&$^iQ2z+}hTgH) z#91TO#+jH<`w4L}XWOt(`gqM*uTUcky`O(mEyU|4dJoy6*UZJ7%*}ajuos%~>&P2j zk23f5<@GeV?(?`l=ih+D8t`d72xrUjv0wsg;%s1@*2p?TQ;n2$pV7h?_T%sL>iL@w zZ{lmc<|B7!e&o!zs6RW+u8+aDyUdG>ZS(v&rT$QVymB7sEC@VsK1dg^3F@K90-wYB zX!we79qx`(6LA>F$~{{xE8-3Wzyfe`+Lsce(?uj{k@lb97YTJt#>l*Z&LyKX@zjmu?UJC9w~;|NsB{%7G}y*uNDBxirfC EKbET!0{{R3 diff --git a/bench/install/remix.config.js b/bench/install/remix.config.js deleted file mode 100644 index a1a396661b..0000000000 --- a/bench/install/remix.config.js +++ /dev/null @@ -1,14 +0,0 @@ -/** @type {import('@remix-run/dev').AppConfig} */ -module.exports = { - ignoredRouteFiles: ["**/.*"], - // appDirectory: "app", - // assetsBuildDirectory: "public/build", - // serverBuildPath: "build/index.js", - // publicPath: "/build/", - future: { - v2_errorBoundary: true, - v2_meta: true, - v2_normalizeFormMethod: true, - v2_routeConvention: true, - }, -}; diff --git a/bench/install/remix.env.d.ts b/bench/install/remix.env.d.ts deleted file mode 100644 index dcf8c45e1d..0000000000 --- a/bench/install/remix.env.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// diff --git a/bench/install/tsconfig.json b/bench/install/tsconfig.json deleted file mode 100644 index 20f8a386a6..0000000000 --- a/bench/install/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], - "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2019"], - "isolatedModules": true, - "esModuleInterop": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "resolveJsonModule": true, - "target": "ES2019", - "strict": true, - "allowJs": true, - "forceConsistentCasingInFileNames": true, - "baseUrl": ".", - "paths": { - "~/*": ["./app/*"] - }, - - // Remix takes care of building everything in `remix build`. - "noEmit": true - } -} diff --git a/src/cli/link_command.zig b/src/cli/link_command.zig index 8ac22d9d48..28c6047c53 100644 --- a/src/cli/link_command.zig +++ b/src/cli/link_command.zig @@ -141,6 +141,8 @@ fn link(ctx: Command.Context) !void { .bin = package.bin, .node_modules_path = &node_modules_path, .global_bin_path = manager.options.bin_path, + .target_node_modules_path = &node_modules_path, + .target_package_name = strings.StringOrTinyString.init(name), // .destination_dir_subpath = destination_dir_subpath, .package_name = strings.StringOrTinyString.init(name), diff --git a/src/cli/unlink_command.zig b/src/cli/unlink_command.zig index 1cc48c2def..86d1a93554 100644 --- a/src/cli/unlink_command.zig +++ b/src/cli/unlink_command.zig @@ -101,6 +101,8 @@ fn unlink(ctx: Command.Context) !void { defer node_modules_path.deinit(); var bin_linker = Bin.Linker{ + .target_node_modules_path = &node_modules_path, + .target_package_name = strings.StringOrTinyString.init(name), .bin = package.bin, .node_modules_path = &node_modules_path, .global_bin_path = manager.options.bin_path, diff --git a/src/env_var.zig b/src/env_var.zig index cdd75eda12..f99f7f7252 100644 --- a/src/env_var.zig +++ b/src/env_var.zig @@ -137,6 +137,13 @@ pub const feature_flag = struct { pub const BUN_BE_BUN = newFeatureFlag("BUN_BE_BUN", .{}); pub const BUN_DEBUG_NO_DUMP = newFeatureFlag("BUN_DEBUG_NO_DUMP", .{}); pub const BUN_DESTRUCT_VM_ON_EXIT = newFeatureFlag("BUN_DESTRUCT_VM_ON_EXIT", .{}); + + /// Disable "nativeDependencies" + pub const BUN_FEATURE_FLAG_DISABLE_NATIVE_DEPENDENCY_LINKER = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_NATIVE_DEPENDENCY_LINKER", .{}); + + /// Disable "ignoreScripts" in package.json + pub const BUN_FEATURE_FLAG_DISABLE_IGNORE_SCRIPTS = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_IGNORE_SCRIPTS", .{}); + pub const BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_ADDRCONFIG", .{}); pub const BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_ASYNC_TRANSPILER", .{}); pub const BUN_FEATURE_FLAG_DISABLE_DNS_CACHE = newFeatureFlag("BUN_FEATURE_FLAG_DISABLE_DNS_CACHE", .{}); diff --git a/src/install/PackageInstaller.zig b/src/install/PackageInstaller.zig index 3e5e633bbf..eb2fa02a63 100644 --- a/src/install/PackageInstaller.zig +++ b/src/install/PackageInstaller.zig @@ -256,10 +256,18 @@ pub const PackageInstaller = struct { log_level: Options.LogLevel, ) void { const lockfile = this.lockfile; + const manager = this.manager; const string_buf = lockfile.buffers.string_bytes.items; var node_modules_path: bun.AbsPath(.{}) = .from(this.node_modules.path.items); defer node_modules_path.deinit(); + const pkgs = lockfile.packages.slice(); + const pkg_name_hashes = pkgs.items(.name_hash); + const pkg_metas = pkgs.items(.meta); + const pkg_resolutions_lists = pkgs.items(.resolutions); + const pkg_resolutions_buffer = lockfile.buffers.resolutions.items; + const pkg_names = pkgs.items(.name); + while (tree.binaries.removeOrNull()) |dep_id| { bun.assertWithLocation(dep_id < lockfile.buffers.dependencies.items.len, @src()); const package_id = lockfile.buffers.resolutions.items[dep_id]; @@ -268,28 +276,50 @@ pub const PackageInstaller = struct { bun.assertWithLocation(bin.tag != .none, @src()); const alias = lockfile.buffers.dependencies.items[dep_id].name.slice(string_buf); + const package_name_ = strings.StringOrTinyString.init(alias); + var target_package_name = package_name_; + var can_retry_without_native_binlink_optimization = false; + var target_node_modules_path_opt: ?bun.AbsPath(.{}) = null; + defer if (target_node_modules_path_opt) |*path| path.deinit(); - var bin_linker: Bin.Linker = .{ - .bin = bin, - .global_bin_path = this.options.bin_path, - .package_name = strings.StringOrTinyString.init(alias), - .string_buf = string_buf, - .extern_string_buf = lockfile.buffers.extern_strings.items, - .seen = &this.seen_bin_links, - .node_modules_path = &node_modules_path, - .abs_target_buf = link_target_buf, - .abs_dest_buf = link_dest_buf, - .rel_buf = link_rel_buf, - }; + if (manager.postinstall_optimizer.isNativeBinlinkEnabled()) native_binlink_optimization: { + // Check for native binlink optimization + const name_hash = pkg_name_hashes[package_id]; + if (manager.postinstall_optimizer.get(name_hash)) |optimizer| { + switch (optimizer) { + .native_binlink => { + const target_cpu = manager.options.cpu; + const target_os = manager.options.os; + if (PostinstallOptimizer.getNativeBinlinkReplacementPackageID( + pkg_resolutions_lists[package_id].get(pkg_resolutions_buffer), + pkg_metas, + target_cpu, + target_os, + )) |replacement_pkg_id| { + if (tree_id != 0) { + // TODO: support this optimization in nested node_modules + // It's tricky to get the hoisting right. + // So we leave this out for now. + break :native_binlink_optimization; + } + const replacement_name = pkg_names[replacement_pkg_id].slice(string_buf); + target_package_name = strings.StringOrTinyString.init(replacement_name); + can_retry_without_native_binlink_optimization = true; + } + }, + .ignore => {}, + } + } + } // globally linked packages shouls always belong to the root // tree (0). - const global = if (!this.manager.options.global) + const global = if (!manager.options.global) false else if (tree_id != 0) false else global: { - for (this.manager.update_requests) |request| { + for (manager.update_requests) |request| { if (request.package_id == package_id) { break :global true; } @@ -298,21 +328,52 @@ pub const PackageInstaller = struct { break :global false; }; - bin_linker.link(global); + while (true) { + var bin_linker: Bin.Linker = .{ + .bin = bin, + .global_bin_path = this.options.bin_path, + .package_name = package_name_, + .target_package_name = target_package_name, + .string_buf = string_buf, + .extern_string_buf = lockfile.buffers.extern_strings.items, + .seen = &this.seen_bin_links, + .node_modules_path = &node_modules_path, + .target_node_modules_path = if (target_node_modules_path_opt) |*path| path else &node_modules_path, + .abs_target_buf = link_target_buf, + .abs_dest_buf = link_dest_buf, + .rel_buf = link_rel_buf, + }; - if (bin_linker.err) |err| { - if (log_level != .silent) { - this.manager.log.addErrorFmtOpts( - this.manager.allocator, - "Failed to link {s}: {s}", - .{ alias, @errorName(err) }, - .{}, - ) catch |e| bun.handleOom(e); + bin_linker.link(global); + + if (can_retry_without_native_binlink_optimization and (bin_linker.skipped_due_to_missing_bin or bin_linker.err != null)) { + can_retry_without_native_binlink_optimization = false; + if (PackageManager.verbose_install) { + Output.prettyErrorln("[Bin Linker] {s} -> {s} retrying without native bin link", .{ + package_name_.slice(), + target_package_name.slice(), + }); + } + target_package_name = package_name_; + continue; } - if (this.options.enable.fail_early) { - this.manager.crash(); + if (bin_linker.err) |err| { + if (log_level != .silent) { + manager.log.addErrorFmtOpts( + manager.allocator, + "Failed to link {s}: {s}", + .{ alias, @errorName(err) }, + .{}, + ) catch |e| bun.handleOom(e); + } + + if (this.options.enable.fail_early) { + manager.crash(); + } } + + break; } } } @@ -357,6 +418,7 @@ pub const PackageInstaller = struct { if (this.canRunScripts(tree_id)) { _ = this.pending_lifecycle_scripts.swapRemove(i); const output_in_foreground = false; + this.manager.spawnPackageLifecycleScripts( this.command_ctx, entry.list, @@ -1100,22 +1162,40 @@ pub const PackageInstaller = struct { defer folder_path.deinit(); folder_path.append(alias.slice(this.lockfile.buffers.string_bytes.items)); - if (this.enqueueLifecycleScripts( - alias.slice(this.lockfile.buffers.string_bytes.items), - log_level, - &folder_path, - package_id, - dep.behavior.optional, - resolution, - )) { - if (is_trusted_through_update_request) { - this.manager.trusted_deps_to_add_to_package_json.append( - this.manager.allocator, - bun.handleOom(this.manager.allocator.dupe(u8, alias.slice(this.lockfile.buffers.string_bytes.items))), - ) catch |err| bun.handleOom(err); + enqueueLifecycleScripts: { + if (this.manager.postinstall_optimizer.shouldIgnoreLifecycleScripts( + pkg_name_hash, + this.lockfile.packages.items(.resolutions)[package_id].get(this.lockfile.buffers.resolutions.items), + this.lockfile.packages.items(.meta), + this.manager.options.cpu, + this.manager.options.os, + this.current_tree_id, + )) { + if (PackageManager.verbose_install) { + Output.prettyErrorln("[Lifecycle Scripts] ignoring {s} lifecycle scripts", .{ + pkg_name.slice(this.lockfile.buffers.string_bytes.items), + }); + } + break :enqueueLifecycleScripts; + } - if (this.lockfile.trusted_dependencies == null) this.lockfile.trusted_dependencies = .{}; - this.lockfile.trusted_dependencies.?.put(this.manager.allocator, truncated_dep_name_hash, {}) catch |err| bun.handleOom(err); + if (this.enqueueLifecycleScripts( + alias.slice(this.lockfile.buffers.string_bytes.items), + log_level, + &folder_path, + package_id, + dep.behavior.optional, + resolution, + )) { + if (is_trusted_through_update_request) { + this.manager.trusted_deps_to_add_to_package_json.append( + this.manager.allocator, + bun.handleOom(this.manager.allocator.dupe(u8, alias.slice(this.lockfile.buffers.string_bytes.items))), + ) catch |err| bun.handleOom(err); + + if (this.lockfile.trusted_dependencies == null) this.lockfile.trusted_dependencies = .{}; + this.lockfile.trusted_dependencies.?.put(this.manager.allocator, truncated_dep_name_hash, {}) catch |err| bun.handleOom(err); + } } } } @@ -1272,24 +1352,42 @@ pub const PackageInstaller = struct { defer folder_path.deinit(); folder_path.append(alias.slice(this.lockfile.buffers.string_bytes.items)); - if (this.enqueueLifecycleScripts( - alias.slice(this.lockfile.buffers.string_bytes.items), - log_level, - &folder_path, - package_id, - dep.behavior.optional, - resolution, - )) { - if (is_trusted_through_update_request) { - this.manager.trusted_deps_to_add_to_package_json.append( - this.manager.allocator, - bun.handleOom(this.manager.allocator.dupe(u8, alias.slice(this.lockfile.buffers.string_bytes.items))), - ) catch |err| bun.handleOom(err); + enqueueLifecycleScripts: { + if (this.manager.postinstall_optimizer.shouldIgnoreLifecycleScripts( + pkg_name_hash, + this.lockfile.packages.items(.resolutions)[package_id].get(this.lockfile.buffers.resolutions.items), + this.lockfile.packages.items(.meta), + this.manager.options.cpu, + this.manager.options.os, + this.current_tree_id, + )) { + if (PackageManager.verbose_install) { + Output.prettyErrorln("[Lifecycle Scripts] ignoring {s} lifecycle scripts", .{ + pkg_name.slice(this.lockfile.buffers.string_bytes.items), + }); + } + break :enqueueLifecycleScripts; } - if (add_to_lockfile) { - if (this.lockfile.trusted_dependencies == null) this.lockfile.trusted_dependencies = .{}; - this.lockfile.trusted_dependencies.?.put(this.manager.allocator, truncated_dep_name_hash, {}) catch |err| bun.handleOom(err); + if (this.enqueueLifecycleScripts( + alias.slice(this.lockfile.buffers.string_bytes.items), + log_level, + &folder_path, + package_id, + dep.behavior.optional, + resolution, + )) { + if (is_trusted_through_update_request) { + this.manager.trusted_deps_to_add_to_package_json.append( + this.manager.allocator, + bun.handleOom(this.manager.allocator.dupe(u8, alias.slice(this.lockfile.buffers.string_bytes.items))), + ) catch |err| bun.handleOom(err); + } + + if (add_to_lockfile) { + if (this.lockfile.trusted_dependencies == null) this.lockfile.trusted_dependencies = .{}; + this.lockfile.trusted_dependencies.?.put(this.manager.allocator, truncated_dep_name_hash, {}) catch |err| bun.handleOom(err); + } } } } @@ -1424,6 +1522,7 @@ const PackageID = install.PackageID; const PackageInstall = install.PackageInstall; const PackageNameHash = install.PackageNameHash; const PatchTask = install.PatchTask; +const PostinstallOptimizer = install.PostinstallOptimizer; const Resolution = install.Resolution; const Task = install.Task; const TaskCallbackContext = install.TaskCallbackContext; diff --git a/src/install/PackageManager.zig b/src/install/PackageManager.zig index 308f1b9a22..c18870587a 100644 --- a/src/install/PackageManager.zig +++ b/src/install/PackageManager.zig @@ -89,6 +89,7 @@ lockfile: *Lockfile = undefined, options: Options, preinstall_state: std.ArrayListUnmanaged(PreinstallState) = .{}, +postinstall_optimizer: PostinstallOptimizer.List = .{}, global_link_dir: ?std.fs.Dir = null, global_dir: ?std.fs.Dir = null, @@ -1314,6 +1315,7 @@ const PackageManifestMap = bun.install.PackageManifestMap; const PackageNameAndVersionHash = bun.install.PackageNameAndVersionHash; const PackageNameHash = bun.install.PackageNameHash; const PatchTask = bun.install.PatchTask; +const PostinstallOptimizer = bun.install.PostinstallOptimizer; const PreinstallState = bun.install.PreinstallState; const Task = bun.install.Task; const TaskCallbackContext = bun.install.TaskCallbackContext; diff --git a/src/install/bin.zig b/src/install/bin.zig index 9d0a5e4e0c..7b2e347a32 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -504,6 +504,14 @@ pub const Bin = extern struct { pub const Linker = struct { bin: Bin, + /// Usually will be the same as `node_modules_path`. + /// Used to support native bin linking. + target_node_modules_path: *bun.AbsPath(.{}), + + /// Usually will be the same as `package_name`. + /// Used to support native bin linking. + target_package_name: strings.StringOrTinyString, + // Hash map of seen destination paths for this `node_modules/.bin` folder. PackageInstaller will reset it before // linking each tree. seen: ?*bun.StringHashMap(void), @@ -523,6 +531,7 @@ pub const Bin = extern struct { rel_buf: []u8, err: ?anyerror = null, + skipped_due_to_missing_bin: bool = false, pub var umask: bun.Mode = 0; @@ -570,6 +579,7 @@ pub const Bin = extern struct { // Skip if the target does not exist. This is important because placing a dangling // shim in path might break a postinstall if (!bun.sys.exists(abs_target)) { + this.skipped_due_to_missing_bin = true; return; } @@ -838,7 +848,7 @@ pub const Bin = extern struct { /// uses `this.abs_target_buf` pub fn buildTargetPackageDir(this: *const Linker) []const u8 { - const dest_dir_without_trailing_slash = strings.withoutTrailingSlash(this.node_modules_path.slice()); + const dest_dir_without_trailing_slash = strings.withoutTrailingSlash(this.target_node_modules_path.slice()); var remain = this.abs_target_buf; @@ -847,7 +857,7 @@ pub const Bin = extern struct { remain[0] = std.fs.path.sep; remain = remain[1..]; - const package_name = this.package_name.slice(); + const package_name = this.target_package_name.slice(); @memcpy(remain[0..package_name.len], package_name); remain = remain[package_name.len..]; remain[0] = std.fs.path.sep; diff --git a/src/install/install.zig b/src/install/install.zig index 3ccf3d3bcc..b587ce67f7 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -257,6 +257,7 @@ pub const Resolution = @import("./resolution.zig").Resolution; pub const Store = @import("./isolated_install/Store.zig").Store; pub const FileCopier = @import("./isolated_install/FileCopier.zig").FileCopier; pub const PnpmMatcher = @import("./PnpmMatcher.zig"); +pub const PostinstallOptimizer = @import("./postinstall_optimizer.zig").PostinstallOptimizer; pub const ArrayIdentityContext = @import("../identity_context.zig").ArrayIdentityContext; pub const IdentityContext = @import("../identity_context.zig").IdentityContext; diff --git a/src/install/isolated_install/Installer.zig b/src/install/isolated_install/Installer.zig index 9985c8882e..e891ca4353 100644 --- a/src/install/isolated_install/Installer.zig +++ b/src/install/isolated_install/Installer.zig @@ -435,6 +435,8 @@ pub const Installer = struct { const pkg_names = pkgs.items(.name); const pkg_name_hashes = pkgs.items(.name_hash); const pkg_resolutions = pkgs.items(.resolution); + const pkg_resolutions_lists = pkgs.items(.resolutions); + const pkg_metas: []const Lockfile.Package.Meta = pkgs.items(.meta); const pkg_bins = pkgs.items(.bin); const pkg_script_lists = pkgs.items(.scripts); @@ -925,8 +927,18 @@ pub const Installer = struct { installer.appendStorePath(&pkg_cwd, this.entry_id); - if (pkg_res.tag != .root and (pkg_res.tag == .workspace or is_trusted)) { + if (pkg_res.tag != .root and (pkg_res.tag == .workspace or is_trusted)) enqueue_lifecycle_scripts: { var pkg_scripts: Package.Scripts = pkg_script_lists[pkg_id]; + if (is_trusted and manager.postinstall_optimizer.shouldIgnoreLifecycleScripts( + pkg_name_hashes[pkg_id], + installer.lockfile.buffers.resolutions.items, + pkg_metas, + manager.options.cpu, + manager.options.os, + null, + )) { + break :enqueue_lifecycle_scripts; + } var log = bun.logger.Log.init(bun.default_allocator); defer log.deinit(); @@ -999,16 +1011,39 @@ pub const Installer = struct { var node_modules_path: bun.AbsPath(.{}) = .initTopLevelDir(); defer node_modules_path.deinit(); - installer.appendStoreNodeModulesPath(&node_modules_path, this.entry_id); + var target_node_modules_path: ?bun.AbsPath(.{}) = null; + defer if (target_node_modules_path) |*path| path.deinit(); + + var target_package_name: strings.StringOrTinyString = strings.StringOrTinyString.init(dep_name); + + if (installer.maybeReplaceNodeModulesPath( + entry_node_ids, + node_pkg_ids, + pkg_name_hashes, + pkg_resolutions_lists, + installer.lockfile.buffers.resolutions.items, + installer.lockfile.packages.items(.meta), + pkg_id, + )) |replacement_entry_id| { + target_node_modules_path = bun.AbsPath(.{}).initTopLevelDir(); + installer.appendStoreNodeModulesPath(&target_node_modules_path.?, replacement_entry_id); + + const replacement_node_id = entry_node_ids[replacement_entry_id.get()]; + const replacement_pkg_id = node_pkg_ids[replacement_node_id.get()]; + target_package_name = strings.StringOrTinyString.init(installer.lockfile.str(&pkg_names[replacement_pkg_id])); + } + var bin_linker: Bin.Linker = .{ .bin = bin, .global_bin_path = installer.manager.options.bin_path, .package_name = strings.StringOrTinyString.init(dep_name), + .target_package_name = target_package_name, .string_buf = string_buf, .extern_string_buf = installer.lockfile.buffers.extern_strings.items, .seen = &seen, + .target_node_modules_path = if (target_node_modules_path) |*path| path else &node_modules_path, .node_modules_path = &node_modules_path, .abs_target_buf = abs_target_buf, .abs_dest_buf = abs_dest_buf, @@ -1017,6 +1052,23 @@ pub const Installer = struct { bin_linker.link(false); + if (target_node_modules_path != null and (bin_linker.skipped_due_to_missing_bin or bin_linker.err != null)) { + target_node_modules_path.?.deinit(); + target_node_modules_path = null; + + bin_linker.target_node_modules_path = &node_modules_path; + bin_linker.target_package_name = strings.StringOrTinyString.init(dep_name); + + if (this.installer.manager.options.log_level.isVerbose()) { + Output.prettyErrorln("[Bin Linker] {s} -> {s} retrying without native bin link", .{ + dep_name, + bin_linker.target_package_name.slice(), + }); + } + + bin_linker.link(false); + } + if (bin_linker.err) |err| { return .failure(.{ .binaries = err }); } @@ -1243,6 +1295,49 @@ pub const Installer = struct { _ = symlinker.ensureSymlink(link_strategy); } + fn maybeReplaceNodeModulesPath( + this: *const Installer, + entry_node_ids: []const Store.Node.Id, + node_pkg_ids: []const PackageID, + name_hashes: []const PackageNameHash, + pkg_resolutions_lists: []const Lockfile.PackageIDSlice, + pkg_resolutions_buffer: []const PackageID, + pkg_metas: []const Package.Meta, + pkg_id: PackageID, + ) ?Store.Entry.Id { + const postinstall_optimizer = &this.manager.postinstall_optimizer; + if (!postinstall_optimizer.isNativeBinlinkEnabled()) { + return null; + } + const name_hash = name_hashes[pkg_id]; + + if (postinstall_optimizer.get(name_hash)) |optimizer| { + switch (optimizer) { + .native_binlink => { + const manager = this.manager; + const target_cpu = manager.options.cpu; + const target_os = manager.options.os; + if (PostinstallOptimizer.getNativeBinlinkReplacementPackageID( + pkg_resolutions_lists[pkg_id].get(pkg_resolutions_buffer), + pkg_metas, + target_cpu, + target_os, + )) |replacement_pkg_id| { + for (entry_node_ids, 0..) |new_node_id, new_entry_id| { + if (node_pkg_ids[new_node_id.get()] == replacement_pkg_id) { + debug("native bin link {d} -> {d}", .{ pkg_id, replacement_pkg_id }); + return .from(@intCast(new_entry_id)); + } + } + } + }, + .ignore => {}, + } + } + + return null; + } + pub fn linkDependencyBins(this: *const Installer, parent_entry_id: Store.Entry.Id) !void { const lockfile = this.lockfile; const store = this.store; @@ -1251,7 +1346,7 @@ pub const Installer = struct { const extern_string_buf = lockfile.buffers.extern_strings.items; const entries = store.entries.slice(); - const entry_node_ids = entries.items(.node_id); + const entry_node_ids: []const Store.Node.Id = entries.items(.node_id); const entry_deps = entries.items(.dependencies); const nodes = store.nodes.slice(); @@ -1259,6 +1354,10 @@ pub const Installer = struct { const node_dep_ids = nodes.items(.dep_id); const pkgs = lockfile.packages.slice(); + const pkg_name_hashes = pkgs.items(.name_hash); + const pkg_metas = pkgs.items(.meta); + const pkg_resolutions_lists = pkgs.items(.resolutions); + const pkg_resolutions_buffer = lockfile.buffers.resolutions.items; const pkg_bins = pkgs.items(.bin); const link_target_buf = bun.path_buffer_pool.get(); @@ -1284,17 +1383,42 @@ pub const Installer = struct { if (bin.tag == .none) { continue; } - const alias = lockfile.buffers.dependencies.items[dep_id].name; + var target_node_modules_path: ?bun.AbsPath(.{}) = null; + defer if (target_node_modules_path) |*path| path.deinit(); + const package_name = strings.StringOrTinyString.init(alias.slice(string_buf)); + + var target_package_name = package_name; + + if (this.maybeReplaceNodeModulesPath( + entry_node_ids, + node_pkg_ids, + pkg_name_hashes, + pkg_resolutions_lists, + pkg_resolutions_buffer, + pkg_metas, + pkg_id, + )) |replacement_entry_id| { + target_node_modules_path = bun.AbsPath(.{}).initTopLevelDir(); + this.appendStoreNodeModulesPath(&target_node_modules_path.?, replacement_entry_id); + + const replacement_node_id = entry_node_ids[replacement_entry_id.get()]; + const replacement_pkg_id = node_pkg_ids[replacement_node_id.get()]; + const pkg_names = pkgs.items(.name); + target_package_name = strings.StringOrTinyString.init(this.lockfile.str(&pkg_names[replacement_pkg_id])); + } + var bin_linker: Bin.Linker = .{ .bin = bin, .global_bin_path = this.manager.options.bin_path, - .package_name = strings.StringOrTinyString.init(alias.slice(string_buf)), + .package_name = package_name, .string_buf = string_buf, .extern_string_buf = extern_string_buf, .seen = &seen, .node_modules_path = &node_modules_path, + .target_node_modules_path = if (target_node_modules_path) |*path| path else &node_modules_path, + .target_package_name = if (target_node_modules_path != null) target_package_name else package_name, .abs_target_buf = link_target_buf, .abs_dest_buf = link_dest_buf, .rel_buf = link_rel_buf, @@ -1302,6 +1426,23 @@ pub const Installer = struct { bin_linker.link(false); + if (target_node_modules_path != null and (bin_linker.skipped_due_to_missing_bin or bin_linker.err != null)) { + target_node_modules_path.?.deinit(); + target_node_modules_path = null; + + bin_linker.target_node_modules_path = &node_modules_path; + bin_linker.target_package_name = package_name; + + if (this.manager.options.log_level.isVerbose()) { + Output.prettyErrorln("[Bin Linker] {s} -> {s} retrying without native bin link", .{ + package_name.slice(), + target_package_name.slice(), + }); + } + + bin_linker.link(false); + } + if (bin_linker.err) |err| { return err; } @@ -1436,6 +1577,8 @@ pub const Installer = struct { const string = []const u8; +const debug = Output.scoped(.IsolatedInstaller, .hidden); + const FileCloner = @import("./FileCloner.zig"); const Hardlinker = @import("./Hardlinker.zig"); const std = @import("std"); @@ -1464,6 +1607,7 @@ const PackageID = install.PackageID; const PackageInstall = install.PackageInstall; const PackageManager = install.PackageManager; const PackageNameHash = install.PackageNameHash; +const PostinstallOptimizer = install.PostinstallOptimizer; const Resolution = install.Resolution; const Store = install.Store; const TruncatedPackageNameHash = install.TruncatedPackageNameHash; diff --git a/src/install/lockfile/Package.zig b/src/install/lockfile/Package.zig index c015adfde6..ddb6fc9e24 100644 --- a/src/install/lockfile/Package.zig +++ b/src/install/lockfile/Package.zig @@ -1583,6 +1583,8 @@ pub fn Package(comptime SemverIntType: type) type { // Count catalog strings in top-level package.json as well, since parseAppend // might process them later if no catalogs were found in workspaces lockfile.catalogs.parseCount(lockfile, json, &string_builder); + + try install.PostinstallOptimizer.fromPackageJSON(&pm.postinstall_optimizer, &json, allocator); } try string_builder.allocate(); diff --git a/src/install/postinstall_optimizer.zig b/src/install/postinstall_optimizer.zig new file mode 100644 index 0000000000..c009058541 --- /dev/null +++ b/src/install/postinstall_optimizer.zig @@ -0,0 +1,170 @@ +pub const PostinstallOptimizer = enum { + native_binlink, + ignore, + + const default_native_binlinks_name_hashes = &[_]PackageNameHash{ + bun.Semver.String.Builder.stringHash("esbuild"), + }; + + const default_ignore_name_hashes = &[_]PackageNameHash{ + bun.Semver.String.Builder.stringHash("sharp"), + }; + + fn fromStringArrayGroup(list: *List, expr: *const ast.Expr, allocator: std.mem.Allocator, value: PostinstallOptimizer) !bool { + var array = expr.asArray() orelse return false; + if (array.array.items.len == 0) { + return true; + } + + while (array.next()) |entry| { + if (entry.isString()) { + const str = entry.asString(allocator) orelse continue; + if (str.len == 0) continue; + const hash = bun.Semver.String.Builder.stringHash(str); + try list.dynamic.put(allocator, hash, value); + } + } + + return true; + } + + pub fn fromPackageJSON(list: *List, expr: *const ast.Expr, allocator: std.mem.Allocator) !void { + if (expr.get("nativeDependencies")) |*native_deps_expr| { + list.disable_default_native_binlinks = try fromStringArrayGroup(list, native_deps_expr, allocator, .native_binlink); + } + if (expr.get("ignoreScripts")) |*ignored_scripts_expr| { + list.disable_default_ignore = try fromStringArrayGroup(list, ignored_scripts_expr, allocator, .ignore); + } + } + + pub fn getNativeBinlinkReplacementPackageID( + resolutions: []const PackageID, + metas: []const Meta, + target_cpu: Npm.Architecture, + target_os: Npm.OperatingSystem, + ) ?PackageID { + // Windows needs file extensions. + if (target_os.isMatch(@enumFromInt(Npm.OperatingSystem.win32))) { + return null; + } + + // Loop through the list of optional dependencies with platform-specific constraints + // Find a matching target-specific dependency. + for (resolutions) |resolution| { + if (resolution > metas.len) continue; + const meta: *const Meta = &metas[resolution]; + if (meta.arch == .all or meta.os == .all) continue; + if (meta.arch.isMatch(target_cpu) and meta.os.isMatch(target_os)) { + return resolution; + } + } + + return null; + } + + pub const List = struct { + dynamic: Map = .{}, + disable_default_native_binlinks: bool = false, + disable_default_ignore: bool = false, + + pub const Map = std.ArrayHashMapUnmanaged(PackageNameHash, PostinstallOptimizer, install.ArrayIdentityContext.U64, false); + + pub fn isNativeBinlinkEnabled(this: *const @This()) bool { + if (this.dynamic.count() == 0) { + if (this.disable_default_native_binlinks) { + return true; + } + } + + if (bun.env_var.feature_flag.BUN_FEATURE_FLAG_DISABLE_NATIVE_DEPENDENCY_LINKER.get()) { + return false; + } + + return true; + } + + pub fn shouldIgnoreLifecycleScripts( + this: *const @This(), + name_hash: PackageNameHash, + resolutions: []const PackageID, + metas: []const Meta, + target_cpu: Npm.Architecture, + target_os: Npm.OperatingSystem, + tree_id: ?Lockfile.Tree.Id, + ) bool { + if (bun.env_var.feature_flag.BUN_FEATURE_FLAG_DISABLE_IGNORE_SCRIPTS.get()) { + return false; + } + + const mode = this.get(name_hash) orelse return false; + + return switch (mode) { + .native_binlink => + // TODO: support hoisted. + (tree_id == null or tree_id.? == 0) and + + // It's not as simple as checking `get(name_hash) != null` because if the + // specific versions of the package do not have optional + // dependencies then we cannot do this optimization without + // breaking the code. + // + // This shows up in test/integration/esbuild/esbuild.test.ts + getNativeBinlinkReplacementPackageID(resolutions, metas, target_cpu, target_os) != null, + + .ignore => true, + }; + } + + fn fromDefault(name_hash: PackageNameHash) ?PostinstallOptimizer { + for (default_native_binlinks_name_hashes) |hash| { + if (hash == name_hash) { + return .native_binlink; + } + } + for (default_ignore_name_hashes) |hash| { + if (hash == name_hash) { + return .ignore; + } + } + return null; + } + + pub fn get(this: *const @This(), name_hash: PackageNameHash) ?PostinstallOptimizer { + if (this.dynamic.get(name_hash)) |optimize| { + return optimize; + } + + const default = fromDefault(name_hash) orelse { + return null; + }; + + switch (default) { + .native_binlink => { + if (!this.disable_default_native_binlinks) { + return .native_binlink; + } + }, + .ignore => { + if (!this.disable_default_ignore) { + return .ignore; + } + }, + } + + return null; + } + }; +}; + +const std = @import("std"); + +const bun = @import("bun"); +const ast = bun.ast; + +const install = bun.install; +const ArrayIdentityContext = install.ArrayIdentityContext; +const Lockfile = install.Lockfile; +const Npm = install.Npm; +const PackageID = install.PackageID; +const PackageNameHash = install.PackageNameHash; +const Meta = Lockfile.Package.Meta; diff --git a/test/cli/install/bun-install-native-binlink.test.ts b/test/cli/install/bun-install-native-binlink.test.ts new file mode 100644 index 0000000000..16b3fde67d --- /dev/null +++ b/test/cli/install/bun-install-native-binlink.test.ts @@ -0,0 +1,152 @@ +import { spawn } from "bun"; +import { afterAll, beforeAll, describe, expect, setDefaultTimeout, test } from "bun:test"; +import { rm, writeFile } from "fs/promises"; +import { bunEnv, bunExe, isWindows, VerdaccioRegistry } from "harness"; +import { join } from "path"; + +let verdaccio: VerdaccioRegistry; + +beforeAll(async () => { + setDefaultTimeout(1000 * 60 * 5); + verdaccio = new VerdaccioRegistry(); + await verdaccio.start(); +}); + +afterAll(() => { + verdaccio.stop(); +}); + +describe.skipIf(isWindows).concurrent("native binlink optimization", () => { + for (const linker of ["hoisted", "isolated"]) { + test(`uses platform-specific bin instead of main package bin with linker ${linker}`, async () => { + let env = { ...bunEnv }; + const { packageDir, packageJson } = await verdaccio.createTestDir(); + env.BUN_INSTALL_CACHE_DIR = join(packageDir, ".bun-cache"); + env.BUN_TMPDIR = env.TMPDIR = env.TEMP = join(packageDir, ".bun-tmp"); + + // Create bunfig + await writeFile( + join(packageDir, "bunfig.toml"), + ` +[install] +cache = "${join(packageDir, ".bun-cache").replaceAll("\\", "\\\\")}" +registry = "${verdaccio.registryUrl()}" +linker = "${linker}" +`, + ); + + // Install the main package + await writeFile( + packageJson, + JSON.stringify({ + name: "test-app", + version: "1.0.0", + dependencies: { + "test-native-binlink": "1.0.0", + }, + nativeDependencies: ["test-native-binlink"], + trustedDependencies: ["test-native-binlink"], + }), + ); + + const installProc = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "inherit", + stdin: "ignore", + stderr: "inherit", + env, + }); + + expect(await installProc.exited).toBe(0); + + // Run the bin - it should use the platform-specific one (exit code 0) + // not the main package one (exit code 1) + const binProc = spawn({ + cmd: [join(packageDir, "node_modules", ".bin", "test-binlink-cmd")], + cwd: packageDir, + stdout: "pipe", + stdin: "ignore", + stderr: "inherit", + env, + }); + + const [binStdout, binExitCode] = await Promise.all([binProc.stdout.text(), binProc.exited]); + + // Should exit with 0 (platform-specific) not 1 (main package) + expect(binExitCode).toBe(0); + expect(binStdout).toContain("SUCCESS: Using platform-specific bin"); + + // Now delete the node_modules folder, keep the bun.lock, re-install + await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); + const installProc2 = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "inherit", + stdin: "ignore", + stderr: "inherit", + env, + }); + expect(await installProc2.exited).toBe(0); + + const binProc2 = spawn({ + cmd: [join(packageDir, "node_modules", ".bin", "test-binlink-cmd")], + cwd: packageDir, + stdout: "pipe", + stdin: "ignore", + stderr: "inherit", + env, + }); + const [binStdout2, binExitCode2] = await Promise.all([binProc2.stdout.text(), binProc2.exited]); + expect(binStdout2).toContain("SUCCESS: Using platform-specific bin"); + expect(binExitCode2).toBe(0); + + // Now do a no-op re-install. + const installProc3 = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "inherit", + stdin: "ignore", + stderr: "inherit", + env, + }); + expect(await installProc3.exited).toBe(0); + + const binProc3 = spawn({ + cmd: [join(packageDir, "node_modules", ".bin", "test-binlink-cmd")], + cwd: packageDir, + stdout: "pipe", + stdin: "ignore", + stderr: "inherit", + env, + }); + const [binStdout3, binExitCode3] = await Promise.all([binProc3.stdout.text(), binProc3.exited]); + expect(binStdout3).toContain("SUCCESS: Using platform-specific bin"); + expect(binExitCode3).toBe(0); + + // Now do an install with the .bin folder gone + await rm(join(packageDir, "node_modules", ".bin"), { recursive: true, force: true }); + const installProc4 = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: "inherit", + stdin: "ignore", + stderr: "inherit", + env, + }); + expect(await installProc4.exited).toBe(0); + + const binProc4 = spawn({ + cmd: [join(packageDir, "node_modules", ".bin", "test-binlink-cmd")], + cwd: packageDir, + stdout: "pipe", + stdin: "ignore", + stderr: "inherit", + env, + }); + const [binStdout4, binExitCode4] = await Promise.all([binProc4.stdout.text(), binProc4.exited]); + expect(binStdout4).toContain("SUCCESS: Using platform-specific bin"); + expect(binExitCode4).toBe(0); + }); + } +}); diff --git a/test/cli/install/multi-tool-pkg-1.0.0.tgz b/test/cli/install/multi-tool-pkg-1.0.0.tgz index bfb886d0cb37d2ec8f58ab4c196c77bd474423d1..d8aa0a7462a06654cce9aad6e013b2de8e493c2b 100644 GIT binary patch literal 353 zcmV-n0iOOJiwFQ*;00*_1MQSuZ-OushPn1vblr_66I-N^xXX=yuqC_gv=S6rQqW{E z{`VaeH^0pcV9fd?^d#-+7jNH_^5K++qG#0zA;f14t)jP%#BtZL1W`r_?R)UK2pYne zg;<@3+Azya`^0sO z|C`|Pp*PF)N`4{dNUD)&4VBDPEbLf?Z5(_AFYj-`>j3RO57dT6XvuSdU^drZ9o%_t z-TFR;zj56E)p@_*qW_7%=YP-fsqudkT;+e%PIx4Z?F|Nl;g0zR*!NKR02BZK*bBZq literal 784 zcmV+r1MmDFiwFRlYl~lgrgT_8`k)#w}%5o z=DPV$Dqh`^jYdsb4c|Lfu5>HPn435L{`&gw@gM#uf{O%3@m`)3mE2Q)HokFSto{ie z&_8pyFwjX1Z5blv`nNHv2i3Y-BZAvGM{vR|L2`tN93vSn&_?N1zPw&6eJWTdG6;O@ zD!H!ZKa}!IU&!v#w_@XMIiF!Uw5cm?e7>$ykH#~%eIL?FTlLvE&oNH_ps;x0sb!;!2hM1VgDEX zI51ZKL;g=t0{$-%My&r)$_dVa|C@O_`j0RAkJrEMdV}$wIvC=A5}cW?tSfWXtRK~a z>T=+DwdKa)f%(EbR(`YUx%v>Xu!VJwo+Fs6XFVnLv2fnd&CuOPm+Sc|Sqp{=g_&W& z^Yh!jc%X0oXYC9D7OC4iN1)F!A~?#e#D1|4A@v{Z}x7 zIr#sXr&s^+ME~*nCv3=cx8nV`2X+hbG84EGWy@Z|6?&2|0%X1{wKjy{vR#WWz_}S4*&pg OhxregtwF^AJOBV-^{0sd diff --git a/test/cli/install/registry/packages/create-native-binlink-packages.ts b/test/cli/install/registry/packages/create-native-binlink-packages.ts new file mode 100644 index 0000000000..9d915b6eb7 --- /dev/null +++ b/test/cli/install/registry/packages/create-native-binlink-packages.ts @@ -0,0 +1,155 @@ +#!/usr/bin/env bun +/** + * This script creates test packages for native binlink optimization testing. + * It creates: + * - test-native-binlink: main package with a bin that exits with code 1 + * - test-native-binlink-target: platform-specific package with bin that exits with code 0 + */ + +import { $ } from "bun"; +import { mkdir, writeFile } from "fs/promises"; +import { join } from "path"; + +const packagesDir = import.meta.dir; + +// Main package that should NOT be used +const mainPkgDir = join(packagesDir, "test-native-binlink-tmp"); +await mkdir(mainPkgDir, { recursive: true }); +await mkdir(join(mainPkgDir, "bin"), { recursive: true }); + +await writeFile( + join(mainPkgDir, "package.json"), + JSON.stringify( + { + name: "test-native-binlink", + version: "1.0.0", + bin: { + "test-binlink-cmd": "./bin/main.js", + }, + optionalDependencies: { + "test-native-binlink-target": "1.0.0", + }, + }, + null, + 2, + ), +); + +await writeFile( + join(mainPkgDir, "bin", "main.js"), + `#!/usr/bin/env node +console.log("ERROR: Using main package bin, not platform-specific!"); +process.exit(1); +`, +); + +// Create package structure for tarball +const mainTarDir = join(mainPkgDir, "package"); +await mkdir(mainTarDir, { recursive: true }); +await mkdir(join(mainTarDir, "bin"), { recursive: true }); +await $`cp ${join(mainPkgDir, "package.json")} ${mainTarDir}/`; +await $`cp ${join(mainPkgDir, "bin", "main.js")} ${join(mainTarDir, "bin")}/`; + +// Create tarball +await mkdir(join(packagesDir, "test-native-binlink"), { recursive: true }); +await $`cd ${mainPkgDir} && tar -czf ${join(packagesDir, "test-native-binlink", "test-native-binlink-1.0.0.tgz")} package`; + +// Platform-specific package +const targetPkgDir = join(packagesDir, "test-native-binlink-target-tmp"); +await mkdir(targetPkgDir, { recursive: true }); +await mkdir(join(targetPkgDir, "bin"), { recursive: true }); + +await writeFile( + join(targetPkgDir, "package.json"), + JSON.stringify( + { + name: "test-native-binlink-target", + version: "1.0.0", + os: ["darwin", "linux", "win32"], + cpu: ["arm64", "x64"], + }, + null, + 2, + ), +); + +// Use the SAME filename as the main package! +await writeFile( + join(targetPkgDir, "bin", "main.js"), + `#!/usr/bin/env node +console.log("SUCCESS: Using platform-specific bin (test-native-binlink-target)"); +process.exit(0); +`, +); + +// Create package structure for tarball +const targetTarDir = join(targetPkgDir, "package"); +await mkdir(targetTarDir, { recursive: true }); +await mkdir(join(targetTarDir, "bin"), { recursive: true }); +await $`cp ${join(targetPkgDir, "package.json")} ${targetTarDir}/`; +await $`cp ${join(targetPkgDir, "bin", "main.js")} ${join(targetTarDir, "bin")}/`; + +// Create tarball +await mkdir(join(packagesDir, "test-native-binlink-target"), { recursive: true }); +await $`cd ${targetPkgDir} && tar -czf ${join(packagesDir, "test-native-binlink-target", "test-native-binlink-target-1.0.0.tgz")} package`; + +// Create package.json for verdaccio registry with proper integrity hashes +for (const pkgName of ["test-native-binlink", "test-native-binlink-target"]) { + const version = "1.0.0"; + const tarballName = `${pkgName}-${version}.tgz`; + const tarballPath = join(packagesDir, pkgName, tarballName); + + // Calculate SHA512 integrity hash + const tarballFile = Bun.file(tarballPath); + const tarballBytes = await tarballFile.arrayBuffer(); + const hash = new Bun.CryptoHasher("sha512"); + hash.update(tarballBytes); + const integrity = `sha512-${Buffer.from(hash.digest()).toString("base64")}`; + + // Calculate SHA1 shasum + const sha1Hash = new Bun.CryptoHasher("sha1"); + sha1Hash.update(tarballBytes); + const shasum = Buffer.from(sha1Hash.digest()).toString("hex"); + + await writeFile( + join(packagesDir, pkgName, "package.json"), + JSON.stringify( + { + _id: pkgName, + name: pkgName, + "dist-tags": { + latest: version, + }, + versions: { + [version]: { + name: pkgName, + version, + _id: `${pkgName}@${version}`, + bin: pkgName === "test-native-binlink" ? { "test-binlink-cmd": "./bin/main.js" } : undefined, + optionalDependencies: + pkgName === "test-native-binlink" + ? { + "test-native-binlink-target": "1.0.0", + } + : undefined, + os: pkgName === "test-native-binlink-target" ? ["darwin", "linux", "win32"] : undefined, + cpu: pkgName === "test-native-binlink-target" ? ["arm64", "x64"] : undefined, + dist: { + integrity, + shasum, + tarball: `http://localhost:4873/${pkgName}/-/${tarballName}`, + }, + }, + }, + }, + null, + 2, + ), + ); +} + +// Clean up temp directories +await $`rm -rf ${mainPkgDir}`; +await $`rm -rf ${targetPkgDir}`; + +console.log("✅ Created native binlink test packages"); diff --git a/test/cli/install/registry/packages/test-native-binlink-target/package.json b/test/cli/install/registry/packages/test-native-binlink-target/package.json new file mode 100644 index 0000000000..1445f333ac --- /dev/null +++ b/test/cli/install/registry/packages/test-native-binlink-target/package.json @@ -0,0 +1,28 @@ +{ + "_id": "test-native-binlink-target", + "name": "test-native-binlink-target", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "test-native-binlink-target", + "version": "1.0.0", + "_id": "test-native-binlink-target@1.0.0", + "os": [ + "darwin", + "linux", + "win32" + ], + "cpu": [ + "arm64", + "x64" + ], + "dist": { + "integrity": "sha512-CVsB3TKHbqr8o3/S5L4EnpFTZjlhGvdry2mF6mZvaUHYvZ8wRUMaJJObkDAXN1+zvuPAUdK4585DBrOpPe/vwA==", + "shasum": "d436fc61b25745d517ddfd117461fb5da7406679", + "tarball": "http://localhost:4873/test-native-binlink-target/-/test-native-binlink-target-1.0.0.tgz" + } + } + } +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/test-native-binlink-target/test-native-binlink-target-1.0.0.tgz b/test/cli/install/registry/packages/test-native-binlink-target/test-native-binlink-target-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2bbe281135d913ed74239b3527c1350ed1abbe4c GIT binary patch literal 374 zcmV-+0g3(}iwFSl%LQow1MQScPQx$|hP%!wjM=mjNu7sXsVpl_P<4e^nAC~fCU#^e zr4r)qOw$BVMLP#VGSMb?Z6RWr$QZ#?H0?JH*{KF8~k#0P}FP!~g&Q literal 0 HcmV?d00001 diff --git a/test/cli/install/registry/packages/test-native-binlink/package.json b/test/cli/install/registry/packages/test-native-binlink/package.json new file mode 100644 index 0000000000..a63957b507 --- /dev/null +++ b/test/cli/install/registry/packages/test-native-binlink/package.json @@ -0,0 +1,25 @@ +{ + "_id": "test-native-binlink", + "name": "test-native-binlink", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "test-native-binlink", + "version": "1.0.0", + "_id": "test-native-binlink@1.0.0", + "bin": { + "test-binlink-cmd": "./bin/main.js" + }, + "optionalDependencies": { + "test-native-binlink-target": "1.0.0" + }, + "dist": { + "integrity": "sha512-1PqDobvLsa0lFqxrns17u0yfmjgLQeuST8PWNxov09gs+yEm2xoBc5yXx5B6NFcXOY6lu5A62cRgFW8FSOgpJQ==", + "shasum": "da15dad1eae537c1341ca87b04d8ae7a52be5e4a", + "tarball": "http://localhost:4873/test-native-binlink/-/test-native-binlink-1.0.0.tgz" + } + } + } +} \ No newline at end of file diff --git a/test/cli/install/registry/packages/test-native-binlink/test-native-binlink-1.0.0.tgz b/test/cli/install/registry/packages/test-native-binlink/test-native-binlink-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..788613a4d0c07cee1c31120011ca702d83b1c6a5 GIT binary patch literal 381 zcmV-@0fPP?iwFSl%LQow1MQUGOT#b_$M@X7BC9VG)-G{P2lG{*g@J!SY_BC-o0KLS zg6x0y+GRS2Q;_{Y?dRn}xVv2Py-OvMg~(ylJ0X@y61dXk#6OaYorV;**#X zT+o>J2tQ>}Tb9O(8cUgrx`yb+R7#f&THh)Bh}N^>;R$ z8?BCg2EIoeMThmjnM8Z~^D&QmgdV$)&-my1zj%cBN|fMFiEqK!KnW{X5a5SUNVV{X zjl&9RBeiOLSV%*<*+m!JX)+KO>w@{Z>jL750^ne(N?)R~D0r;tncMDX2t( z*-P}XhyW$Z!M-2gr1I96e!IWG?*H$EI{F#k%|DL&{`@o6&Hq`r>W`MDZsiBHB1)&= zC0ZF>Kv?K};NQ { setDefaultTimeout(1000 * 60 * 5); }); -describe("esbuild integration test", () => { +describe.concurrent("esbuild integration test", () => { test("install and use esbuild", async () => { - const packageDir = tmpdirSync(); - - await writeFile( - join(packageDir, "package.json"), - JSON.stringify({ + using dir = tempDir("esbuild-test", { + "package.json": JSON.stringify({ name: "bun-esbuild-test", version: "1.0.0", }), - ); + }); + const packageDir = dir + ""; var { stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install", "esbuild@0.19.8"], @@ -52,17 +50,15 @@ describe("esbuild integration test", () => { }); test("install and use estrella", async () => { - const packageDir = tmpdirSync(); - - await writeFile( - join(packageDir, "package.json"), - JSON.stringify({ + using dir = tempDir("esbuild-estrella-test", { + "package.json": JSON.stringify({ name: "bun-esbuild-estrella-test", version: "1.0.0", }), - ); + }); + const packageDir = dir + ""; - var { stdout, stderr, exited } = spawn({ + let { stdout, stderr, exited } = spawn({ cmd: [bunExe(), "install", "estrella@1.4.1"], cwd: packageDir, stdout: "pipe", @@ -70,12 +66,14 @@ describe("esbuild integration test", () => { stderr: "pipe", env, }); + let exitCode = 0; + let err = ""; + let out = ""; - var err = await stderr.text(); - var out = await stdout.text(); + [err, out, exitCode] = await Promise.all([new Response(stderr).text(), new Response(stdout).text(), exited]); expect(err).toContain("Saved lockfile"); expect(out).toContain("estrella@1.4.1"); - expect(await exited).toBe(0); + expect(exitCode).toBe(0); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "estrella", "--estrella-version"], @@ -86,11 +84,10 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); + [err, out, exitCode] = await Promise.all([new Response(stderr).text(), new Response(stdout).text(), exited]); expect(err).toBe(""); expect(out).toContain("1.4.1"); - expect(await exited).toBe(0); + expect(exitCode).toBe(0); await cp(join(import.meta.dir, "build-file.js"), join(packageDir, "build-file.js")); @@ -103,11 +100,7 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); - expect(err).toBe(""); - expect(out).toBe('console.log("hello"),console.log("estrella");\n'); - expect(await exited).toBe(0); + [err, out, exitCode] = await Promise.all([stderr.text(), stdout.text(), exited]); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); await rm(join(packageDir, "bun.lockb"), { force: true }); @@ -134,12 +127,11 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); + [err, out, exitCode] = await Promise.all([stderr.text(), stdout.text(), exited]); expect(err).toContain("Saved lockfile"); expect(out).toContain("estrella@1.4.1"); expect(out).toContain("esbuild@0.19.8"); - expect(await exited).toBe(0); + expect(exitCode).toBe(0); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "estrella", "--estrella-version"], @@ -150,11 +142,10 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); + [err, out, exitCode] = await Promise.all([stderr.text(), stdout.text(), exited]); expect(err).toBe(""); expect(out).toContain("1.4.1"); - expect(await exited).toBe(0); + expect(exitCode).toBe(0); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "esbuild", "--version"], @@ -165,11 +156,10 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); + [err, out, exitCode] = await Promise.all([stderr.text(), stdout.text(), exited]); expect(err).toBe(""); expect(out).toContain("0.19.8"); - expect(await exited).toBe(0); + expect(exitCode).toBe(0); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "esbuild", "--version"], @@ -180,10 +170,10 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); + [err, out, exitCode] = await Promise.all([stderr.text(), stdout.text(), exited]); expect(err).toBe(""); expect(out).toContain("0.11.23"); + expect(exitCode).toBe(0); ({ stdout, stderr, exited } = spawn({ cmd: [bunExe(), "estrella", "build-file.js"], @@ -194,10 +184,9 @@ describe("esbuild integration test", () => { env, })); - err = await stderr.text(); - out = await stdout.text(); + [err, out, exitCode] = await Promise.all([stderr.text(), stdout.text(), exited]); expect(err).toBe(""); expect(out).toBe('console.log("hello"),console.log("estrella");\n'); - expect(await exited).toBe(0); + expect(exitCode).toBe(0); }); });