[
  {
    "path": ".changeset/config.json",
    "content": "{\n\t\"$schema\": \"https://unpkg.com/@changesets/config@3.1.1/schema.json\",\n\t\"changelog\": [\"@svitejs/changesets-changelog-github-compact\", { \"repo\": \"jsrepojs/jsrepo\" }],\n\t\"commit\": false,\n\t\"fixed\": [],\n\t\"linked\": [],\n\t\"access\": \"public\",\n\t\"baseBranch\": \"main\",\n\t\"updateInternalDependencies\": \"patch\",\n\t\"ignore\": [\"@jsrepo/docs\", \"@example/react\", \"@example/svelte\"],\n\t\"prettier\": false\n}\n"
  },
  {
    "path": ".github/workflows/build-example-registries.yml",
    "content": "name: build-example-registries\n\non:\n    pull_request:\n        branches: [main]\n\njobs:\n    build-example-registries:\n        runs-on: ubuntu-latest\n\n        steps:\n            - uses: actions/checkout@v4\n            - uses: pnpm/action-setup@v4\n            - uses: actions/setup-node@v4\n              with:\n                  node-version: \"20\"\n                  cache: pnpm\n\n            - name: Install dependencies\n              run: pnpm install\n\n            - name: Build example registries\n              run: pnpm build:example-registries"
  },
  {
    "path": ".github/workflows/bundle-analyze.yml",
    "content": "name: Bundle Analyze\n\non:\n    pull_request:\n        branches: [next, main]\n\njobs:\n    bundle-analyze:\n        runs-on: ubuntu-latest\n        permissions:\n            pull-requests: write\n\n        steps:\n            - uses: actions/checkout@v4\n            - uses: pnpm/action-setup@v4\n            - uses: actions/setup-node@v4\n              with:\n                  node-version: \"20\"\n                  cache: pnpm\n\n            - name: Install dependencies\n              run: pnpm install\n\n            - name: Build packages\n              run: pnpm build:packages\n\n            - name: Bundle Analyze\n              run: |\n                  pnpm bundle-analyze\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n    pull_request:\n        branches: [main]\n\njobs:\n    CI:\n        runs-on: ubuntu-latest\n\n        steps:\n            - uses: actions/checkout@v4\n            - uses: pnpm/action-setup@v4\n            - uses: actions/setup-node@v4\n              with:\n                  node-version: \"20\"\n                  cache: pnpm\n\n            - name: Install dependencies\n              run: pnpm install\n\n            - name: Check Types\n              run: pnpm check\n\n            - name: Test\n              run: pnpm test"
  },
  {
    "path": ".github/workflows/cli-preview.yml",
    "content": "name: Package Previews\non:\n    pull_request:\n        branches: [main]\n        paths:\n            - 'pnpm-lock.yaml'\n            - 'packages/**'\n\njobs:\n  release-previews:\n    runs-on: ubuntu-latest\n\n    steps:\n        - uses: actions/checkout@v4\n        - uses: pnpm/action-setup@v4\n        - uses: actions/setup-node@v4\n          with:\n              node-version: \"20\"\n              cache: pnpm\n\n        - name: Install dependencies\n          run: pnpm install # packages automatically built on postinstall\n\n        - name: publish preview\n          run: pnpm pkg-pr-new publish --pnpm './packages/jsrepo' './packages/mcp' './packages/transform-prettier' './packages/transform-biome' './packages/transform-javascript' './packages/migrate' './packages/transform-oxfmt' './packages/transform-filecasing' --packageManager=pnpm\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n    push:\n        branches:\n            - main\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}\n\njobs:\n    release:\n        permissions:\n            contents: write # to create release (changesets/action)\n            pull-requests: write # to create pull request (changesets/action)\n            id-token: write # Required for OIDC\n        name: Build & Publish Release\n        runs-on: ubuntu-latest\n\n        steps:\n            - uses: actions/checkout@v4\n              with:\n                # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits\n                fetch-depth: 0\n            - uses: pnpm/action-setup@v4\n            - uses: actions/setup-node@v4\n              with:\n                node-version: 24\n                cache: 'pnpm'\n\n            - run: npm install -g npm@latest\n\n            - name: Install dependencies\n              run: pnpm install\n\n            - name: Create Release Pull Request or Publish to npm\n              id: changesets\n              uses: changesets/action@v1\n              with:\n                commit: \"chore(release): version package\"\n                title: \"chore(release): version package\"\n                publish: pnpm ci:release\n              env:\n                GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n                NPM_CONFIG_PROVENANCE: true"
  },
  {
    "path": ".github/workflows/test-external-projects.yml",
    "content": "name: Test External Projects\n\non:\n    pull_request:\n        branches: [main]\n\njobs:\n    test-external-projects:\n        runs-on: ubuntu-latest\n        strategy:\n            matrix:\n                project:\n                    - name: shadcn-svelte-extras\n                      repo: ieedan/shadcn-svelte-extras\n                      pm: pnpm\n                      build: pnpm registry:build\n                    - name: react-bits\n                      repo: DavidHDev/react-bits\n                      pm: npm\n                      build: npm run registry:build\n\n        steps:\n            - name: Checkout jsrepo\n              uses: actions/checkout@v4\n\n            - name: Setup pnpm\n              uses: pnpm/action-setup@v4\n\n            - name: Setup Node.js\n              uses: actions/setup-node@v4\n              with:\n                  node-version: \"20\"\n                  cache: pnpm\n\n            - name: Install jsrepo dependencies\n              run: pnpm install\n\n            - name: Build jsrepo\n              run: pnpm build:packages\n\n            - name: Pack jsrepo\n              id: pack\n              run: |\n                  cd packages/jsrepo && pnpm pack\n                  TARBALL=\"$(ls jsrepo-*.tgz)\"\n                  echo \"tarball_path=${{ github.workspace }}/packages/jsrepo/$TARBALL\" >> $GITHUB_OUTPUT\n\n            - name: Clone ${{ matrix.project.name }}\n              uses: actions/checkout@v4\n              with:\n                  repository: ${{ matrix.project.repo }}\n                  path: external-project\n\n            - name: Cache ${{ matrix.project.name }} dependencies\n              uses: actions/cache@v4\n              with:\n                  path: external-project/node_modules\n                  key: ${{ matrix.project.name }}-deps-${{ hashFiles('external-project/package-lock.json', 'external-project/pnpm-lock.yaml') }}\n\n            - name: Install local jsrepo in ${{ matrix.project.name }}\n              working-directory: external-project\n              env:\n                  TARBALL: ${{ steps.pack.outputs.tarball_path }}\n              run: |\n                  if [ \"${{ matrix.project.pm }}\" = \"pnpm\" ]; then\n                      pnpm add \"jsrepo@file:$TARBALL\"\n                  else\n                      npm install \"jsrepo@file:$TARBALL\"\n                  fi\n\n            - name: Build ${{ matrix.project.name }} registry\n              working-directory: external-project\n              run: ${{ matrix.project.build }}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n\n# out\ndist\n\n# testing\ntemp-test\n**/tests/fixtures/**/node_modules\n**/tests/fixtures/**/package-lock.json\n**/tests/fixtures/**/pnpm-lock.yaml\n\n.DS_Store\n# ralphex progress logs\nprogress*.txt\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n\t\"cSpell.words\": [\"fuzzysort\", \"onwarn\", \"packlist\"]\n}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to jsrepo\n\nLooking to contribute to jsrepo? Great! You're in the right place. This guide will help you get started.\n\n## AI Assistance Notice\n\n> [!IMPORTANT]\n>\n> If you are using **any kind of AI assistance** to contribute to jsrepo,\n> it must be disclosed in the pull request.\n\nIf you are using any kind of AI assistance while contributing to jsrepo,\n**this must be disclosed in the pull request**, along with the extent to\nwhich AI assistance was used (e.g. docs only vs. code generation).\nIf PR responses are being generated by an AI, disclose that as well.\nAs a small exception, trivial tab-completion doesn't need to be disclosed,\nso long as it is limited to single keywords or short phrases.\n\nAn example disclosure:\n\n> This PR was written primarily by Claude Code.\n\nOr a more detailed disclosure:\n\n> I consulted ChatGPT to understand the codebase but the solution\n> was fully authored manually by myself.\n\nFailure to disclose this is first and foremost rude to the human operators\non the other end of the pull request, but it also makes it difficult to\ndetermine how much scrutiny to apply to the contribution.\n\nWhen using AI assistance, we expect contributors to understand the code\nthat is produced and be able to answer critical questions about it. It\nisn't a maintainers job to review a PR so broken that it requires\nsignificant rework to be acceptable.\n\nPlease be respectful to maintainers and disclose AI assistance.\n\n## Development\n\nFirst install dependencies:\n\n```sh\npnpm install\n```\n\nThen run the dev script:\n\n```sh\npnpm dev\n```\n\nThis will start the development server and watch for changes to packages.\n\nThe docs are available at [http://localhost:3000](http://localhost:3000).\n\nTo run the tests, run the following command:\n\n```sh\npnpm test\n```\n\nTo test jsrepo manually you can use the playground projects in the [`playground/`](./playground/) directory.\n\n### Project Structure\n\n- `packages/jsrepo` - jsrepo CLI\n- `packages/mcp` - MCP server for jsrepo\n- `playground/` - Playground projects to manually test jsrepo\n- `apps/docs` - Contains the documentation hosted at [https://jsrepo.dev](https://jsrepo.dev).\n\n## Before opening a PR\n\n### Open an issue\n\nIf your PR is more than a one line fix please open an issue first to discuss the changes you want to make.\n\n### Checklist\n\n- Run `pnpm format`\n- Run `pnpm lint` (Ensure passing)\n- Run `pnpm check` (Ensure passing)\n- Run `pnpm test` (Ensure passing)\n- Run `pnpm changeset` and add a changeset for your changes if it effects any of the released packages (under packages/*)"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 jsrepo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<img width=\"100%\" alt=\"CleanShot 2025-11-24 at 09 35 57\" src=\"https://github.com/user-attachments/assets/61899098-fbc9-4263-972f-aacdfe0976de\" />\n\n[![npm version](https://flat.badgen.net/npm/v/jsrepo?color=yellow)](https://npmjs.com/package/jsrepo)\n[![npm downloads](https://flat.badgen.net/npm/dm/jsrepo?color=yellow)](https://npmjs.com/package/jsrepo)\n\n# jsrepo \n\nThe modern registry toolchain. Distribute your code in any language anywhere with one CLI. \n\n## Get Started\n\n```sh\nnpx jsrepo init\n```\n"
  },
  {
    "path": "apps/docs/.gitignore",
    "content": "# deps\n/node_modules\n\n# generated content\n.contentlayer\n.content-collections\n.source\n\n# test & build\n/coverage\n/.next/\n/out/\n/build\n*.tsbuildinfo\n\n# misc\n.DS_Store\n*.pem\n/.pnp\n.pnp.js\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# others\n.env*.local\n.vercel\nnext-env.d.ts\n\npublic/registry-kit\n"
  },
  {
    "path": "apps/docs/README.md",
    "content": "# docs\n\nThis is a Next.js application generated with\n[Create Fumadocs](https://github.com/fuma-nama/fumadocs).\n\nRun development server:\n\n```bash\nnpm run dev\n# or\npnpm dev\n# or\nyarn dev\n```\n\nOpen http://localhost:3000 with your browser to see the result.\n\n## Explore\n\nIn the project, you can see:\n\n- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.\n- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep.\n\n| Route                     | Description                                            |\n| ------------------------- | ------------------------------------------------------ |\n| `app/(home)`              | The route group for your landing page and other pages. |\n| `app/docs`                | The documentation layout and pages.                    |\n| `app/api/search/route.ts` | The Route Handler for search.                          |\n\n### Fumadocs MDX\n\nA `source.config.ts` config file has been included, you can customise different options like frontmatter schema.\n\nRead the [Introduction](https://fumadocs.dev/docs/mdx) for further details.\n\n## Learn More\n\nTo learn more about Next.js and Fumadocs, take a look at the following\nresources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js\n  features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs\n"
  },
  {
    "path": "apps/docs/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": []\n\t}\n}\n"
  },
  {
    "path": "apps/docs/cli.json",
    "content": "{\n  \"aliases\": {\n    \"uiDir\": \"./components/ui\",\n    \"componentsDir\": \"./components\",\n    \"blockDir\": \"./components\",\n    \"cssDir\": \"./styles\",\n    \"libDir\": \"./lib\"\n  },\n  \"baseDir\": \"src\",\n  \"commands\": {}\n}"
  },
  {
    "path": "apps/docs/content/docs/cli/add.mdx",
    "content": "---\ntitle: add\ndescription: Add items from registries to your project.\n---\n\n```sh\njsrepo add\n```\n\n## Usage\n\nAdd an item from the registries in your `jsrepo.config` file:\n\n```sh\njsrepo add button\n```\n\nIf the item exists in multiple registries you will be prompted to select which one you want to add:\n\n```\n◆  Multiple registries contain button. Please select one:\n│  ● button (@ieedan/shadcn-svelte-extras)\n│  ○ button (@ai/elements)\n└\n```\n\nAdd an item from a specific registry:\n\n```sh\njsrepo add @ieedan/shadcn-svelte-extras/button\n```\n\nAdd all items from a specific registry:\n\n```sh\njsrepo add @ieedan/shadcn-svelte-extras --all\n```\n\nAdd all items from every registry in your `jsrepo.config` file:\n\n```sh\njsrepo add --all\n```\n\n## Options\n\n### `--all`\n\nAdd all items from every registry:\n\n```sh\njsrepo add --all\n```\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo add button --cwd ./my-project\n```\n\n### `--expand`\n\nExpands the diff so you see the entire file:\n\n```sh\njsrepo add button --expand\n```\n\n### `--max-unchanged`\n\nMaximum unchanged lines that will show without being collapsed:\n\n```sh\njsrepo add button --max-unchanged 20\n```\n\n### `--overwrite`\n\nOverwrite files without prompting:\n\n```sh\njsrepo add button --overwrite\n```\n\n### `--registry`\n\nThe registry to add items from:\n\n```sh\njsrepo add button --registry @ieedan/shadcn-svelte-extras\n```\n\n### `--verbose`\n\nInclude debug logs:\n\n```sh\njsrepo add button --verbose\n```\n\n### `--with`\n\nInclude files with the given roles:\n\n```sh\njsrepo add --with example test storybook\n```\n\n### `--with-docs` (Deprecated)\n\nUse `--with doc`:\n\n```sh\njsrepo add --with-docs\n```\n\n### `--with-examples` (Deprecated)\n\nUse `--with example`:\n\n```sh\njsrepo add --with-examples\n```\n\n### `--with-tests` (Deprecated)\n\nUse `--with test`:\n\n```sh\njsrepo add --with-tests\n```\n\n### `--yes`\n\nSkip the confirmation prompt:\n\n```sh\njsrepo add button --yes\n```\n"
  },
  {
    "path": "apps/docs/content/docs/cli/auth.mdx",
    "content": "---\ntitle: auth\ndescription: Authenticate to a provider or registry.\n---\n\n```sh\njsrepo auth\n```\n\n## Usage\n\nAuthenticate to a provider. You will be prompted to select a provider if multiple are available:\n\n```sh\njsrepo auth\n```\n\nAuthenticate to a specific provider:\n\n```sh\njsrepo auth github\n```\n\nIf the provider requires registry-specific authentication, you will be prompted to select a registry:\n\n```\n◆  Select a registry to authenticate to.\n│  ● @ieedan/shadcn-svelte-extras\n│  ○ @ai/elements\n│  ○ Other\n└\n```\n\nLogout from a provider:\n\n```sh\njsrepo auth github --logout\n```\n\nIf the provider uses registry-specific tokens, you will be prompted to select which registry to logout from:\n\n```\n◆  Select a registry to logout of.\n│  ● @ieedan/shadcn-svelte-extras\n│  ○ @ai/elements\n└\n```\n\nAuthenticate using a token directly:\n\n```sh\njsrepo auth github --token <your-token>\n```\n\nAuthenticate non-interactively for a specific registry:\n\n```sh\njsrepo auth --registry https://private-registry.example.com --token <your-token>\n```\n\n## Options\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo auth --cwd ./my-project\n```\n\n### `--logout`\n\nExecute the logout flow:\n\n```sh\njsrepo auth github --logout\n```\n\n### `--registry`\n\nThe registry to authenticate to. This is especially useful in non-interactive environments where prompts are unavailable:\n\n```sh\njsrepo auth --registry https://private-registry.example.com --token <your-token>\n```\n\n### `--token`\n\nThe token to use for authenticating to this provider:\n\n```sh\njsrepo auth github --token <your-token>\n```\n\n### `--verbose`\n\nInclude debug logs:\n\n```sh\njsrepo auth github --verbose\n```\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/build.mdx",
    "content": "---\ntitle: build\ndescription: Build your registry.\n---\n\n```sh\njsrepo build\n```\n\n## Usage\n\nBuild all blocks from the registries in your `jsrepo.config` file:\n\n```sh\njsrepo build\n```\n\nBuild blocks and watch for changes:\n\n```sh\njsrepo build --watch\n```\n\n## Options\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo build --cwd ./my-project\n```\n\n### `--debounce`, `-d`\n\nHow long to wait before building again after a change is detected (watch mode only):\n\n```sh\njsrepo build --watch --debounce 200\n```\n\n### `--watch`, `-w`\n\nWatch for changes and rebuild automatically:\n\n```sh\njsrepo build --watch\n```\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/config/language.mdx",
    "content": "---\ntitle: language\ndescription: Add a language to your config.\n---\n\n```sh\njsrepo config language\n```\n\n## Usage\n\nAdd a language to your config. You will be prompted to install dependencies after adding:\n\n```sh\njsrepo config language jsrepo-language-go\n```\n\nAdd multiple languages at once:\n\n```sh\njsrepo config language jsrepo-language-go jsrepo-language-rust\n```\n\n## Options\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo config language jsrepo-language-go --cwd ./my-project\n```\n\n### `--yes`\n\nSkip the confirmation prompt:\n\n```sh\njsrepo config language jsrepo-language-go --yes\n```\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/config/mcp.mdx",
    "content": "---\ntitle: mcp\ndescription: Configure the jsrepo MCP server for your environment.\n---\n\n```sh\njsrepo config mcp\n```\n\n## Usage\n\nConfigure the jsrepo MCP server. You will be prompted to select which clients to configure:\n\n```sh\njsrepo config mcp\n```\n\nYou will be prompted to select which clients you want to configure:\n\n```\n◆  Which clients would you like to configure?\n│  ■ Cursor\n│  □ Claude Code\n│  □ VS Code\n│  □ Codex\n└\n```\n\nConfigure a specific client:\n\n```sh\njsrepo config mcp --client cursor\n```\n\nConfigure multiple clients:\n\n```sh\njsrepo config mcp --client cursor vscode\n```\n\nConfigure all supported MCP clients:\n\n```sh\njsrepo config mcp --all\n```\n\n## Options\n\n### `--all`\n\nConfigure all supported MCP clients:\n\n```sh\njsrepo config mcp --all\n```\n\n### `--client`\n\nThe MCP client(s) to configure. Supported clients: `cursor`, `claude`, `vscode`, `codex`:\n\n```sh\njsrepo config mcp --client cursor\n```\n\nConfigure multiple clients:\n\n```sh\njsrepo config mcp --client cursor vscode\n```\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo config mcp --cwd ./my-project\n```\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/config/meta.json",
    "content": "{\n  \"title\": \"config\"\n}\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/config/provider.mdx",
    "content": "---\ntitle: provider\ndescription: Add a provider to your config.\n---\n\n```sh\njsrepo config provider\n```\n\n## Usage\n\nAdd a provider to your config. You will be prompted to install dependencies after adding:\n\n```sh\njsrepo config provider jsrepo-provider-jsr\n```\n\nAdd multiple providers at once:\n\n```sh\njsrepo config provider jsrepo-provider-jsr jsrepo-provider-npm\n```\n\n## Options\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo config provider jsrepo-provider-jsr --cwd ./my-project\n```\n\n### `--yes`\n\nSkip the confirmation prompt:\n\n```sh\njsrepo config provider jsrepo-provider-jsr --yes\n```\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/config/transform.mdx",
    "content": "---\ntitle: transform\ndescription: Add a transform to your config.\n---\n\n```sh\njsrepo config transform\n```\n\n## Usage\n\nAdd a transform to your config. You will be prompted to install dependencies after adding:\n\n```sh\njsrepo config transform @jsrepo/transform-prettier\n```\n\nAdd multiple transforms at once:\n\n```sh\njsrepo config transform @jsrepo/transform-prettier @jsrepo/transform-biome\n```\n\n## Options\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo config transform @jsrepo/transform-prettier --cwd ./my-project\n```\n\n### `--yes`\n\nSkip the confirmation prompt:\n\n```sh\njsrepo config transform @jsrepo/transform-prettier --yes\n```\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/meta.json",
    "content": "{\n  \"title\": \"CLI\"\n}\n\n"
  },
  {
    "path": "apps/docs/content/docs/cli/publish.mdx",
    "content": "---\ntitle: publish\ndescription: Publish your registry to jsrepo.com.\n---\n\n```sh\njsrepo publish\n```\n\n## Usage\n\nPublish all registries in your `jsrepo.config` file:\n\n```sh\njsrepo publish\n```\n\nPublish a specific registry:\n\n```sh\njsrepo publish @jsrepo/playground\n```\n\nPublish in dry run mode:\n\n```sh\njsrepo publish --dry-run\n```\n\n## Options\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo build --cwd ./my-project\n```\n\n### `--dry-run`\n\nPublish in dry run mode. This will check with jsrepo.com to see if the registry can be published in it's current state:\n\n```sh\njsrepo publish --dry-run\n```\n\n### `--verbose`\n\nInclude debug logs:\n\n```sh\njsrepo publish --verbose\n```"
  },
  {
    "path": "apps/docs/content/docs/cli/update.mdx",
    "content": "---\ntitle: update\ndescription: Update items in your project.\n---\n\n```sh\njsrepo update\n```\n\n## Usage\n\nUpdate an item in your project:\n\n```sh\njsrepo update button\n```\n\nSelect which items from your project that you want to update:\n\n```sh\njsrepo update\n```\n\n```\n◆  Which items would you like to update?\n│  ■ button\n│  □ math\n│  □ logger\n└\n```\n\nUpdate an item from a specific registry:\n\n```sh\njsrepo update @ieedan/shadcn-svelte-extras/button\n```\n\nUpdate all items in your project:\n\n```sh\njsrepo update --all\n```\n\n## Options\n\n### `--all`\n\nUpdate all items in your project:\n\n```sh\njsrepo update --all\n```\n\n### `--cwd`\n\nThe current working directory:\n\n```sh\njsrepo update button --cwd ./my-project\n```\n\n### `--expand`\n\nExpands the diff so you see the entire file:\n\n```sh\njsrepo update button --expand\n```\n\n### `--max-unchanged`\n\nMaximum unchanged lines that will show without being collapsed:\n\n```sh\njsrepo update button --max-unchanged 20\n```\n\n### `--overwrite`\n\nOverwrite files without prompting:\n\n```sh\njsrepo update button --overwrite\n```\n\n### `--registry`\n\nThe registry to update items from:\n\n```sh\njsrepo update button --registry @ieedan/shadcn-svelte-extras\n```\n\n### `--verbose`\n\nInclude debug logs:\n\n```sh\njsrepo update button --verbose\n```\n\n### `--with`\n\nInclude files with the given roles:\n\n```sh\njsrepo update --with example test storybook\n```\n\n### `--with-docs` (Deprecated)\n\nUse `--with doc`:\n\n```sh\njsrepo update --with-docs\n```\n\n### `--with-examples` (Deprecated)\n\nUse `--with example`:\n\n```sh\njsrepo update --with-examples\n```\n\n### `--with-tests` (Deprecated)\n\nUse `--with test`:\n\n```sh\njsrepo update --with-tests\n```\n\n### `--yes`\n\nSkip the confirmation prompt:\n\n```sh\njsrepo update button --yes\n```\n"
  },
  {
    "path": "apps/docs/content/docs/create-a-registry.mdx",
    "content": "---\ntitle: Create a registry\ndescription: A complete guide to creating your own registry with jsrepo.\n---\n\nIn this guide we will show you how to setup a registry with **jsrepo**.\n\n## Creating a registry\n\nTo create a registry start by running:\n\n```npm\nnpx jsrepo init\n```\n\nThis will initialize a blank config in your project and install **jsrepo** as a dev dependency.\n\nBefore we continue let's create some items for our registry.\n\n```ts tab=\"src/stdout.ts\"\nexport function print(msg: string) {\n    console.log(msg);\n}\n```\n\n```ts tab=\"src/logger.ts\"\nimport { print } from './stdout';\nexport function createLogger() {\n    return {\n        log: print,\n    }\n}\n```\n\nNext we can configure the registry with the `registry` key.\n\nLet's start by giving the registry a name:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\tname: 'my-first-registry', // [!code ++]\n\t},\n});\n```\n\nNext let's add the items we just created to the registry:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n        items: [ // [!code ++]\n            { // [!code ++]\n                name: 'logger', // [!code ++]\n                type: 'utils', // [!code ++]\n                files: [ // [!code ++]\n                    { // [!code ++]\n                        path: 'src/logger.ts', // [!code ++]\n                    }, // [!code ++]\n                ] // [!code ++]\n            }, // [!code ++]\n            { // [!code ++]\n                name: 'stdout', // [!code ++]\n                type: 'utils', // [!code ++]\n                add: 'when-needed', // [!code ++] this will prevent the item from being listed by the `add` command\n                files: [ // [!code ++]\n                    { // [!code ++]\n                        path: 'src/stdout.ts', // [!code ++]\n                    }, // [!code ++]\n                ] // [!code ++]\n            } // [!code ++]\n        ], // [!code ++]\n    }\n});\n```\n\nFor now we will use the `repository` output for our registry so let's add it to our config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\toutputs: [repository()], // [!code ++]\n\t},\n});\n```\n\nNow we can build our registry with the `jsrepo build` command:\n\n```sh\njsrepo build\n```\n\nThis will create a `registry.json` file at the root of our project that contains everything we need to start adding items from our registry to other projects.\n\n### Testing the registry\n\nTo test our registry locally we can use the `fs` provider. Let's add it to our config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\";\nimport { fs } from \"jsrepo/providers\"; // [!code ++]\n\nexport default defineConfig({\n    // ...\n\tproviders: [fs()], // [!code ++]\n});\n```\n\nNow let's initialize our registry with the `jsrepo init` command:   \n\n```sh\njsrepo init fs://./\n```\n\nThis will add the registry to the `registries` key in our config file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\";\nimport { fs } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\t// ...\n\tregistries: [\"fs://./\"], // [!code ++]\n});\n```\n\nNow we can run `jsrepo add` to add an item to our project:\n\n```sh\njsrepo add logger\n```\n\n## Deploying your registry\n\nThis is the end of the basic guide.\n\nNow that you have a working registry you can deploy it wherever you want! Take a look at the [providers](/docs/providers) docs for the full list of hosting options. \n\nFor more advanced usage you can continue reading below...\n\n## Advanced Usage\n\nNow that we have covered the basics of creating a registry we can start to explore some of the features that make **jsrepo** so powerful.\n\n### Resolving Dependencies\n\n**jsrepo** automatically resolves dependencies both to other items in the registry and to remote packages. This ensures that when building your registry it will work out of the box for end users.\n\n#### Excluding dependencies\n\nMany times you may not want certain dependencies to be installed with your registry items. For instance if you import `useState` from `react` you probably don't want to force users to install `react` with your registry.\n\nFor this you can use the `excludeDeps` key of your registry config.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\texcludeDeps: [\"react\"], // [!code ++]\n\t},\n});\n```\n\n<Callout type=\"info\">\n    It's good practice to put your framework in the `excludeDeps` list whether that be `react`, `vue`, `svelte` etc.\n</Callout>\n\n#### Opting out of automatic dependency resolution\n\nOccasionally you may want to opt out of automatic dependency resolution for a particular item or file.\n\nTo do this you can set the `dependencyResolution` key to `manual` on the item or on specific files within the item:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\t// ...\n\t\t\t\tdependencyResolution: \"manual\", // [!code ++]\n\t\t\t}\n\t\t]\n\t},\n});\n```\n\n#### Resolving `workspace:` and `catalog:` dependencies\n\nBy default `workspace:` and `catalog:` dependencies won't be handled by **jsrepo**, however if you are using `pnpm` or `bun` you can use the `@jsrepo/pnpm` or `@jsrepo/bun` packages to automatically resolve these dependencies.\n\npnpm:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { pnpm } from \"@jsrepo/pnpm\"; // [!code ++]\n\nexport default defineConfig({\n    // ...\n\tbuild: {\n\t\tremoteDependencyResolver: pnpm(), // [!code ++]\n\t},\n});\n```\n\nbun:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { bun } from \"@jsrepo/bun\"; // [!code ++]\n\nexport default defineConfig({\n    // ...\n\tbuild: {\n\t\tremoteDependencyResolver: bun(), // [!code ++]\n\t},\n});\n```\n\nNow when you build your registry the `workspace:` and `catalog:` dependencies will be resolved to concrete versions.\n\n### Files\n\nWe showed you how to include files in the registry earlier by referencing them by their path but there's much more to know!\n\n#### File types\n\nFiles can have any type that an item can. If you don't provide a type the file will simply inherit the type from the parent item.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\t// ...\n                files: [\n                    {\n                        path: \"src/example.ts\",\n                        type: \"utils\", // [!code ++]\n                    }\n                ]\n\t\t\t}\n\t\t]\n\t}\n});\n```\n\n#### File dependencies\n\nJust like items you can set the `dependencyResolution` to `manual` and manually specify the dependencies of a file.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\t// ...\n                files: [\n                    {\n                        path: \"src/example.ts\",\n                        dependencyResolution: \"manual\", // [!code ++]\n                        dependencies: [\"chalk\"], // [!code ++]\n                    }\n                ]\n\t\t\t}\n\t\t]\n\t}\n});\n```\n\n#### File roles\n\nFile roles classify files into different categories that can be optionally installed by the user. The built-in roles are:\n\n- `file` - The default (always installed)\n- `example` - An example file\n- `doc` - A documentation file\n- `test` - A test file\n- Custom roles - Any other role you want to give files in your registry\n\nBy default only files with the `file` role are included.\n\nUsers can include files with specific roles using the `--with <role>` flag:\n\n```sh\njsrepo add --with story doc\n```\n\n<Callout type=\"info\">\n    These files are also made available to LLMs when using the `@jsrepo/mcp` server.\n</Callout>\n\nYou can specify the role of a file when you define it on an item:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\titems: [\n\t\t\t{\n\t\t\t\t// ...\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/example.ts\",\n\t\t\t\t\t\trole: \"example\", // [!code ++]\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\t\t]\n\t},\n});\n```\n\nFiles with any non-`file` role will only install their dependencies when the file is included.\n\nFor example if you have a file with `role: \"test\"` that depends on `vitest`. `vitest` will only be installed to the user's project when the user provides `--with test`.\n\nSimilarly if that same file was to depend on another item in the registry. Then that item will only be installed to the user's project when the test file is added.\n\nThis allows you to add **documentation**, **examples**, **tests**, or any other optional role to your registry without forcing the user to install them.\n\n#### Folders\n\nWhen using frameworks like **Svelte** you are forced to define *one component per file* which will require you to bundle all your files into a folder. In cases like this you need to be able to include folders in your registry.\n\nDoing this is intuitive in **jsrepo**.\n\nLet's take for example, this **Empty** component:\n\nimport { Files, Folder, File } from \"@/components/files\";\n\n<Files>\n\t<Folder name=\"src\" defaultOpen>\n\t\t<Folder name=\"components\" defaultOpen>\n\t\t\t<Folder name=\"ui\" defaultOpen>\n\t\t\t\t<Folder name=\"empty\" defaultOpen>\n\t\t\t\t\t<File name=\"empty-content.svelte\" />\n\t\t\t\t\t<File name=\"empty-description.svelte\" />\n\t\t\t\t\t<File name=\"empty-header.svelte\" />\n\t\t\t\t\t<File name=\"empty-media.svelte\" />\n\t\t\t\t\t<File name=\"empty-title.svelte\" />\n                    <File name=\"empty.svelte\" />\n\t\t\t\t\t<File name=\"index.ts\" />\n\t\t\t\t</Folder>\n\t\t\t</Folder>\n\t\t</Folder>\n\t</Folder>\n</Files>\n\nWe can simply reference the folder path and **jsrepo** will automatically include all the files in the folder:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: 'empty',\n\t\t\t\ttype: 'ui',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/components/ui/empty', // [!code ++]\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t},\n});\n```\n\nAll the files in the folder will be included in the registry automatically and when users add them they will be added together under the `empty` folder.\n\nIf you need to configure the files that are included in a folder you can use the `files` key on folder. This is also useful if you need to change properties like the `role` or `dependencyResolution` of a particular file.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\t// ...\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/components/ui/empty', // [!code ++]\n                        files: [ // [!code ++]\n                            { // [!code ++]\n                                // file paths are relative to the parent folder path so this turns into `src/components/ui/empty/empty-content.svelte` // [!code ++]\n                                path: 'empty-content.svelte', // [!code ++]\n                                // you can also configure other properties like the `role` or `dependencyResolution` of a particular file. // [!code ++]\n                                dependencyResolution: 'manual', // [!code ++]\n                            }, // [!code ++]\n                            { // [!code ++]\n                                path: 'empty-description.svelte', // [!code ++]\n                            }, // [!code ++]\n                            { // [!code ++]\n                                path: 'empty-header.svelte', // [!code ++]\n                            }, // [!code ++]\n                            { // [!code ++]\n                                path: 'empty-media.svelte', // [!code ++]\n                            }, // [!code ++]\n                            { // [!code ++]\n                                path: 'empty-title.svelte', // [!code ++]\n                            }, // [!code ++]\n                            { // [!code ++]\n                                path: 'empty.svelte', // [!code ++]\n                            }, // [!code ++]\n                            { // [!code ++]\n                                path: 'index.ts', // [!code ++]\n                            }, // [!code ++]\n                        ], // [!code ++]\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n});\n```\n\n#### Glob patterns in file paths\n\nYou can also use glob patterns in file paths to include all files that match the pattern.\n\nFor example lets say I want to include all my demos for the button component as examples:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n                name: 'button',\n                type: 'ui',\n\t\t\t\tfiles: [\n                    {\n                        path: 'src/lib/components/button',\n                    },\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/lib/demos/button-*.svelte\", // [!code ++]\n                        role: 'example',\n                        dependencyResolution: 'manual',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n});\n```\n\nThis will match all files that match the pattern `button-*.svelte` in the `src/lib/demos` directory.\n\n**Recursive glob patterns:**\n\nMatch files in subdirectories while preserving the directory structure:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n                name: 'button',\n                type: 'ui',\n\t\t\t\tfiles: [\n                    {\n                        path: 'src/lib/components/button',\n                    },\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/lib/demos/**/button-*.svelte\", // [!code ++]\n                        role: 'example',\n                    },\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n});\n```\n\nThis will match all files (including those in subdirectories) that match the pattern `button-*.svelte` while maintaining the directory structure.\n\nFor instance:\n\n| Source Path | Path Relative to Item |\n|-------------|----------------------|\n| `src/lib/demos/button-default.svelte` | `button-default.svelte` |\n| `src/lib/demos/variants/button-outlined.svelte` | `variants/button-outlined.svelte` |\n| `src/lib/demos/variants/themes/button-dark.svelte` | `variants/themes/button-dark.svelte` |\n\n### Configure when an item is added\n\nWe mentioned this briefly above but you can configure when an item is added in the user's project by setting the `add` key an item.\n\n- `\"on-init\"` - Added on registry init or when it's needed by another item\n- `\"optionally-on-init\"` - Users are prompted to add the item when initializing the registry\n- `\"when-needed\"` - Not listed and only added when another item is added that depends on it\n- `\"when-added\"` - Added when the user selects it to be added\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\t// ...\n\t\t\t\tadd: \"when-added\", // [!code ++]\n\t\t\t}\n\t\t]\n\t},\n});\n```\n\n### Configuring the user's project\n\nThere are a few common things you may want to automatically configure in the user's project when they first initialize your registry.\n\n#### Default Paths\n\nDefault paths are just that, the default locations for which items types or specific items should be added to in the user's project.\n\nYou can configure the `defaultPaths` key of your registry config to configure the default paths for items or item types to be added to in the user's project.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\tdefaultPaths: { // [!code ++]\n\t\t\tcomponent: \"src/components/ui\", // [!code ++]\n            // you can of course also configure a specific item by referencing it by `<type>/<name>` // [!code ++]\n            \"ui/button\": \"src/components/ui/button\", // [!code ++]\n\t\t}, // [!code ++]\n\t},\n});\n```\n\n#### Plugins\n\nYou can configure the `plugins` key to automatically install plugins to the user's project when they initialize your registry.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\tplugins: {\n            languages: [{ package: \"jsrepo-language-go\" }], // [!code ++]\n            // by setting the optional key to true the user will be prompted to install the plugin if it is not already installed. // [!code ++]\n\t\t\ttransforms: [{ package: \"@jsrepo/transform-prettier\", optional: true }], // [!code ++]\n\t\t},\n\t},\n});\n```\n\n### Environment Variables\n\nSometimes your registry items may require environment variables to work.\n\nFor this you can define the `envVars` key of that particular item:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n        items: [\n            // ...\n            {\n                // ...\n                name: 'db',\n                type: 'lib',\n                files: [\n                    {\n                        path: 'src/db.ts',\n                    }\n                ],\n                envVars: { // [!code ++]\n                    DATABASE_URL: \"https://example.com/database\", // [!code ++]\n                    DATABASE_SECRET_TOKEN: \"\", // [!code ++]\n                }, // [!code ++]\n            }\n        ]\n\t},\n});\n```\n\nEnvironment variables will be added to the users `.env.local` or `.env` file.\n\nIf you leave an environment variable blank the user will be prompted to add a value for it.\n\nValues you configure here will ***never*** overwrite existing values in the user's env file.\n\n### Distributing multiple registries\n\nIt's become common to distribute multiple registries to allow users to optionally use different variants of your registry for example JavaScript or TypeScript.\n\nHowever until now there wasn't an easy way to do this.\n\n**jsrepo** solves this by allowing you to define multiple registries in the same config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: [\n        {\n            name: '@my-registry/typescript',\n            // ...\n        },\n        {\n            name: '@my-registry/javascript',\n            // ...\n        }\n    ]\n});\n```\n\nYou can then use the `outputs` api to define where each registry should be output to:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { distributed } from \"jsrepo/outputs\";\n\nexport default defineConfig({\n\tregistry: [\n        {\n            name: '@my-registry/vanilla',\n            outputs: [distributed({ dir: \"./public/r/v\" })], // [!code ++]\n            // ...\n        },\n        {\n            name: '@my-registry/tailwind',\n            outputs: [distributed({ dir: \"./public/r/tw\" })], // [!code ++]\n            // ...\n        }\n    ]\n});\n```\n\n### Dynamically generating registries\n\nYou don't always want to have to manually define your entire registry in your config file.\n\nAI has made this less cumbersome but it's still annoying to have a 1k LOC file just to define your registry.\n\nIn **jsrepo v2** we automatically generated your registry based on a bunch of complicated options and this wasn't the best experience.\n\nIn **jsrepo v3** we are giving the control back to you allowing you to write your own code to generate your registry.\n\nTo do this simply pass a function to the `registry` key that returns a registry config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n// define your own custom function\nimport { getItems } from \"./getItems\";\n\nexport default defineConfig({\n\tregistry: ({ cwd }) => {\n        return {\n            name: 'my-registry',\n            items: getItems(cwd)\n        }\n    }\n});\n```\n\nOh and of course you can also pass an array of functions:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n// define your own custom function\nimport { getItems } from \"./getItems\";\n\nexport default defineConfig({\n\tregistry: [\n\t\t({ cwd }) => {\n\t\t\treturn {\n\t\t\t\tname: '@my-registry/typescript',\n\t\t\t\titems: getItems(path.join(cwd, 'src/registry/ts'))\n\t\t\t}\n\t\t},\n        ({ cwd }) => {\n\t\t\treturn {\n\t\t\t\tname: '@my-registry/javascript',\n\t\t\t\titems: getItems(path.join(cwd, 'src/registry/js'))\n\t\t\t}\n\t\t}\n\t]\n});\n```\n\n<Callout type=\"info\">\n    Dynamically generated registries will still work with the `--watch` flag.\n</Callout>\n\n### Supporting JavaScript and TypeScript\n\nThanks to the **jsrepo** transforms API it's extremely straightforward to allow your users to choose between JavaScript and TypeScript when using your registry.\n\nUsers can simply initialize your registry with the `--js` flag to use JavaScript:\n\n```sh\njsrepo init @example/registry --js\n# or add the javascript plugin at any time\njsrepo config transform javascript\n```\n\nOr add the `@jsrepo/transform-javascript` transform to their config manually:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport javascript from \"@jsrepo/transform-javascript\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [javascript()], // [!code ++]\n});\n```\n\nThis will automatically strip the types from TypeScript files and rename them to JavaScript files. You can see the full documentation for the `@jsrepo/transform-javascript` transform [here](/docs/transforms/javascript).\n\n<Callout type=\"warning\">\n    This only works for [erasable syntax](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html#the---erasablesyntaxonly-option) if you are expecting for your users to use TypeScript ensure you are using the `--erasableSyntaxOnly` option when writing your TypeScript code.\n</Callout>\n\n### Depending on items from other registries\n\nOften times you may want to depend on items from other registries. In `shadcn/ui` you would add the registry URL to the item you are dependent on.\n\n**jsrepo** doesn't support this for a few reasons:\n\n1. You may have made changes to the component in your project that are not tracked by the upstream registry. This can break the users code or cause it to function incorrectly.\n2. It's possible that the author of the upstream registry may make changes to the component that break your code in users projects.\n\nFor this reason **jsrepo** doesn't support depending on items from other registries. The alternative is to simply copy the components from the upstream registry into your own registry and serve them from your own registry.\n\nWe recommend when you do this to follow a few best practices:\n\n1. Credit the upstream registry in some way so users know where those items came from.\n2. Ensure to set `add: \"when-needed\"` on items from external registries to prevent them from being listed by the `add` command.\n\n### Modifying items in your registry before build\n\nSometimes you may want to modify the content of registry items before they are built to an output.\n\nFor example you may want to build multiple different types of your registry for each style you offer.\n\nYou can do this with the `build.transforms` option:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tbuild: {\n\t\ttransforms: [\n\t\t\t{\n\t\t\t\ttransform: async (content, opts) => {\n\t\t\t\t\treturn { content: content.replaceAll('{{PROJECT_NAME}}', 'jsrepo') };\n\t\t\t\t},\n\t\t\t}\n\t\t],\n\t},\n});\n```\n\nThis will run the transform on the content of each file in your registry before it's added to the output. This way you can easily modify the content at build time without needing to write another registry to the filesystem."
  },
  {
    "path": "apps/docs/content/docs/index.mdx",
    "content": "---\ntitle: Introduction\ndescription: Why we built jsrepo.\n---\n\nimport Link from \"next/link\";\n\n[shadcn/ui](https://ui.shadcn.com) proved that distributing code in a way that allows consumers to own the source is an extremely powerful pattern. Since then registries have become a common way to distribute reusable components.\n\nAs more and more registries start to emerge it became clear that there was a need for a more powerful toolchain to help manage and distribute these registries. That's where **jsrepo** comes in...\n\n**jsrepo** is a toolchain for distributing your code. It gives powerful tools to both registry owners and consumers. \n\n**Registry owners...**\n- Don't want to have to write their own CLI and build system to distribute their code\n- Want to be able to test and verify that their registry will work for their users before they publish it\n- Want to be able to host their registries privately without having to write their own hosting solution\n\n**Registry consumers...**\n- Want to quickly add items from a registry to their project and start using them without having to even look at the code\n- Want to be able to easily update their items with visibility to what as changed\n- Want to be able to install and manage code from multiple registries in a single project\n- Want an easy way to authenticate to private registries\n\n**jsrepo...**\n- Provides a powerful CLI for building and distributing your registry\n- Prevents you from publishing broken or unusable registries\n- Provides provider adapters to allow you to host your registry publicly or privately anywhere you want\n- Automatically resolves dependencies to other items so your items always come ready to use\n- Provides an interactive update command to easily update your items with visibility to what as changed\n- Allows you to authenticate to private registries with `jsrepo auth`\n\n<Accordion type=\"single\" collapsible>\n\t<AccordionItem value=\"q-1\">\n\t\t<AccordionTrigger>Is this trying to replace npm?</AccordionTrigger>\n\t\t<AccordionContent className=\"flex flex-col gap-4 text-balance\">\n\t\t\tNo, **jsrepo** won't replace npm, but it's perfect for distributing code like leftPad that probably shouldn't\n\t\t\tbe a package but also shouldn't have to be rewritten every time.\n\t\t</AccordionContent>\n\t</AccordionItem>\n\t<AccordionItem value=\"q-2\">\n\t\t<AccordionTrigger>Is this shadcn compatible?</AccordionTrigger>\n\t\t<AccordionContent className=\"flex flex-col gap-4 text-balance\">\n\t\t\tYes **jsrepo** is fully shadcn compatible. You can add and update items from **shadcn** with zero configuration needed.\n\t\t</AccordionContent>\n\t</AccordionItem>\n</Accordion>\n"
  },
  {
    "path": "apps/docs/content/docs/jsrepo-com.mdx",
    "content": "---\ntitle: jsrepo.com\ndescription: A registry for your registries.\n---\n\nimport { BadgesTable } from \"@/components/badges-table\";\n\n<BadgeGroup>\n\t<OfficialBadge />\n</BadgeGroup>\n\n[jsrepo.com](https://jsrepo.com) is a centralized source registry for registries. Much like npm is for packages, **jsrepo.com** is for registries.\n\nLike npm, you publish your registry to [jsrepo.com](https://jsrepo.com) with the `jsrepo publish` command and then other developers can add your code to their projects with the `jsrepo add` command.\n\n## The benefits of jsrepo.com\n\n- Semver support - You can publish multiple versions of the same registry (including pre-release versions) just like you would on npm.\n- Easy installation - You can add your registry to your project with the simple `@<scope>/<registry>` syntax.\n- Private registries - You can easily publish private registries and share them with your team for free.\n- Marketplace - You can monetize your registry by selling it on the marketplace.\n- Discoverability - Your registry will be discoverable through the **jsrepo.com** website and the jsrepo mcp server.\n\n## Publishing your registry\n\nIf you already have a registry that can be built with **jsrepo** then there are only a few steps to publishing it to **jsrepo.com**. If not then checkout the [create a registry](/docs/create-a-registry) guide to get started.\n\n<Steps>\n\n<Step>\n    ### Create an account\n    Go to [jsrepo.com](https://jsrepo.com) and create an account.\n</Step>\n\n<Step>\n    ### Claim a scope\n    Once you have an account navigate to `/account/scopes/new` to [claim a scope](https://www.jsrepo.com/account/scopes/new). \n    \n    Scopes are used to group your registries together and are required to publish your registry.\n\n    When users add your registry the scope will be the first part of the registry name: `@<scope>/<registry>`.\n</Step>\n\n<Step>\n    ### Authenticate with jsrepo.com\n    Run the following command to authenticate with jsrepo.com:\n\n    ```sh\n    jsrepo auth jsrepo\n    ```\n\n    You will be prompted to finish the sign in in your browser and then you will be authenticated.\n</Step>\n\n<Step>\n    ### Prepare your registry\n    Decide on a name for your registry and add it to your `jsrepo.config.ts` file:\n\n    ```ts title=\"jsrepo.config.ts\"\n    import { defineConfig } from \"jsrepo\";\n\n    export default defineConfig({\n        registry: {\n            name: \"@my-scope/my-registry\", // [!code ++]\n            // ...\n        },\n    });\n    ```\n\n    Next you need to provide the version or your registry that will be published. If you want **jsrepo** to pull from the version in your `package.json` file just provide `package` as the version:\n\n    ```ts title=\"jsrepo.config.ts\"\n    import { defineConfig } from \"jsrepo\";\n\n    export default defineConfig({\n        registry: {\n            name: \"@my-scope/my-registry\",\n            version: \"1.0.0\", // [!code ++]\n            // or take the version from the `package.json` file\n            version: \"package\", // [!code ++]\n            // ...\n        },\n    });\n    ```\n\n    (Optional) Now is also a good time to add any other metadata you want to your registry:\n\n    ```ts title=\"jsrepo.config.ts\"\n    import { defineConfig } from \"jsrepo\";\n\n    export default defineConfig({\n        registry: {\n            name: \"@my-scope/my-registry\",\n            description: \"My first registry\", // [!code ++]\n            homepage: \"https://my-registry.com\", // [!code ++]\n            repository: \"https://github.com/my-scope/my-registry\", // [!code ++]\n            bugs: \"https://github.com/my-scope/my-registry/issues\", // [!code ++]\n            authors: [\"Aidan Bleser\"], // [!code ++]\n            tags: [\"first-registry\"], // [!code ++]\n            // ...\n        },\n    });\n    ```\n\n    (Optional) By default your registry will be published as public so anyone can see and use it. You can change the access level to `\"private\"` or `\"marketplace\"` by setting the `access` property:\n\n    ```ts title=\"jsrepo.config.ts\"\n    import { defineConfig } from \"jsrepo\";\n\n    export default defineConfig({\n        registry: {\n            name: \"@my-scope/my-registry\",\n            access: \"private\", // [!code ++]\n        },\n    });\n    ```\n</Step>\n\n<Step>\n    ### Publish your registry\n    Run the following command to publish your registry:\n\n    ```sh\n    jsrepo publish\n    ```\n</Step>\n\n<Step>\n    ### Start adding items to your project\n    Run the following command to add items to your project:\n\n    ```sh\n    jsrepo add --registry @my-scope/my-registry\n    # or \n    jsrepo init @my-scope/my-registry\n    ```\n</Step>\n\n</Steps>\n\n## Badges\n\n<BadgesTable\n\tbadges={[\n\t\t{\n\t\t\talt: 'jsrepo latest version',\n\t\t\threfTemplate: 'https://jsrepo.com/badges/{{registry}}'\n\t\t},\n\t\t{\n\t\t\talt: 'jsrepo downloads weekly',\n\t\t\threfTemplate: 'https://jsrepo.com/badges/{{registry}}/dm'\n\t\t},\n\t\t{\n\t\t\talt: 'jsrepo downloads monthly',\n\t\t\threfTemplate: 'https://jsrepo.com/badges/{{registry}}/dw'\n\t\t},\n\t\t{\n\t\t\talt: 'jsrepo downloads yearly',\n\t\t\threfTemplate: 'https://jsrepo.com/badges/{{registry}}/dy'\n\t\t},\n\t\t{\n\t\t\talt: 'jsrepo registry rating',\n\t\t\threfTemplate: 'https://jsrepo.com/badges/{{registry}}/rating'\n\t\t}\n\t]}\n\tdefaultRegistry=\"@ieedan/std\"\n/>"
  },
  {
    "path": "apps/docs/content/docs/jsrepo-config.mdx",
    "content": "---\ntitle: jsrepo.config\ndescription: The configuration file for jsrepo.\n---\n\nThe `jsrepo.config.(ts|js|mts|mjs)` file is used to configure **jsrepo** projects and registries.\n\nHaving a js based config allows you far more flexibility when configuring your project or registry allowing you to abstract out the reusable parts of your config.\n\n## Creating a config\n\nTo create a new config in your project you can run the following command or copy the code below:\n\n```sh\njsrepo init\n```\n\nThis will initialize a blank config in your project.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\t// configure where stuff comes from here\n\tregistries: [],\n\t// configure where stuff goes here\n\tpaths: {},\n});\n```\n\n<Callout type=\"info\">\n\tWe default to `.mts` to prevent errors when you don't have `\"type\": \"module\"` in your `package.json`. But you can\n\trename this to `.ts` if you prefer.\n</Callout>\n\n### Create with a registry\n\nTo create your config with a registry you can run the following command:\n\n```sh\njsrepo init [registry]\n```\n\nLet's look at an example...\n\nYou might run:\n\n```sh\njsrepo init https://example.com/registry\n```\n\nFirst you will be prompted to install any plugins specified by the registry author:\n\n```plaintext\n┌   jsrepo\n│\n◆  Would you like to add the @jsrepo/transform-prettier transform plugin?\n│  ● Yes / ○ No\n```\n\nNext you can configure the paths for the items you want to add. (These are the types of the items you can add from the registry)\n\n```plaintext\n◆  Which paths would you like to configure?\n│  ◻ block (Default: src/components)\n│  ◻ component\n│  ◻ lib\n└\n```\n\nOnce you have configured the paths any items that were specified by the registry author to add upon initialization will be added to your project. And the resulting config should look like this:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport prettier from \"@jsrepo/transform-prettier\";\n\nexport default defineConfig({\n\tregistries: [\"https://example.com/registry\"],\n\ttransforms: [prettier()],\n\tpaths: {\n\t\tblock: \"src/components\",\n\t\tcomponent: \"src/components/ui\",\n\t\tlib: \"src/lib\",\n\t},\n});\n```\n\n## Adding plugins\n\nPlugins are a big part of what makes jsrepo so powerful but having to manually add them to your config kinda sucks. So we've added a way to automatically install and add plugins to your config.\n\nTo add a plugin run the following command:\n\n```sh\n# add a transform plugin\njsrepo config transform @jsrepo/transform-prettier\n# add a provider plugin\njsrepo config provider jsrepo-provider-<provider>\n# add a language plugin\njsrepo config language jsrepo-language-<language>\n```\n\nThis will automatically install the plugin and add it to your config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport prettier from \"@jsrepo/transform-prettier\"; // [!code ++]\n\nexport default defineConfig({\n\tregistries: [\"https://example.com/registry\"],\n\ttransforms: [prettier()], // [!code ++]\n\tpaths: {\n\t\tblock: \"src/components\",\n\t\tcomponent: \"src/components/ui\",\n\t\tlib: \"src/lib\",\n\t},\n});\n```\n\nAs your config grows this will continue to work (so long as you don't use some really contrived syntax for your config).\n\n## Options\n\n### languages\n\nLanguages are how **jsrepo** knows how to parse and transform code. You can read more about the supported languages [here](/docs/languages).\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { js } from \"jsrepo/languages\";\n\nexport default defineConfig({\n\tlanguages: [js()], // [!code highlight]\n});\n```\n\n### paths\n\nPaths are how **jsrepo** knows where to put items in your project. Paths can either reference a type of item or a specific item.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tpaths: { // [!code highlight]\n\t\tblock: \"src/components\", // [!code highlight]\n\t\t// control where a specific item goes by referencing it by `<type>/<name>` // [!code highlight]\n\t\t\"ui/button\": \"src/components/ui/button\", // [!code highlight]\n\t}, // [!code highlight]\n});\n```\n\n### providers\n\nProviders are how **jsrepo** knows where to fetch items from. You can read more about providers [here](/docs/providers).\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { fs } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [fs()], // [!code highlight]\n});\n```\n\n### registries\n\nRegistries are the default locations that items will be fetched from when you run **jsrepo** commands.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistries: [\"https://example.com/registry\"], // [!code highlight]\n});\n```\n\n### registry\n\nThe `registry` option allows you to define your own registry or registries. You can learn more about creating your own registry [here](/docs/create-a-registry).\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: ..., // [!code highlight]\n});\n```\n\n### transforms\n\nTransforms allow you to make modifications to code before it is added to your project this is where you might add formatting, and other code modifications. You can read more about transforms [here](/docs/transforms).\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport prettier from \"@jsrepo/transform-prettier\";\n\nexport default defineConfig({\n\ttransforms: [prettier()], // [!code highlight]\n});\n```\n\n### onwarn\n\n<Callout type=\"warning\">\n**Deprecated:** The top-level `onwarn` option is deprecated. Use [`build.onwarn`](#buildonwarn) instead.\n</Callout>\n\n### build.onwarn\n\nThe `build.onwarn` option allows you to customize how warnings are handled during the build process. You can suppress specific warnings, transform them, or use the default logging behavior.\n\nThe handler receives two arguments:\n- `warning`: The warning instance (extends the base `Warning` class)\n- `handler`: A function to log the warning using the default format\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { InvalidImportWarning, LanguageNotFoundWarning } from \"jsrepo/warnings\";\n\nexport default defineConfig({\n\t// ...\n\tbuild: {\n\t\tonwarn: (warning, handler) => { // [!code highlight]\n\t\t\t// Suppress warnings for SvelteKit internal imports\n\t\t\tif (warning instanceof InvalidImportWarning) { // [!code highlight]\n\t\t\t\tif (['$app/server', '$app/navigation'].includes(warning.specifier)) { // [!code highlight]\n\t\t\t\t\treturn; // Don't log this warning // [!code highlight]\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Suppress language warnings for specific file types\n\t\t\tif (warning instanceof LanguageNotFoundWarning) { // [!code highlight]\n\t\t\t\tif (warning.path.endsWith('.glb') || warning.path.endsWith('.png')) { // [!code highlight]\n\t\t\t\t\treturn; // Don't log this warning // [!code highlight]\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Log all other warnings using the default handler\n\t\t\thandler(warning); // [!code highlight]\n\t\t},\n\t},\n});\n```\n\nAvailable warning types:\n- `InvalidImportWarning`: Triggered when an import is skipped because it's not a valid package name or path alias\n- `LanguageNotFoundWarning`: Triggered when a language cannot be found to resolve dependencies for a file\n- `UnresolvableDynamicImportWarning`: Triggered when a dynamic import cannot be resolved due to unresolvable syntax\n\nAll warnings extend the base `Warning` class which has a `message` property. Each specific warning type includes additional properties relevant to that warning type.\n\n### build.remoteDependencyResolver\n\nThe `build.remoteDependencyResolver` option lets you rewrite each detected remote dependency before it is added to the built registry output.\n\nThis is useful when your source `package.json` uses version protocols like `workspace:*` or `catalog:` and you want to replace them with concrete versions during build.\n\nFor pnpm workspaces (workspace and catalog protocols), use `@jsrepo/pnpm`:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { pnpm } from \"@jsrepo/pnpm\";\n\nexport default defineConfig({\n\tbuild: {\n\t\tremoteDependencyResolver: pnpm(),\n\t},\n});\n```\n\nFor bun workspaces, use `@jsrepo/bun`:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { bun } from \"@jsrepo/bun\";\n\nexport default defineConfig({\n\tbuild: {\n\t\tremoteDependencyResolver: bun(),\n\t},\n});\n```\n\nOr implement a custom resolver:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tbuild: {\n\t\tremoteDependencyResolver: async (dep) => {\n\t\t\tif (dep.version === \"workspace:*\") {\n\t\t\t\treturn { ...dep, version: \"1.2.3\" };\n\t\t\t}\n\t\t\tif (dep.version === \"catalog:\") {\n\t\t\t\treturn { ...dep, version: \"^4.0.0\" };\n\t\t\t}\n\t\t\treturn dep;\n\t\t},\n\t},\n});\n```\n\n### build.transforms\n\nThe `build.transforms` option allows you to transform the content of files before they are added to your project.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tbuild: {\n\t\ttransforms: [\n\t\t\t{\n\t\t\t\ttransform: async (content, opts) => {\n\t\t\t\t\treturn { content: content.replaceAll('{{PROJECT_NAME}}', 'jsrepo') };\n\t\t\t\t},\n\t\t\t}\n\t\t],\n\t},\n});\n```\n\n### hooks\n\nHooks allow you to run custom logic before and after CLI commands.\n\n- `before` hooks run before the command executes.\n- `after` hooks run after the command completes.\n\nHooks can be a function, a shell command string, or an array of either.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\thooks: {\n\t\tbefore: ({ command }) => {\n\t\t\tconsole.log(`Running ${command}...`);\n\t\t},\n\t\tafter: \"echo done\",\n\t},\n});\n```\n\nFunction hooks receive typed arguments with a `command` discriminant. Use it to narrow and access command-specific data:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\thooks: {\n\t\tafter: (args) => {\n\t\t\tif (args.command === \"add\") {\n\t\t\t\tconsole.log(`Added ${args.result.items.length} items`);\n\t\t\t}\n\t\t},\n\t},\n});\n```\n\nYou can also use an array of hooks:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\thooks: {\n\t\tbefore: [\n\t\t\t({ command }) => console.log(`Starting ${command}...`),\n\t\t\t\"echo 'before hook done'\",\n\t\t],\n\t},\n});\n```\n"
  },
  {
    "path": "apps/docs/content/docs/languages/css.mdx",
    "content": "---\ntitle: css\ndescription: CSS language support for jsrepo.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/langs/css.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\nThe `css` language plugin supports dependency resolution for CSS, SCSS, and Sass files as well as installing dependencies with `ecosystem: \"js\"`.\n\n## Supported syntax\n\nThe CSS plugin uses the [css-dependency](https://www.npmjs.com/package/css-dependency) library under the hood to detect dependencies. So it supports all the following syntax:\n\n-   `@import` statements\n-   `@plugin` statements (if `allowTailwindDirectives` is `true`)\n-   `@config` statements (if `allowTailwindDirectives` is `true`)\n-   `@reference` statements (if `allowTailwindDirectives` is `true`)\n\nBy default `allowTailwindDirectives` is `true` so it will also detect Tailwind directives as imports. To opt out of this behavior you can set `allowTailwindDirectives` to `false`.\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport { css } from \"jsrepo/langs\";\n\nexport default defineConfig({\n\tlanguages: [css({ allowTailwindDirectives: false })], // [!code ++]\n});\n```\n## Supported File Extensions\n\nThe CSS language plugin supports the following file extensions:\n\n| Extension |\n| --------- |\n| `.css`    |\n| `.scss`   |\n| `.sass`   |"
  },
  {
    "path": "apps/docs/content/docs/languages/html.mdx",
    "content": "---\ntitle: html\ndescription: HTML language support for jsrepo.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/langs/html.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\nThe `html` language plugin supports dependency resolution for HTML files as well as installing dependencies with `ecosystem: \"js\"`.\n\n## Supported syntax\n\nThe HTML plugin will detect dependencies from the following syntax:\n\n-   `script` tags with `src` attributes\n-   `script` tags with inline code\n-   `link` tags with `href` attributes\n\nFor example the following code:\n\n```html\n<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<link rel=\"stylesheet\" href=\"./app.css\">\n\t</head>\n\t<!-- Doesn't resolve remote deps -->\n\t<script src=\"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19\"></script>\n\t<script>\n\t\timport { print } from \"@/utils/stdout\";\n\t\tprint(\"Hello, world!\");\n\t</script>\n</html>\n```\n\nWill result in the following dependencies:\n\n-   `./app.css` - From `link` tag\n-   `@/utils/stdout` - From `script` tag with inline code\n\n## Supported File Extensions\n\nThe HTML language plugin supports the following file extensions:\n\n| Extension |\n| --------- |\n| `.html`   |"
  },
  {
    "path": "apps/docs/content/docs/languages/index.mdx",
    "content": "---\ntitle: Languages\ndescription: Language support for jsrepo.\n---\n\n**jsrepo** uses languages to determine the dependencies of registry items when you run the `build` command and also how to add and install dependencies.\n\n<Callout type=\"info\">\n\t**jsrepo** can distribute code of any language or file type but these are the languages that support [dependency\n\tresolution](#what-is-dependency-resolution).\n</Callout>\n\n## Available Languages\n\nBy default **jsrepo** supports the following languages:\n\n<Cards>\n\t<Card href=\"/docs/languages/js\" icon={<JavaScriptLogo />} title=\"JavaScript\">\n\t\tSupport for `*.js`, `*.ts`, `*.jsx`, `*.tsx`, `*.mjs`, `*.mts` files.\n\t</Card>\n\t<Card href=\"/docs/languages/svelte\" icon={<SvelteLogo />} title=\"Svelte\">\n\t\tSupport for `*.svelte` files.\n\t</Card>\n\t<Card href=\"/docs/languages/vue\" icon={<VueLogo />} title=\"Vue\">\n\t\tSupport for `*.vue` files.\n\t</Card>\n\t<Card href=\"/docs/languages/css\" icon={<CSSLogo />} title=\"CSS\">\n\t\tSupport for `*.css`, `*.scss`, `*.sass` files.\n\t</Card>\n\t<Card href=\"/docs/languages/html\" icon={<HTML5Logo />} title=\"HTML\">\n\t\tSupport for `*.html` files.\n\t</Card>\n</Cards>\n\n## What is dependency resolution?\n\nDependency resolution is the process of determining the dependencies of a registry item. This process ensures that when you add a registry item that all of it's dependencies are also added.\n\nWithout dependency resolution you need to manually specify dependencies of a registry item which is cumbersome and error prone.\n\n### How to manually specify dependencies\n\nIf your language of choice doesn't support dependency resolution or your registry items have dependencies that cannot be automatically detected you can manually specify them like so:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { js } from \"jsrepo/langs\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: \"button\",\n\t\t\t\ttype: \"component\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/components/button.tsx\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tregistryDependencies: [\"utils\"], // [!code ++]\n\t\t\t\tdependencies: [\n\t\t\t\t\t// [!code ++]\n\t\t\t\t\t{\n\t\t\t\t\t\t// [!code ++]\n\t\t\t\t\t\tecosystem: \"js\", // [!code ++]\n\t\t\t\t\t\tname: \"radix-ui\", // [!code ++]\n\t\t\t\t\t\tversion: \"1.4.3\", // [!code ++]\n\t\t\t\t\t}, // [!code ++]\n\t\t\t\t], // [!code ++]\n\t\t\t},\n\t\t],\n\t},\n});\n```\n\n### How to prevent automatic dependency resolution\n\nIf you want to opt out of automatic dependency resolution you can do so by setting the `dependencyResolution` option to `manual` on the registry item or on the file itself.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { js } from \"jsrepo/langs\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: \"button\",\n\t\t\t\ttype: \"component\",\n\t\t\t\t// for the entire item\n\t\t\t\tdependencyResolution: \"manual\", // [!code ++]\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/components/button.tsx\",\n\t\t\t\t\t\t// for individual files\n\t\t\t\t\t\tdependencyResolution: \"manual\", // [!code ++]\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t},\n});\n```\n\n### Strict mode\n\nBy default **jsrepo** will error if it cannot resolve all dependencies of a registry item. You can opt out of this behavior by setting the `strict` option to `false`.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { js } from \"jsrepo/langs\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: \"button\",\n\t\t\t\ttype: \"component\",\n\t\t\t\tstrict: false, // [!code ++]\n\t\t\t},\n\t\t],\n\t},\n});\n```\n"
  },
  {
    "path": "apps/docs/content/docs/languages/js.mdx",
    "content": "---\ntitle: js\ndescription: JavaScript language support for jsrepo.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/langs/js.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\nThe `js` language plugin supports dependency resolution for both JavaScript and TypeScript files as well as installing dependencies with `ecosystem: \"js\"`.\n\n## Supported syntax\n\nThe JavaScript plugin will detect dependencies from the following syntax:\n\n- Static imports\n- Dynamic imports\n- Export from statements\n\nFor example the following code:\n\n```ts\nimport 'dotenv/config';\nimport { print } from \"@/utils/stdout\";\n\nasync function printDynamic() {\n\tconst data = await import(\"./data.json\", { with: { type: \"json\" } });\n\tprint(data.message);\n}\n\nexport { logger } from \"@/utils/logger\";\n```\n\nWill result in the following dependencies:\n\n-   `dotenv` - From side effect import\n-   `@/utils/stdout` - From static import\n-   `./data.json` - From dynamic import\n-   `@/utils/logger` - From export from statement\n\n## Supported File Extensions\n\nThe JavaScript language plugin supports the following file extensions:\n\n| Extension |\n| --------- |\n| `.js`     |\n| `.ts`     |\n| `.jsx`    |\n| `.tsx`    |\n| `.mjs`    |\n| `.mts`    |\n"
  },
  {
    "path": "apps/docs/content/docs/languages/svelte.mdx",
    "content": "---\ntitle: svelte\ndescription: Svelte language support for jsrepo.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/langs/svelte.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\nThe `svelte` language plugin supports dependency resolution for Svelte files as well as installing dependencies with `ecosystem: \"js\"`.\n\n<Callout type=\"warning\">\n\tThis language plugin requires `svelte` to be installed in your project.\n</Callout>\n\n## Supported syntax\n\nThe Svelte plugin parses all script tags and passes the code to the [JavaScript plugin](/docs/languages/js) to resolve dependencies so all the same syntax is supported.\n\n## Supported File Extensions\n\nThe JavaScript language plugin supports the following file extensions:\n\n| Extension |\n| --------- |\n| `.svelte` |\n"
  },
  {
    "path": "apps/docs/content/docs/languages/vue.mdx",
    "content": "---\ntitle: vue\ndescription: Vue language support for jsrepo.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/langs/vue.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\nThe `vue` language plugin supports dependency resolution for Vue files as well as installing dependencies with `ecosystem: \"js\"`.\n\n<Callout type=\"warning\">\n\tThis language plugin requires `vue` to be installed in your project.\n</Callout>\n\n## Supported syntax\n\nThe Vue plugin parses all script tags and passes the code to the [JavaScript plugin](/docs/languages/js) to resolve dependencies so all the same syntax is supported.\n\n## Supported File Extensions\n\nThe Vue language plugin supports the following file extensions:\n\n| Extension |\n| --------- |\n| `.vue`    |"
  },
  {
    "path": "apps/docs/content/docs/legacy.mdx",
    "content": "---\ntitle: Legacy Docs\ndescription: Legacy documentation for jsrepo.\n---\n\nAll documentation for older versions of **jsrepo** can be found below:\n\n<Cards>\n\t<Card href=\"https://v2.jsrepo.dev\" icon={<BookIcon />} title=\"jsrepo v2\">\n\t\tLegacy documentation for jsrepo v2.\n\t</Card>\n</Cards>"
  },
  {
    "path": "apps/docs/content/docs/mcp.mdx",
    "content": "---\ntitle: MCP Server\ndescription: The jsrepo MCP server.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/mcp\" />\n\t<OfficialBadge />\n    <NpmBadge packageName=\"@jsrepo/mcp\" />\n</BadgeGroup>\n\nThe **jsrepo** MCP server has been built alongside the **jsrepo** CLI to ensure agents have first class support for interacting with jsrepo registries.\n\n<Callout type=\"info\">\n    The **jsrepo** MCP ships separate to the **jsrepo** CLI to reduce the overall size of the CLI and can be found on npm as `@jsrepo/mcp`.\n</Callout>\n\nAll **jsrepo** registries are supported out of the box by the MCP server, including registries hosted on custom providers (provided the custom provider is present in your `jsrepo.config`).\n\n## Configuration\n\n<UnderlineTabs defaultValue=\"cursor\">\n\t<UnderlineTabsList>\n\t\t<UnderlineTabsTrigger value=\"cursor\"><CursorLogo /> Cursor</UnderlineTabsTrigger>\n\t\t<UnderlineTabsTrigger value=\"claude\"><ClaudeLogo /> Claude Code </UnderlineTabsTrigger>\n\t\t<UnderlineTabsTrigger value=\"vs-code\"><VSCodeLogo /> VS Code</UnderlineTabsTrigger>\n\t\t<UnderlineTabsTrigger value=\"codex\"><OpenAILogo /> Codex</UnderlineTabsTrigger>\n\t\t<UnderlineTabsTrigger value=\"antigravity\"><AntigravityLogo /> Antigravity</UnderlineTabsTrigger>\n\t</UnderlineTabsList>\n\t<UnderlineTabsContent value=\"cursor\">\n        You can quickly configure the MCP server for Cursor by running the following command:\n\n\t\t```sh\n        jsrepo config mcp --client cursor\n        ```\n\n        This will automatically configure the MCP server for Cursor in the correct file:\n\n        ```json title=\".cursor/mcp.json\"\n        {\n            \"mcpServers\": {\n                \"jsrepo\": {\n                    \"command\": \"npx\",\n                    \"args\": [\"@jsrepo/mcp\"]\n                }\n            }\n        }\n        ```\n\t</UnderlineTabsContent>\n\t<UnderlineTabsContent value=\"claude\">\n        You can quickly configure the MCP server for Claude Code by running the following command:\n\n\t\t```sh\n        jsrepo config mcp --client claude\n        ```\n\n        This will automatically configure the MCP server for Claude Code in the correct file:\n\n        ```json title=\".mcp.json\"\n        {\n            \"mcpServers\": {\n                \"jsrepo\": {\n                    \"command\": \"npx\",\n                    \"args\": [\"@jsrepo/mcp\"]\n                }\n            }\n        }\n        ```\n    </UnderlineTabsContent>\n\t<UnderlineTabsContent value=\"vs-code\">\n        You can quickly configure the MCP server for VSCode by running the following command:\n\n\t\t```sh\n        jsrepo config mcp --client vscode\n        ```\n\n        This will automatically configure the MCP server for VSCode in the correct file:\n\n        ```json title=\".vscode/mcp.json\"\n        {\n            \"servers\": {\n                \"jsrepo\": {\n                    \"command\": \"npx\",\n                    \"args\": [\"@jsrepo/mcp\"]\n                }\n            }\n        }\n        ```\n    </UnderlineTabsContent>\n\t<UnderlineTabsContent value=\"codex\">\n        You can quickly configure the MCP server for Codex by running the following command:\n\n\t\t```sh\n        jsrepo config mcp --client codex\n        ```\n\n        This will automatically configure the MCP server for Codex in the correct file:\n\n        ```toml title=\"~/.codex/config.toml\"\n        [mcp_servers.jsrepo]\n        command = \"npx\"\n        args = [\"@jsrepo/mcp\"]\n        ```\n    </UnderlineTabsContent>\n    <UnderlineTabsContent value=\"antigravity\">\n        You can quickly configure the MCP server for Antigravity by running the following command:\n\n\t\t```sh\n        jsrepo config mcp --client antigravity\n        ```\n\n        This will automatically configure the MCP server for Antigravity in the correct file:\n\n        ```json title=\"~/.gemini/antigravity/mcp_config.json\"\n        {\n            \"mcpServers\": {\n                \"jsrepo\": {\n                    \"command\": \"npx\",\n                    \"args\": [\"@jsrepo/mcp\"]\n                }\n            }\n        }\n        ```\n    </UnderlineTabsContent>\n</UnderlineTabs>\n\n## Tools\n\nThe jsrepo MCP server provides the following tools:\n\n| Tool | Description |\n|------|-------------|\n| `add_item_to_project` | Add a registry item or items directly to the users project |\n| `view_registry_item` | View the code and information about a registry item |\n| `list_items_in_registry` | List registry items in one or more registries and optionally fuzzy search |\n\n## Registry Authors\n\nYour registry will automatically be compatible with the **jsrepo** MCP server, but here are a few tips to improve the agents usage of your registry:\n\n- include all the registry metadata in your `jsrepo.config` to allow agents to find your registry more easily when searching\n- include a `description` with each item in your registry\n- include files with `role: \"example\"` that show how to use each item\n- include files with `role: \"doc\"` that document each item\n"
  },
  {
    "path": "apps/docs/content/docs/migrate.mdx",
    "content": "---\ntitle: Migration\ndescription: Migrate from jsrepo v2 to jsrepo v3.\n---\n\n**jsrepo** v3 is a complete rewrite of the **jsrepo** CLI and we haven't been shy about making breaking changes. \n\nIf you're coming from **jsrepo** v2 and want to quickly get started with **jsrepo** v3 you can run the following command in your existing **jsrepo** project:\n\n```sh\npnpm dlx @jsrepo/migrate v3\n```\n\nThe migration tool will:\n- Migrate your `jsrepo-build-config.json` and `jsrepo.json` files into the new `jsrepo.config.ts` file.\n- Install `jsrepo` and the correct formatting transform (if you were using one)\n- Build your registry using both v2 and v3 to ensure compatibility\n\n<Callout type=\"info\">\n    You can put this in your upgrade guide for your users.\n</Callout>\n\n## Breaking changes\n\n### 1. A new js based config\n\nIn **jsrepo** v2 you used a `jsrepo-build-config.json` file to configure your registry and a `jsrepo.json` file to configure your project. This wasn't great for obvious reasons.\n\nIn **jsrepo** v3 we use a `jsrepo.config.ts` file to configure your registry and project: \n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\t// ...\n});\n```\n\nThis affords us more flexibility and allows us to support plugins.\n\n<Callout type=\"info\">\n    If you're like me you may be worried about having to automatically make modifications to a js based config. Not to worry! The blood, sweat, and (mostly) tears have been shed to make it as seamless as possible.\n</Callout>\n\n### 2. No more categories\n\nIn **jsrepo** v2 we had item categories. This allowed you to group items together and add them by running `jsrepo add <category>/<item>`.\n\nIn **jsrepo** v3 you can add items by running `jsrepo add <item>`.\n\n\"categories\" are now the `type` of the item. The type of an item is used to determine the path that the item will be added to in the user's project:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: \"button\",\n\t\t\t\ttype: \"component\", // instead of category now it's type // [!code highlight]\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/components/button.tsx\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t},\n});\n```\n\n### 3. No backwards compatibility with v2 registries\n\nYou probably could've guessed this by some of the changes we have already mentioned but unfortunately we don't support adding items from v2 registries from the v3 CLI.\n\nIf you still want to use a v2 registry you can of course continue to use the v2 CLI to add items to your project:\n\n```sh\npnpm dlx jsrepo@2 add ui/button\n```\n\n### 4. Deprecated commands\n\nThe following commands have been deprecated in **jsrepo** v3:\n\n- `jsrepo test`\n- `jsrepo execute`\n- `jsrepo tokens` - Now all auth functions are handled by the `jsrepo auth` command.\n- `jsrepo mcp` - The MCP server has been moved to the `@jsrepo/mcp` package.\n\n### 5. No more `Update with AI`\n\nWhile we were excited by the `Update with AI` feature it didn't feel worth the added bundle cost. We'll likely explore ways to improve the workflow of updating items with AI without the added bundle cost.\n\n### 6. Formatting functionality has been externalized\n\nIn **jsrepo** v2 you could format code before it was added to your project by configuring the formatter in your `jsrepo.json` file.\n\nWith **jsrepo** v3 we introduced the concept of \"transforms\" and in so doing we realized that the formatting functionality should be externalized to reduce the bundle size of the CLI and allow for more flexibility for users.\n\nYou can now configure formatting for your project by adding one of the officially supported transforms to your config:\n\n```sh\n# prettier\njsrepo config transform prettier\n# biome\njsrepo config transform biome\n```\n\n### 7. Output configuration changes\n\nIn **jsrepo** v2, registries could specify an `outputDir` in their build config. In **jsrepo** v3, this has been replaced with output plugins:\n\n- Registries that had an `outputDir` are migrated to use the [distributed](/docs/outputs/distributed) output\n- Registries without an `outputDir` use the [repository](/docs/outputs/repository) output by default\n\n### 8. Removed peer dependencies\n\nRight now this is just a choice to reduce API surface for language plugins. If this becomes something the community wants we can add it back in the future.\n\n## The wins\n\nNow let's talk about the reasons we are so excited to break everyone's code for **jsrepo** v3.\n\n### Plugins\n\nIn **jsrepo** v3 we have introduced plugins. There are currently three types of plugins:\n\n- [Languages](/docs/languages) - Extend jsrepo dependency resolution support to include additional languages.\n- [Outputs](/docs/outputs) - Customize the way your registry is distributed.\n- [Providers](/docs/providers) - Allow users to install items from a custom source.\n- [Transforms](/docs/transforms) - Modify code before it's added to your project.\n\n### Shadcn compatibility\n\n**jsrepo** v3 is now **shadcn** compatible. You can now add and update items from **shadcn** meaning **jsrepo** now has access to an entire ecosystem of **shadcn** registries.\n\nTry it out by running:\n\n```sh\npnpm jsrepo add --registry https://ui.shadcn.com/r/styles/new-york-v4\n```\n\n### Greatly improved MCP responses\n\nThanks to adopting the more manual approach for defining a registry it's now possible to add metadata to your registry items greatly improving the responses you will get when using the **jsrepo** MCP server.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: \"button\",\n                title: \"Button\", // [!code ++]\n                description: \"A button component\", // [!code ++]\n                files: [\n                    {\n                        path: \"src/components/button.tsx\",\n                    },\n                    // add examples for LLMs to use\n                    { // [!code ++]\n                        path: 'src/demos/button-demo.tsx', // [!code ++]\n                        role: 'example', // [!code ++]\n                    } // [!code ++]\n                ]\n\t\t\t},\n\t\t],\n\t},\n});\n```\n"
  },
  {
    "path": "apps/docs/content/docs/outputs/distributed.mdx",
    "content": "---\ntitle: Distributed\ndescription: Output your registry as json files in a directory.\n---\n\nimport { Files, Folder, File } from \"@/components/files\";\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/outputs/distributed.ts\" />\n\t<OfficialBadge />\n</BadgeGroup>\n\nThe `distributed` output is used for maximizing performance when serving your registry as a static asset.\n\n## Usage\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { distributed } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n\t\toutputs: [distributed({ dir: \"./public/r\" })], // [!code ++]\n\t},\n});\n```\n\nNow running `jsrepo build` will output your registry to the `./public/r` directory.\n\nUnlike the `repository` output, the `distributed` output will output each item as a separate json file. For example:\n\n<Files>\n\t<Folder name=\"public\" defaultOpen>\n\t\t<Folder name=\"r\" defaultOpen>\n\t\t\t<File name=\"registry.json\" />\n\t\t\t<File name=\"button.json\" />\n\t\t\t<File name=\"utils.json\" />\n\t\t</Folder>\n\t</Folder>\n</Files>\n\nSince each item file contains all the code for all the files required for that item this is much more performant, and portable than the `repository` output."
  },
  {
    "path": "apps/docs/content/docs/outputs/index.mdx",
    "content": "---\ntitle: Outputs\ndescription: Output your registry however you want.\n---\n\nOutputs allow you to customize the way your registry is distributed, or (more generally) what to do with the registry after it's built.\n\nThis isn't limited into building your registry into a format that **jsrepo** can understand you could use it to output documentation or a manifest for your own CLI application.\n\nBy default **jsrepo** ships with two output types:\n\n- [Distributed](/docs/outputs/distributed) - Output your registry as json files in a directory.\n- [Repository](/docs/outputs/repository) - Output your registry as a single json file in the root of your repository.\n\nHere are all the officially available outputs:\n\n<Cards>\n\t<Card href=\"/docs/outputs/distributed\" icon={<SplitIcon />} title=\"Distributed\">\n\t\tOutput your registry as json files in a directory.\n\t</Card>\n\t<Card href=\"/docs/outputs/repository\" icon={<FolderIcon />} title=\"Repository\">\n\t\tOutput your registry as a single json file in the root of your repository.\n\t</Card>\n    <Card href=\"/docs/outputs/shadcn\" icon={<ShadcnLogo />} title=\"shadcn\">\n\t\tOutput your registry as a shadcn registry.\n\t</Card>\n</Cards>\n\n## Creating a custom output\n\nYou can create your own output either inline in your config file or as a standalone package.\n\nFor this example we will create a standalone package that outputs your registry as a markdown file called `REGISTRY.md`.\n\nLet's create our `output.ts` file and import the `Output` type from `jsrepo/outputs`:\n\n```ts\nimport type { Output } from \"jsrepo/outputs\";\n\nexport function output(): Output {\n    return {\n        \n    }\n}\n```\n\nNext let's define the `output` key to define how the registry should be output:\n\n```ts title=\"src/output.ts\"\nimport type { Output } from \"jsrepo/outputs\";\n\nexport const OUTPUT_FILE = \"REGISTRY.md\";\n\nexport function output(): Output {\n    return {\n        output: async (buildResult, { cwd }) => { // [!code ++]\n            let content = `# ${buildResult.name}\\n\\n`; // [!code ++]\n            // [!code ++]\n            for (const item of buildResult.items) { // [!code ++]\n                content += `## ${item.name}\\n\\n${item.description}\\n\\n`; // [!code ++]\n            } // [!code ++]\n            // [!code ++]\n            fs.writeFileSync(path.join(cwd, OUTPUT_FILE), content); // [!code ++]\n        }, // [!code ++]\n    }\n}\n```\n\n<Callout type=\"info\">\n    The `buildResult` object contains a bunch of useful information about the registry. You can find the full type [here](https://github.com/jsrepojs/jsrepo/blob/next/packages/jsrepo/src/utils/build.ts#L31).\n</Callout>\n\nFinally we can define the `clean` key to define how to remove the output file before the next build:\n\n```ts title=\"src/output.ts\"\nimport type { Output } from \"jsrepo/outputs\";\n\nexport const OUTPUT_FILE = \"REGISTRY.md\";\n\nexport function output(): Output {\n    return {\n        output: async (buildResult, { cwd }) => {\n            let content = `# ${buildResult.name}\\n\\n`;\n\n            for (const item of buildResult.items) {\n                content += `## ${item.name}\\n\\n${item.description}\\n\\n`;\n            }\n\n            fs.writeFileSync(path.join(cwd, OUTPUT_FILE), content);\n        },\n        clean: async ({ cwd }) => { // [!code ++]\n            const manifestPath = path.join(cwd, OUTPUT_FILE); // [!code ++]\n            if (!fs.existsSync(manifestPath)) return; // [!code ++]\n            fs.rmSync(manifestPath); // [!code ++]\n        }, // [!code ++]\n    }\n}\n```\n\nNow we can use the output in our config file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { output } from \"./src/output\";\n\nexport default defineConfig({\n    registry: {\n        name: \"my-registry\",\n        outputs: [output()],\n        // ...\n    }\n});\n```\n\nAnd the result should look like this:\n\n```markdown title=\"REGISTRY.md\"\n# my-registry\n\n## button\n\nA button component.\n\n```"
  },
  {
    "path": "apps/docs/content/docs/outputs/repository.mdx",
    "content": "---\ntitle: Repository\ndescription: Output your registry as a single json file in the root of your repository.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/outputs/distributed.ts\" />\n\t<OfficialBadge />\n</BadgeGroup>\n\nThe `repository` output is used when you are serving your registry from a repository and want to minimize code duplication and tracked files.\n\n## Usage\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n\t\toutputs: [repository()], // [!code ++]\n\t},\n});\n```\n\nNow running `jsrepo build` will output a `registry.json` file to the root of your repository."
  },
  {
    "path": "apps/docs/content/docs/outputs/shadcn.mdx",
    "content": "---\ntitle: shadcn\ndescription: Output your registry as a shadcn registry.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/shadcn/src/output.ts\" />\n\t<OfficialBadge />\n\t<NpmBadge packageName=\"@jsrepo/shadcn\" />\n</BadgeGroup>\n\nThe `@jsrepo/shadcn` is a package that helps you distribute your jsrepo registry as a shadcn registry.\n\n## Usage\n\nTo get started install the package:\n\n```npm\nnpm install @jsrepo/shadcn -D\n```\n\nNext let's add the output to our config file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { output } from \"@jsrepo/shadcn/output\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\toutputs: [output({ dir: \"./public/r/shadcn\" })], // [!code ++]\n\t},\n});\n```\n\nNow running `jsrepo build` will output a **shadcn** registry to the `./public/r/shadcn` directory.\n\n## defineShadcnRegistry\n\n**shadcn** registries are defined a bit differently than **jsrepo** registries, generally this plugin should correctly resolve those differences or warn you if it is unable to. \n\nHowever if you are only distributing your registry as a **shadcn** registry and don't care about the other features of **jsrepo** you can use the `defineShadcnRegistry` function to define your registry the *\"shadcn\"* way.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { defineShadcnRegistry, output } from \"@jsrepo/shadcn\";\n\nexport default defineConfig({\n\tregistry: defineShadcnRegistry({\n\t\t// make sure you still include the output\n\t\toutputs: [output({ dir: \"./public/r/shadcn\" })],\n\t}),\n});\n```\n\n<Callout type=\"info\">\n    When using the `defineShadcnRegistry` function it's be possible to just paste the contents of the **shadcn** `registry.json` file into your config file and start building your registry with **jsrepo**.\n</Callout>\n\n## Outputting jsrepo registries alongside shadcn registries\n\nCurrently the **jsrepo** and **shadcn** outputs are not compatible to be used in the same directory. Because of this you will need to serve each registry from a different URL.\n\nThe preferred way to avoid this conflict would be to publish your registry to [jsrepo.com](https://jsrepo.com) (which also has it's own benefits).\n\nIn absence of that, the easiest way to do this and our recommendation is to postfix the **shadcn** registry output `dir` option with `/shadcn`.\n\nFor example:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { distributed } from \"jsrepo/outputs\";\nimport { output } from \"@jsrepo/shadcn/output\";\n\nexport default defineConfig({\n\tregistry: {\n\t\toutputs: [\n            distributed({ dir: \"./public/r\" }), \n            output({ dir: \"./public/r\" }) // [!code --]\n            output({ dir: \"./public/r/shadcn\" }) // [!code ++]\n        ],\n\t},\n});\n```"
  },
  {
    "path": "apps/docs/content/docs/providers/azure.mdx",
    "content": "---\ntitle: azure\ndescription: Download and add registry items from an Azure DevOps repository.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/azure.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\n## Usage\n\nTo start adding registry items from an Azure DevOps repository you can run the following command:\n\n```sh\njsrepo add azure/<org>/<project>/<repo>\n```\n\nThe Azure provider understands refs as either branches or tags:\n\n```sh\njsrepo add azure/<org>/<project>/<repo>/heads/<ref>\njsrepo add azure/<org>/<project>/<repo>/tags/<ref>\n```\n\n### Custom base urls\n\nBy default the Azure provider will use `https://dev.azure.com` as the base url. You can configure this behavior by configuring `azure` in your `jsrepo.config` file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { azure } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [azure({ baseUrl: \"https://dev.azure.com\" })], // [!code highlight]\n});\n```\n\nAlternatively you can use the `azure:https://<url>` shorthand to use a custom base url without having to touch your config file:\n\n```sh\njsrepo add azure:https://dev.azure.com/<org>/<project>/<repo>/heads/<ref>\n```\n\nThis syntax tells the Azure provider that it can resolve this URL as an Azure DevOps repository.\n\n## Deploying your registry to Azure DevOps\n\nWhen deploying your registry to Azure DevOps you will want to use the `repository` output type:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n\t\t// ...\n\t\toutputs: [repository()], // [!code ++]\n\t},\n});\n```\n\nThis will create a `registry.json` file that will contain everything users need to use your registry.\n\n<Callout type=\"warning\">\n\tThe `registry.json` file must be at the **root** of your repository otherwise users won't be able to use your registry.\n</Callout>\n\n## Authentication\n\nTo authenticate with Azure DevOps you can run the following command:\n\n```sh\njsrepo auth azure\n```\n\nYou can logout of your account by running:\n\n```sh\njsrepo auth azure --logout\n```\n\n### Environment Variable\n\nIf you prefer to use an environment variable or are in an environment where you can't use the `jsrepo auth` command you can provide the token via the `AZURE_TOKEN` environment variable.\n\n```sh\nAZURE_TOKEN=... jsrepo add \n```\n\n## Options\n\n<TypeTable\n\ttype={{\n\t\tbaseUrl: {\n\t\t\tdescription: \"The base url to use.\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: \"https://dev.azure.com\",\n\t\t},\n\t}}\n/>"
  },
  {
    "path": "apps/docs/content/docs/providers/bitbucket.mdx",
    "content": "---\ntitle: bitbucket\ndescription: Download and add registry items from a Bitbucket repository.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/bitbucket.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\n## Usage\n\nTo start adding registry items from a Bitbucket repository you can run the following command:\n\n```sh\njsrepo add https://bitbucket.org/<owner>/<repo>/<item-name>\n```\n\nThe Bitbucket provider can parse Bitbucket URLs so that you can just copy and paste a link to a Bitbucket repository and it will just work:\n\n```sh\njsrepo add https://bitbucket.org/<owner>/<repo>/<item-name>\njsrepo add https://bitbucket.org/<owner>/<repo>/src/<ref>\n```\n\n### The `bitbucket/` shorthand\n\nYou can also just use the `bitbucket/` shorthand in place of `https://bitbucket.org/`.\n\n```sh\njsrepo add bitbucket/<owner>/<repo>/<item-name>\njsrepo add bitbucket/<owner>/<repo>/src/<ref>\n```\n\n<Callout type=\"info\">\n\tIf you configured a custom `baseUrl` in your `jsrepo.config` file then using the `bitbucket/` shorthand will use your\n\tcustom base url instead of `https://bitbucket.org/`.\n</Callout>\n\n### Custom base urls\n\nBy default the Bitbucket provider will use `https://bitbucket.org` as the base url for the `bitbucket/` shorthand. You can configure this behavior by configuring `bitbucket` in your `jsrepo.config` file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { bitbucket } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [bitbucket()], // [!code ++]\n});\n```\n\nAlternatively you can use the `bitbucket:https://<url>` shorthand to use a custom base url without having to touch your config file:\n\n```sh\njsrepo add bitbucket:https://bitbucket.org/<owner>/<repo>/src/<ref>\n```\n\nThis syntax tells the Bitbucket provider that it can resolve this URL as a Bitbucket repository.\n\n## Deploying your registry to Bitbucket\n\nWhen deploying your registry to Bitbucket you will want to use the `repository` output type:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n\t\t// ...\n\t\toutputs: [repository()], // [!code ++]\n\t},\n});\n```\n\nThis will create a `registry.json` file that will contain everything users need to use your registry.\n\n<Callout type=\"warning\">\n\tThe `registry.json` file must be at the **root** of your repository otherwise users won't be able to use your registry.\n</Callout>\n\n## Authentication\n\nTo authenticate with Bitbucket you can run the following command:\n\n```sh\njsrepo auth bitbucket\n```\n\nYou can logout of your account by running:\n\n```sh\njsrepo auth bitbucket --logout\n```\n\n### Environment Variable\n\nIf you prefer to use an environment variable or are in an environment where you can't use the `jsrepo auth` command you can provide the token via the `BITBUCKET_TOKEN` environment variable.\n\n```sh\nBITBUCKET_TOKEN=... jsrepo add \n```\n\n## Options\n\n<TypeTable\n\ttype={{\n\t\tbaseUrl: {\n\t\t\tdescription: \"The base url to use when using the bitbucket/ shorthand.\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: \"https://bitbucket.org\",\n\t\t},\n\t}}\n/>"
  },
  {
    "path": "apps/docs/content/docs/providers/fs.mdx",
    "content": "---\ntitle: fs\ndescription: Download and add registry items from your local filesystem.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/fs.ts\" />\n\t<OfficialBadge />\n</BadgeGroup>\n\nThe `fs` provider allows you to distribute code from your local filesystem. This is useful when you want to test out how your registries behave without having to deploy them somewhere first.\n\n## Usage\n\nTo start adding registry items from your local filesystem you first need to configure the provider in your `jsrepo.config` file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { fs } from \"jsrepo/providers\"; // [!code ++]\n\nexport default defineConfig({\n\tproviders: [fs()], // [!code ++]\n});\n```\n\nNow you can start adding registry items from your local filesystem:\n\n```sh\njsrepo add fs://../registries/my-registry\n```\n\nYou may also want to supply a base directory to the provider:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { fs } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [fs({ baseDir: \"../registries\" })], // [!code highlight]\n});\n```\n\nNow your commands will be relative to the base directory you defined:\n\n```sh\njsrepo add fs://./my-registry\n```\n\n## Options\n\n<TypeTable\n\ttype={{\n\t\tbaseDir: {\n\t\t\tdescription: \"All paths will be relative to this directory.\",\n\t\t\ttype: \"string\",\n\t\t},\n\t}}\n/>"
  },
  {
    "path": "apps/docs/content/docs/providers/github.mdx",
    "content": "---\ntitle: github\ndescription: Download and add registry items from a GitHub repository.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/github.ts\" />\n\t<OfficialBadge />\n    <DefaultBadge />\n</BadgeGroup>\n\n## Usage\n\nTo start adding registry items from a GitHub repository you can run the following command:\n\n```sh\njsrepo add https://github.com/<owner>/<repo>/<item-name>\n```\n\nThe GitHub provider can parse GitHub URLs so that you can just copy and paste a link to a GitHub repository and it will just work:\n\n```sh\njsrepo add https://github.com/<owner>/<repo>/<item-name>\njsrepo add https://github.com/<owner>/<repo>/tree/<ref>\n```\n\n### The `github/` shorthand\n\nYou can also just use the `github/` shorthand in place of `https://github.com/`.\n\n```sh\njsrepo add github/<owner>/<repo>/<item-name>\njsrepo add github/<owner>/<repo>/tree/<ref>\n```\n\n<Callout type=\"info\">\n\tIf you configured a custom `baseUrl` in your `jsrepo.config` file then using the `github/` shorthand will use your\n\tcustom base url instead of `https://github.com/`.\n</Callout>\n\n### Custom base urls\n\nBy default the GitHub provider will use `https://github.com` as the base url for the `github/` shorthand. You can configure this behavior by configuring `github` in your `jsrepo.config` file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { github } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [github({ baseUrl: \"https://my-github-instance.com\" })], // [!code highlight]\n});\n```\n\nAlternatively you can use the `github:https://<url>` shorthand to use a custom base url without having to touch your config file:\n\n```sh\njsrepo add github:https://my-github-instance.com/<owner>/<repo>/<item-name>\n```\n\nThis syntax tells the GitHub provider that it can resolve this URL as a GitHub repository.\n\n## Authentication\n\nTo authenticate with GitHub you can run the following command:\n\n```sh\njsrepo auth github\n```\n\nYou can logout of your account by running:\n\n```sh\njsrepo auth github --logout\n```\n\n### Environment Variable\n\nIf you prefer to use an environment variable or are in an environment where you can't use the `jsrepo auth` command you can provide the token via the `GITHUB_TOKEN` environment variable.\n\n```sh\nGITHUB_TOKEN=... jsrepo add \n```\n\n## Deploying your registry to GitHub\n\nWhen deploying your registry to GitHub you will want to use the `repository` output type:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\toutputs: [repository()], // [!code ++]\n\t},\n});\n```\n\nThis will create a `registry.json` file that will contain everything users need to use your registry.\n\n<Callout type=\"warning\">\n\tThe `registry.json` file must be at the **root** of your repository otherwise users won't be able to use your registry.\n</Callout>\n\n### Workflow\n\nIf you want to use GitHub Actions to automatically build your registry when you push changes you can use this workflow:\n\n```yml title=\".github/workflows/build-registry.yml\"\nname: build-registry\non:\n  push:\n    branches:\n      - main\npermissions:\n  contents: write\n  pull-requests: write\n  id-token: write\njobs:\n  build:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - uses: actions/setup-node@v4\n        with:\n          node-version: '20'\n      - name: Install dependencies\n        run: npm install\n      - name: Build registry.json\n        run: jsrepo build\n      - name: Create pull request with changes\n        uses: peter-evans/create-pull-request@v7\n        with:\n          title: 'chore: update `registry.json`'\n          body: |\n            - Update `registry.json`\n            ---\n            This PR was auto generated\n          branch: build-registry\n          commit-message: build `registry.json`\n```\n\n## Options\n\n<TypeTable\n\ttype={{\n\t\tbaseUrl: {\n\t\t\tdescription: \"The base url to use when using the github/ shorthand.\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: \"https://github.com\",\n\t\t},\n\t}}\n/>\n"
  },
  {
    "path": "apps/docs/content/docs/providers/gitlab.mdx",
    "content": "---\ntitle: gitlab\ndescription: Download and add registry items from a GitLab repository.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/gitlab.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\n\n## Usage\n\nTo start adding registry items from a GitLab repository you can run the following command:\n\n```sh\njsrepo add https://gitlab.com/<group>/<project>/<item-name>\n```\n\nThe GitLab provider can parse GitLab URLs so that you can just copy and paste a link to a GitLab repository and it will just work:\n\n```sh\njsrepo add https://gitlab.com/<group>/<project>/<item-name>\njsrepo add https://gitlab.com/<group>/<project>/-/tree/<ref>\n```\n\n### The `gitlab/` shorthand\n\nYou can also just use the `gitlab/` shorthand in place of `https://gitlab.com/`.\n\n```sh\njsrepo add gitlab/<group>/<project>/<item-name>\njsrepo add gitlab/<group>/<subgroup>/<project>/-/tree/<ref>\n```\n\n<Callout type=\"info\">\n\tIf you configured a custom `baseUrl` in your `jsrepo.config` file then using the `gitlab/` shorthand will use your\n\tcustom base url instead of `https://gitlab.com/`.\n</Callout>\n\n### Custom base urls\n\nBy default the GitLab provider will use `https://gitlab.com` as the base url for the `gitlab/` shorthand. You can configure this behavior by configuring `gitlab` in your `jsrepo.config` file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { gitlab } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [gitlab({ baseUrl: \"https://my-gitlab-instance.com\" })], // [!code highlight]\n});\n```\n\nAlternatively you can use the `gitlab:https://<url>` shorthand to use a custom base url without having to touch your config file:\n\n```sh\njsrepo add gitlab:https://my-gitlab-instance.com/<group>/<project>/<item-name>\n```\n\nThis syntax tells the GitLab provider that it can resolve this URL as a GitLab repository.\n\n## Deploying your registry to GitLab\n\nWhen deploying your registry to GitLab you will want to use the `repository` output type:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { repository } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\tregistry: {\n\t\t// ...\n\t\toutputs: [repository()], // [!code ++]\n\t},\n});\n```\n\nThis will create a `registry.json` file that will contain everything users need to use your registry.\n\n<Callout type=\"warning\">\n\tThe `registry.json` file must be at the **root** of your repository otherwise users won't be able to use your registry.\n</Callout>\n\n## Authentication\n\nTo authenticate with GitLab you can run the following command:\n\n```sh\njsrepo auth gitlab\n```\n\nYou can logout of your account by running:\n\n```sh\njsrepo auth gitlab --logout\n```\n\n### Environment Variable\n\nIf you prefer to use an environment variable or are in an environment where you can't use the `jsrepo auth` command you can provide the token via the `GITLAB_TOKEN` environment variable.\n\n```sh\nGITLAB_TOKEN=... jsrepo add \n```\n\n## Options\n\n<TypeTable\n\ttype={{\n\t\tbaseUrl: {\n\t\t\tdescription: \"The base url to use when using the gitlab/ shorthand.\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: \"https://gitlab.com\",\n\t\t},\n\t}}\n/>\n\n"
  },
  {
    "path": "apps/docs/content/docs/providers/http.mdx",
    "content": "---\ntitle: http\ndescription: Download and add registry items from an arbitrary HTTP endpoint.\n---\n\nimport { Files, Folder, File } from \"@/components/files\";\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/http.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\nThe http provider is the simplest provider by far. To add registry items simply provide the base url of the `registry.json` file:\n\n```sh\njsrepo add https://example.com/registry\n```\n\n**jsrepo** will then find the `registry.json` file from `https://example.com/registry/registry.json`.\n\n## Deploying a registry to your own website\n\nWhen deploying a registry to your own website you will want to use the `distributed` output type:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { distributed } from \"jsrepo/outputs\"; // [!code ++]\n\nexport default defineConfig({\n\t// `dir` is the directory to output the files to\n\toutputs: [distributed({ dir: \"./public/r\" })], // [!code ++]\n});\n```\n\nThis will output your registry to the `public/r` directory which might look something like this:\n\n<Files>\n\t<Folder name=\"public\" defaultOpen>\n\t\t<Folder name=\"r\" defaultOpen>\n\t\t\t<File name=\"registry.json\" />\n\t\t\t<File name=\"button.json\" />\n\t\t\t<File name=\"utils.json\" />\n\t\t</Folder>\n\t</Folder>\n</Files>\n\nUsers will then be able to add registry items to their project by running:\n\n```sh\njsrepo add https://your-website.com/r\n```\n\n## Authentication\n\nTo authenticate with the http provider you can run the following command:\n\n```sh\njsrepo auth http\n```\n\nYou will then be prompted to select or enter the name of a registry to authenticate to.\n\nOnce authenticated that token will continue to be used for that registry until you logout.\n\nYou can logout of your account by running:\n\n```sh\njsrepo auth http --logout\n```\n\n## Options\n\n### `baseUrl`\n\nThe `baseUrl` option allows you to configure what registries this provider will match. \n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { http } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\t// only use this provider for registries starting with https://myregistry.com\n\tproviders: [\n\t\thttp({ \n\t\t\tbaseUrl: \"https://myregistry.com\" // [!code ++]\n\t\t})\n\t],\n});\n```\n\n### `authHeader`\n\nThe `authHeader` function allows you to set headers for every request using the provided token. This is useful for when you need to authenticate to a registry that doesn't support the Bearer token format.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { http } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [\n\t\thttp({ \n\t\t\t// set your custom headers here\n\t\t\tauthHeader: (token) => ({ 'X-API-Key': token }) // [!code ++]\n\t\t})\n\t],\n});\n```\n\n### `headers`\n\nYou can set custom headers on every request by providing the `headers` option:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { http } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [\n\t\thttp({\n\t\t\theaders: {  // [!code ++]\n\t\t\t\t'X-Custom-Header': 'custom value' // [!code ++]\n\t\t\t} // [!code ++]\n\t\t})\n\t],\n});\n```\n\nOne way you might use headers is by providing an authorization token via an environment variable:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { http } from \"jsrepo/providers\";\n\nexport default defineConfig({\n\tproviders: [\n\t\thttp({\n\t\t\tbaseUrl: \"https://myregistry.com\", // [!code ++]\n\t\t\theaders: {  // [!code ++]\n\t\t\t\tAuthorization: `Bearer ${process.env.MYREGISTRY_TOKEN}` // [!code ++]\n\t\t\t} // [!code ++]\n\t\t})\n\t],\n});\n```\n\nNow anyone running `jsrepo add` will have the `Authorization` header set to the value of the `MYREGISTRY_TOKEN` environment variable when making requests to `https://myregistry.com/registry`."
  },
  {
    "path": "apps/docs/content/docs/providers/index.mdx",
    "content": "---\ntitle: Providers\ndescription: Host your code anywhere with jsrepo.\n---\n\nProviders are how **jsrepo** knows where to find registry items. When you provide a registry identifier to a **jsrepo** command the provider is responsible for resolving that to a usable URL.\n\nFor example you might run the following command:\n\n```sh\njsrepo init github/ieedan/std\n```\n\nIn this case the `github` provider will be used to resolve the path to the `registry.json` file which might look something like this:\n\n```plaintext\nhttps://api.github.com/repos/ieedan/std/contents/registry.json?ref=main\n```\n\nThis makes **jsrepo** more flexible than any other registry because you can host your registry anywhere.\n\n## Available providers\n\n<Cards>\n\t<Card href=\"/docs/providers/jsrepo\" icon={<JsrepoLogo />} title=\"jsrepo\">\n\t\tDownload and add registry items from jsrepo.com\n\t</Card>\n\t<Card href=\"/docs/providers/github\" icon={<GitHubLogo />} title=\"GitHub\">\n\t\tDownload and add registry items from a GitHub repository.\n\t</Card>\n\t<Card href=\"/docs/providers/gitlab\" icon={<GitLabLogo />} title=\"GitLab\">\n\t\tDownload and add registry items from a GitLab repository.\n\t</Card>\n\t<Card href=\"/docs/providers/bitbucket\" icon={<BitbucketLogo />} title=\"Bitbucket\">\n\t\tDownload and add registry items from a Bitbucket repository.\n\t</Card>\n\t<Card href=\"/docs/providers/azure\" icon={<AzureDevopsLogo />} title=\"AzureDevops\">\n\t\tDownload and add registry items from an Azure DevOps repository.\n\t</Card>\n\t<Card href=\"/docs/providers/http\" icon={<GlobeIcon />} title=\"Your Website\">\n\t\tDownload and add registry items from your own website.\n\t</Card>\n\t<Card href=\"/docs/providers/fs\" icon={<FolderIcon />} title=\"Local Filesystem\">\n\t\tDownload and add registry items from your local filesystem.\n\t</Card>\n</Cards>\n\nYou can customize the providers that **jsrepo** uses by adding them to your `jsrepo.config` file.\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport { github } from \"jsrepo/providers\"; // [!code ++]\n\nexport default defineConfig({\n\tproviders: [github()], // [!code ++]\n});\n```\n\n## Provider plugins\n\nProvider plugins are a way to add support for additional providers to **jsrepo**.\n\n### Adding provider plugins\n\nYou can add provider plugins by running the following command:\n\n```sh\njsrepo config provider <plugin>\n```\n\nThis will automatically install the plugin and add it to your config file:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig, DEFAULT_PROVIDERS } from \"jsrepo\"; // [!code ++]\nimport myProvider from \"jsrepo-provider-myprovider\"; // [!code ++]\n\nexport default defineConfig({\n\tproviders: [...DEFAULT_PROVIDERS, myProvider()], // [!code ++]\n});\n```\n\n<Callout type=\"info\">\n\tYou will notice the addition of the `DEFAULT_PROVIDERS` object in the example above. This way you can continue to\n\tuse all other providers alongside your provider plugin.\n</Callout>\n"
  },
  {
    "path": "apps/docs/content/docs/providers/jsrepo.mdx",
    "content": "---\ntitle: jsrepo\ndescription: Download and add registry items from jsrepo.com\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/jsrepo/src/providers/jsrepo.ts\" />\n\t<OfficialBadge />\n\t<DefaultBadge />\n</BadgeGroup>\n\n**jsrepo.com** is a centralized source registry for registries. Much like npm is for packages, **jsrepo.com** is for registries.\n\n**jsrepo.com** comes with a few added benefits over other registry providers:\n\n-   Semver support\n-   First class support for private registries with the `jsrepo auth` command\n-   Discoverability of your registry through the **jsrepo.com** website (also by the jsrepo mcp agent)\n-   Easier installation for your users with simple `@<scope>/<registry>` syntax\n\n## Usage\n\nTo start adding registry items from **jsrepo.com** you can run the following command:\n\n```sh\njsrepo add @<scope>/<registry>\n```\n\nYou can also add registries at a specific version:\n\n```sh\njsrepo add @<scope>/<registry>@1.0.0\n```\n\n## Authentication\n\nTo authenticate with jsrepo.com you can run the following command:\n\n```sh\njsrepo auth jsrepo\n```\n\nOnce authenticated you will have access to all registries associated with your account public and private as well as the ability to publish registries.\n\nYou can logout of your account by running:\n\n```sh\njsrepo auth jsrepo --logout\n```\n\n### Environment Variable\n\nIf you prefer to use an environment variable or are in an environment where you can't use the `jsrepo auth` command you can provide the token via the `JSREPO_TOKEN` environment variable.\n\n```sh\nJSREPO_TOKEN=... jsrepo publish \n```\n\n## Publishing your registry to jsrepo.com\n\nOnce you have authenticated you can publish your registry with the `publish` command:\n\n```sh\njsrepo publish\n```\n\nFor a more detailed guide on publishing your registry to jsrepo.com checkout the guide [here](/docs/jsrepo-com#publishing-your-registry).\n\n"
  },
  {
    "path": "apps/docs/content/docs/providers/shadcn.mdx",
    "content": "---\ntitle: shadcn\ndescription: Download and add items from the shadcn registry index.\n---\n\nimport { RegistryDirectory } from \"@/components/registry-index\";\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/shadcn/src/provider.ts\" />\n\t<OfficialBadge />\n</BadgeGroup>\n\n## Usage\n\nThe **shadcn** registry index is a nice way to shorten shadcn registry urls. If you are making use of shadcn registries in your project you can use this provider to use registries by their shortened name.\n\nSince this doesn't come by default you will need to add it to your `jsrepo.config.ts` file. You can do this with a command:\n\n```sh\njsrepo config provider @jsrepo/shadcn\n```\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport shadcn from \"@jsrepo/shadcn\"; // [!code ++]\n\nexport default defineConfig({\n\tproviders: [shadcn()], // [!code ++]\n});\n```\n\nNow you can simply use the `shadcn:` prefix followed by the registry namespace:\n\n```sh\njsrepo add --registry shadcn:@react-bits\n```\n\n## Options\n\n<TypeTable\n\ttype={{\n\t\tregistryIndexUrl: {\n\t\t\tdescription: \"The base url of the registry index.\",\n\t\t\ttype: \"string\",\n\t\t\tdefault: \"https://ui.shadcn.com/r/registries.json\",\n\t\t},\n\t}}\n/>\n\n## Registry Directory\n\nAll of these registries are available to use with the **shadcn** provider.\n\n<RegistryDirectory />"
  },
  {
    "path": "apps/docs/content/docs/transforms/biome.mdx",
    "content": "---\ntitle: Biome\ndescription: Format code before it's added to your project using Biome.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/transform-biome/src/index.ts\" />\n\t<OfficialBadge />\n\t<NpmBadge packageName=\"@jsrepo/transform-biome\" />\n</BadgeGroup>\n\nThe [@jsrepo/transform-biome](https://npmjs.com/package/@jsrepo/transform-biome) package is a transform plugin for **jsrepo** that formats code using your local biome configuration before it's added to your project using [Biome](https://biomejs.dev).\n\n<Callout type=\"info\">\n    This is especially useful when you are making use of the `jsrepo update` command as it will format the code the same way as it is in your project preventing any formatting changes from being shown in the diff.\n</Callout>\n\n## Installation\n\nTo add the **@jsrepo/transform-biome** transform to your config run the following command:\n\n```sh\njsrepo config transform biome\n```\n\nThis will automatically install the transform and add it to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport biome from \"@jsrepo/transform-biome\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [biome()], // [!code ++]\n});\n```"
  },
  {
    "path": "apps/docs/content/docs/transforms/filecasing.mdx",
    "content": "---\ntitle: File Casing\ndescription: Transform file and folder names to different case formats before adding them to your project.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/transform-filecasing/src/index.ts\" />\n\t<OfficialBadge />\n\t<NpmBadge packageName=\"@jsrepo/transform-filecasing\" />\n</BadgeGroup>\n\nThe [@jsrepo/transform-filecasing](https://npmjs.com/package/@jsrepo/transform-filecasing) package is a transform plugin for **jsrepo** that transforms file and folder names to different case formats before adding them to your project.\n\n<Callout type=\"info\">\n    This is useful when your project follows a specific file or folder naming convention and you want registry items to automatically conform to it.\n</Callout>\n\n<Callout type=\"info\">\n    The transform only affects the item's relative path, not the base path configured in your jsrepo.config. For example, if your config specifies `src/lib/components/ui` as the path for components, and you add a button component, only the part after that base path is transformed: `button/index.ts` → `Button/Index.ts`, resulting in `src/lib/components/ui/Button/Index.ts`.\n</Callout>\n\n## Installation\n\nTo add the **@jsrepo/transform-filecasing** transform to your config run the following command:\n\n```sh\njsrepo config transform filecasing\n```\n\nThis will automatically install the transform and add it to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport fileCasing from \"@jsrepo/transform-filecasing\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [fileCasing({ to: \"camel\" })], // [!code ++]\n});\n```\n\nNow when a file is added to your project its file and folder names will be transformed to the target case format. Note that only the item's relative path is transformed, not the base path from your config:\n\n```ts title=\"Config path: 'src/lib/components/ui', Item: 'button/index.ts' → Result: 'src/lib/components/ui/Button/Index.ts'\"\n// Config path: 'src/lib/components/ui'\n// Item path: 'button/index.ts'\n// Result: 'src/lib/components/ui/Button/Index.ts'\n```\n\n## Configuration\n\nThe transform accepts an options object with the following properties:\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `to` | `\"kebab\" \\| \"camel\" \\| \"snake\" \\| \"pascal\"` | The target case format for file and folder names |\n| `transformDirectories` | `boolean` | Whether to transform directory segments in the path. When `false`, only the filename baseName is transformed. Defaults to `true` |\n\n### Transform Directories and Filenames (Default)\n\nBy default, both directory segments and filenames are transformed:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport fileCasing from \"@jsrepo/transform-filecasing\";\n\nexport default defineConfig({\n\t// Config path: 'src/lib/components/ui'\n\ttransforms: [fileCasing({ to: \"pascal\" })],\n});\n```\n\nThis will transform the item's relative path:\n- Item: `button/index.ts` → Result: `src/lib/components/ui/Button/Index.ts`\n- Item: `my-components/use-hook.ts` → Result: `src/lib/components/ui/MyComponents/UseHook.ts`\n\nNote that the config path (`src/lib/components/ui`) is never transformed.\n\n### Transform Only Filenames\n\nTo preserve directory names and only transform filenames, set `transformDirectories` to `false`:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport fileCasing from \"@jsrepo/transform-filecasing\";\n\nexport default defineConfig({\n\ttransforms: [fileCasing({ to: \"camel\", transformDirectories: false })],\n});\n```\n\nThis will transform only the filename in the item's relative path:\n- Item: `my-components/use-hook.ts` → Result: `src/lib/components/ui/my-components/useHook.ts`\n- Item: `button/index.ts` → Result: `src/lib/components/ui/button/index.ts`\n\nNote that directory names in the item path are preserved, and the config path is never transformed.\n\n<Callout type=\"info\">\n    This only transforms directories and files within added items and will never rename existing files or directories in your project.\n</Callout>\n"
  },
  {
    "path": "apps/docs/content/docs/transforms/index.mdx",
    "content": "---\ntitle: Transforms\ndescription: Modify code before it is added to your project.\n---\n\nTransforms are a way to modify code before it's added to your project. You can use transforms to format code, add comments, or any other code modification you can think of.\n\nTransforms are also pluggable so you can create your own and share them with the community. Here are a few of the officially supported transforms:\n<Cards>\n\t<Card href=\"/docs/transforms/prettier\" icon={<PrettierLogo />} title=\"@jsrepo/transform-prettier\">\n\t\tFormat code before it's added to your project using **Prettier**.\n\t</Card>\n\t<Card href=\"/docs/transforms/biome\" icon={<BiomeLogo />} title=\"@jsrepo/transform-biome\">\n\t\tFormat code before it's added to your project using **Biome**.\n\t</Card>\n\t<Card href=\"/docs/transforms/oxfmt\" icon={<OxfmtLogo />} title=\"@jsrepo/transform-oxfmt\">\n\t\tFormat code before it's added to your project using **oxfmt**.\n\t</Card>\n\t<Card href=\"/docs/transforms/javascript\" icon={<JavaScriptLogo />} title=\"@jsrepo/transform-javascript\">\n\t\tTransform TypeScript registry items into JavaScript before adding them to your project.\n\t</Card>\n\t<Card href=\"/docs/transforms/filecasing\" title=\"@jsrepo/transform-filecasing\">\n\t\tTransform file and folder names to different case formats before adding them to your project.\n\t</Card>\n</Cards>\n\n## Adding transforms\n\nYou can add transforms to your config by running the following command:\n\n```sh\njsrepo config transform jsrepo-transform-my-transform\n```\n\nThis will automatically install the transform and add it to your config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport myTransform from \"jsrepo-transform-my-transform\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [myTransform()], // [!code ++]\n});\n```\n\n## Transform authors\n\nWhen creating transforms we recommend you follow a few rules:\n\n1. Name your transform package with the following naming convention: `jsrepo-transform-<name>` or `@<org>/jsrepo-transform-<name>`\n2. Export the transform as the `default` export\n\nThe name of your transform in the config will be the name of your package without the `jsrepo-transform-` prefix converted to camel case.\n\nFor example:\n\n```plaintext\njsrepo-transform-my-transform -> myTransform\n@my-org/jsrepo-transform-my-transform -> myTransform\n```\n\nOfficial plugins are an exception to this rule to prevent having to name packages like `@jsrepo/jsrepo-transform-prettier`. \n\nFor instance:\n\n```plaintext\n@jsrepo/transform-prettier -> prettier\n@jsrepo/transform-faster-prettier -> fasterPrettier\n```\n\n## Creating your own transform\n\nTo demonstrate the power of transforms let's create a simple transform that adds a comment at the top of each file letting the user know what registry the item was added from.\n\n```ts title=\"src/transform.ts\"\nimport type { Transform } from \"jsrepo\";\n\nexport default function(): Transform {\n\treturn {\n\t\ttransform: async (code, fileName, { registryUrl }) => {\n\t\t\tif (fileName.endsWith('.ts') || fileName.endsWith('.js')) {\n\t\t\t\treturn { code: `// Added from ${registryUrl}\\n\\n${code}` };\n\t\t\t}\n\n\t\t\t// return nothing since no changes were made\n\t\t\treturn { };\n\t\t},\n\t};\n}\n```\n\nNow we can use the transform in our config:\n\n```ts title=\"jsrepo.config.ts\"\nimport { defineConfig } from \"jsrepo\";\nimport addComment from \"./src/transform\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [addComment()], // [!code ++]\n});\n```\n\nNow when users add items from the registry they will include the comment at the top of the file letting them know what registry the item was added from:\n\n```ts title=\"src/math/add.ts\"\n// Added from https://example.com/registry // [!code highlight]\n\nexport function add(a: number, b: number): number {\n\treturn a + b;\n}\n```\n"
  },
  {
    "path": "apps/docs/content/docs/transforms/javascript.mdx",
    "content": "---\ntitle: JavaScript\ndescription: Transform TypeScript registry items into JavaScript before adding them to your project.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/transform-javascript/src/index.ts\" />\n\t<OfficialBadge />\n\t<NpmBadge packageName=\"@jsrepo/transform-javascript\" />\n</BadgeGroup>\n\nThe [@jsrepo/transform-javascript](https://npmjs.com/package/@jsrepo/transform-javascript) package is a transform plugin for **jsrepo** that converts TypeScript registry items into JavaScript before adding them to your project.\n\n<Callout type=\"warning\">\n    This only works for [erasable syntax](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html#the---erasablesyntaxonly-option).\n</Callout>\n\n## Installation\n\n```sh\njsrepo config transform javascript\n# or initialize any jsrepo project with the --js flag\njsrepo init @ieedan/std --js\n```\n\nThis will automatically install the transform and add it to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport javascript from \"@jsrepo/transform-javascript\"; // [!code ++]\n\nexport default defineConfig({\n\t// ...\n\ttransforms: [javascript()], // [!code ++]\n});\n```\n\nThis plugin will then transform any registry items written in TypeScript into JavaScript before adding them to your project.\n\n**Before:**\n\n```ts title=\"math/add.ts\"\nexport function add(a: number, b: number): number {\n\treturn a + b;\n}\n```\n\n**After:**\n\n```ts title=\"math/add.js\"\nexport function add(a: number, b: number): number { // [!code --]\nexport function add(a, b) { // [!code ++]\n\treturn a + b;\n}\n```\n\n## Supported Languages\n\nIf your language of choice is not supported here feel free to contribute it!\n\n- ✅ Full support\n- ⚠️ Partial support\n- ❌ No support\n\n\n| Language   | Support |\n|------------|---------|\n| TypeScript | ✅      |\n| Svelte     | ✅      |\n"
  },
  {
    "path": "apps/docs/content/docs/transforms/oxfmt.mdx",
    "content": "---\ntitle: oxfmt\ndescription: Format code before it's added to your project using oxfmt.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/transform-oxfmt/src/index.ts\" />\n\t<OfficialBadge />\n\t<NpmBadge packageName=\"@jsrepo/transform-oxfmt\" />\n</BadgeGroup>\n\nThe [@jsrepo/transform-oxfmt](https://npmjs.com/package/@jsrepo/transform-oxfmt) package is a transform plugin for **jsrepo** that formats code using [oxfmt](https://github.com/oxc-project/oxfmt) before it's added to your project.\n\n<Callout type=\"info\">\n    This is especially useful when you are making use of the `jsrepo update` command as it will format the code the same way as it is in your project preventing any formatting changes from being shown in the diff.\n</Callout>\n\n## Installation\n\nTo add the **@jsrepo/transform-oxfmt** transform to your config run the following command:\n\n```sh\njsrepo config transform oxfmt\n```\n\nThis will automatically install the transform and add it to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport oxfmt from \"@jsrepo/transform-oxfmt\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [oxfmt()], // [!code ++]\n});\n```\n"
  },
  {
    "path": "apps/docs/content/docs/transforms/prettier.mdx",
    "content": "---\ntitle: Prettier\ndescription: Format code before it's added to your project using Prettier.\n---\n\n<BadgeGroup>\n\t<SourceBadge path=\"packages/transform-prettier/src/index.ts\" />\n\t<OfficialBadge />\n\t<NpmBadge packageName=\"@jsrepo/transform-prettier\" />\n</BadgeGroup>\n\nThe [@jsrepo/transform-prettier](https://npmjs.com/package/@jsrepo/transform-prettier) package is a transform plugin for **jsrepo** that formats code using your local Prettier configuration before it's added to your project using [Prettier](https://prettier.io).\n\n<Callout type=\"info\">\n    This is especially useful when you are making use of the `jsrepo update` command as it will format the code the same way as it is in your project preventing any formatting changes from being shown in the diff.\n</Callout>\n\n## Installation\n\nTo add the **@jsrepo/transform-prettier** transform to your config run the following command:\n\n```sh\njsrepo config transform prettier\n```\n\nThis will automatically install the transform and add it to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport prettier from \"@jsrepo/transform-prettier\"; // [!code ++]\n\nexport default defineConfig({\n\ttransforms: [prettier()], // [!code ++]\n});\n```"
  },
  {
    "path": "apps/docs/instrumentation-client.js",
    "content": "import posthog from \"posthog-js\";\n\nif (process.env.NODE_ENV === 'production') {\n    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {\n        api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,\n        defaults: \"2025-05-24\"\n    });\n}\n"
  },
  {
    "path": "apps/docs/jsrepo.config.mts",
    "content": "import { defineConfig, DEFAULT_PROVIDERS } from \"jsrepo\";\nimport shadcn from '@jsrepo/shadcn';\n\nexport default defineConfig({\n\tregistries: [\"https://reactbits.dev/r\", \"https://magicui.design/r\", \"https://ui.shadcn.com/r/styles/new-york-v4\"],\n\tpaths: {\n\t\tcomponent: '@/components',\n\t\tutil: '@/lib/utils',\n\t\tui: '@/components/ui',\n\t\tlib: '@/lib',\n\t\thook: '@/hooks',\n\t},\n\tproviders: [...DEFAULT_PROVIDERS, shadcn()]\n});\n"
  },
  {
    "path": "apps/docs/middleware.ts",
    "content": "import { NextRequest, NextResponse } from \"next/server\";\nimport { isMarkdownPreferred, rewritePath } from \"fumadocs-core/negotiation\";\n\nconst { rewrite: rewriteLLM } = rewritePath(\"/docs/*path\", \"/llms.mdx/*path\");\n\nexport function middleware(request: NextRequest) {\n\tif (isMarkdownPreferred(request)) {\n\t\tconst result = rewriteLLM(request.nextUrl.pathname);\n\n\t\tif (result) {\n\t\t\treturn NextResponse.rewrite(new URL(result, request.nextUrl));\n\t\t}\n\t}\n\n\treturn NextResponse.next();\n}\n"
  },
  {
    "path": "apps/docs/next.config.mjs",
    "content": "import { createMDX } from \"fumadocs-mdx/next\";\n\nconst withMDX = createMDX();\n\n/** @type {import('next').NextConfig} */\nconst config = {\n\treactStrictMode: true,\n\tasync rewrites() {\n\t\treturn [\n\t\t\t{\n\t\t\t\tsource: \"/docs/:path*.mdx\",\n\t\t\t\tdestination: \"/llms.mdx/:path*\",\n\t\t\t},\n\t\t];\n\t},\n};\n\nexport default withMDX(config);\n"
  },
  {
    "path": "apps/docs/og-image.d.ts",
    "content": "/**\n * Type declarations for @vercel/og ImageResponse.\n * The `tw` prop allows Tailwind-style class names for OG image generation.\n * @see https://vercel.com/docs/og-image-generation\n */\nexport {};\n\ndeclare module \"react\" {\n\tinterface HTMLAttributes<T> {\n\t\ttw?: string;\n\t}\n}\n"
  },
  {
    "path": "apps/docs/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/docs\",\n\t\"version\": \"0.0.0\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"build\": \"next build\",\n\t\t\"dev\": \"next dev --turbo\",\n\t\t\"start\": \"next start\",\n\t\t\"postinstall\": \"fumadocs-mdx\"\n\t},\n\t\"dependencies\": {\n\t\t\"@gsap/react\": \"^2.1.2\",\n\t\t\"@radix-ui/react-accordion\": \"^1.2.12\",\n\t\t\"@radix-ui/react-collapsible\": \"^1.1.12\",\n\t\t\"@radix-ui/react-label\": \"^2.1.8\",\n\t\t\"@radix-ui/react-navigation-menu\": \"^1.2.14\",\n\t\t\"@radix-ui/react-popover\": \"^1.1.15\",\n\t\t\"@radix-ui/react-presence\": \"^1.1.5\",\n\t\t\"@radix-ui/react-scroll-area\": \"^1.2.10\",\n\t\t\"@radix-ui/react-separator\": \"^1.1.8\",\n\t\t\"@radix-ui/react-slot\": \"^1.2.4\",\n\t\t\"@radix-ui/react-tabs\": \"^1.1.13\",\n\t\t\"@radix-ui/react-tooltip\": \"^1.2.8\",\n\t\t\"@shikijs/transformers\": \"3.14.0\",\n\t\t\"@tanstack/react-query\": \"^5.90.21\",\n\t\t\"class-variance-authority\": \"^0.7.1\",\n\t\t\"clsx\": \"^2.1.1\",\n\t\t\"fumadocs-core\": \"16.0.7\",\n\t\t\"fumadocs-mdx\": \"13.0.5\",\n\t\t\"fumadocs-ui\": \"16.0.7\",\n\t\t\"gsap\": \"^3.13.0\",\n\t\t\"lucide-react\": \"^0.575.0\",\n\t\t\"next\": \"16.1.6\",\n\t\t\"next-themes\": \"^0.4.6\",\n\t\t\"ogl\": \"^1.0.11\",\n\t\t\"react\": \"19.2.4\",\n\t\t\"react-dom\": \"19.2.4\",\n\t\t\"sonner\": \"^2.0.7\",\n\t\t\"tailwind-merge\": \"^3.5.0\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@jsrepo/shadcn\": \"workspace:*\",\n\t\t\"@tailwindcss/postcss\": \"^4.2.0\",\n\t\t\"@types/mdx\": \"^2.0.13\",\n\t\t\"@types/node\": \"25.3.0\",\n\t\t\"@types/react\": \"^19.2.14\",\n\t\t\"@types/react-dom\": \"^19.2.3\",\n\t\t\"fuzzysort\": \"^3.1.0\",\n\t\t\"jsrepo\": \"workspace:*\",\n\t\t\"motion\": \"^12.34.3\",\n\t\t\"postcss\": \"^8.5.6\",\n\t\t\"posthog-js\": \"^1.352.0\",\n\t\t\"tailwindcss\": \"^4.2.0\",\n\t\t\"tw-animate-css\": \"^1.4.0\",\n\t\t\"typescript\": \"^5.9.3\",\n\t\t\"zod\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "apps/docs/postcss.config.mjs",
    "content": "export default {\n  plugins: {\n    '@tailwindcss/postcss': {},\n  },\n};\n"
  },
  {
    "path": "apps/docs/source.config.ts",
    "content": "import { defineConfig, defineDocs, frontmatterSchema, metaSchema } from \"fumadocs-mdx/config\";\nimport { remarkMdxFiles } from \"fumadocs-core/mdx-plugins\";\n\n// You can customize Zod schemas for frontmatter and `meta.json` here\n// see https://fumadocs.dev/docs/mdx/collections\nexport const docs = defineDocs({\n\tdocs: {\n\t\tschema: frontmatterSchema,\n\t\tpostprocess: {\n\t\t\tincludeProcessedMarkdown: true,\n\t\t},\n\t},\n\tmeta: {\n\t\tschema: metaSchema,\n\t},\n});\n\nexport default defineConfig({\n\tmdxOptions: {\n\t\tremarkPlugins: [remarkMdxFiles],\n\t},\n});\n"
  },
  {
    "path": "apps/docs/src/app/(home)/code-block.tsx",
    "content": "import * as Base from \"fumadocs-ui/components/codeblock\";\nimport { highlight } from \"fumadocs-core/highlight\";\nimport { type HTMLAttributes } from \"react\";\nimport { transformerNotationDiff, transformerNotationHighlight } from \"@shikijs/transformers\";\nimport { cn } from \"@/lib/utils\";\n\nexport async function CodeBlock({\n\tcode,\n\tlang,\n\tclassName,\n\t...rest\n}: HTMLAttributes<HTMLElement> & {\n\tcode: string;\n\tlang: string;\n}) {\n\tconst rendered = await highlight(code, {\n\t\tlang,\n\t\tcomponents: {\n\t\t\tpre: (props) => <Base.Pre {...props} />,\n\t\t},\n\t\ttransformers: [transformerNotationDiff(), transformerNotationHighlight()],\n\t});\n\n\treturn (\n\t\t<Base.CodeBlock className={cn(\"h-full my-0 [&_div.overflow-auto]:h-full\", className)} {...rest}>\n\t\t\t{rendered}\n\t\t</Base.CodeBlock>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/app/(home)/layout.tsx",
    "content": "import { HomeLayout } from \"@/components/layout/home\";\nimport { baseOptions } from \"@/lib/layout.shared\";\n\nexport default function Layout({ children }: LayoutProps<\"/\">) {\n\treturn (\n\t\t<HomeLayout\n\t\t\t{...baseOptions()}\n\t\t\tlinks={[\n\t\t\t\t{\n\t\t\t\t\ttext: <>Docs</>,\n\t\t\t\t\turl: \"/docs\",\n\t\t\t\t},\n\t\t\t]}\n\t\t>\n\t\t\t{children}\n\t\t</HomeLayout>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/app/(home)/page.tsx",
    "content": "import Link from \"next/link\";\nimport type { Metadata } from \"next\";\nimport { Button } from \"@/components/ui/button\";\nimport { FeatureTabs, FeatureTabsList, FeatureTabsTrigger, FeatureTabsContent } from \"@/components/feature-tabs\";\nimport { AnimatedSpan, Terminal, TypingAnimation } from \"@/components/ui/terminal\";\nimport { CodeBlock } from \"./code-block\";\nimport { ProvidersSection } from \"./providers-section\";\nimport { cn } from \"@/lib/utils\";\nimport PrismaticBurst from \"@/components/PrismaticBurst\";\nimport { ExternalLinkIcon } from \"lucide-react\";\n\nexport const metadata: Metadata = {\n\ttitle: \"jsrepo.dev - The modern registry toolchain\",\n\tdescription: \"jsrepo - The modern registry toolchain\",\n\topenGraph: {\n\t\ttitle: \"jsrepo.dev\",\n\t\tdescription: \"jsrepo - The modern registry toolchain\",\n\t\ttype: \"website\",\n\t\tsiteName: \"jsrepo.dev\",\n\t\timages: [\n\t\t\t{\n\t\t\t\turl: \"/og\",\n\t\t\t\twidth: 1200,\n\t\t\t\theight: 630,\n\t\t\t},\n\t\t],\n\t},\n\ttwitter: {\n\t\tcard: \"summary_large_image\",\n\t\ttitle: \"jsrepo.dev\",\n\t\tdescription: \"jsrepo - The modern registry toolchain\",\n\t\timages: [\n\t\t\t{\n\t\t\t\turl: \"/og\",\n\t\t\t\twidth: 1200,\n\t\t\t\theight: 630,\n\t\t\t},\n\t\t],\n\t},\n\tmetadataBase: new URL(\"https://jsrepo.dev\"),\n};\n\nexport default function HomePage() {\n\treturn (\n\t\t<>\n\t\t\t<main className=\"flex flex-1 flex-col gap-12 justify-center text-center\">\n\t\t\t\t<HeroSection />\n\t\t\t\t<div className=\"flex flex-col w-full\">\n\t\t\t\t\t<FeatureAccordionSection />\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ProvidersSection />\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<PluginsSection />\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ShadcnCompatibilitySection />\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<LLMsSection />\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<RestEasySection />\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t\t\t\t<div className=\"max-w-6xl relative w-full border-x text-start p-10 flex items-center justify-center flex-col gap-3\">\n\t\t\t\t\t\t\t<div className=\"relative z-10 flex items-center justify-center flex-col gap-3\">\n\t\t\t\t\t\t\t\t<h2 className=\"text-2xl font-bold text-center\">Ready to level up your registry?</h2>\n\t\t\t\t\t\t\t\t<Button asChild>\n\t\t\t\t\t\t\t\t\t<Link href=\"/docs/create-a-registry\">Start Building</Link>\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"size-full absolute\">\n\t\t\t\t\t\t\t\t<PrismaticBurst\n\t\t\t\t\t\t\t\t\tanimationType=\"rotate3d\"\n\t\t\t\t\t\t\t\t\tintensity={2}\n\t\t\t\t\t\t\t\t\tspeed={0.5}\n\t\t\t\t\t\t\t\t\tdistort={1.0}\n\t\t\t\t\t\t\t\t\tpaused={false}\n\t\t\t\t\t\t\t\t\toffset={{ x: 0, y: 0 }}\n\t\t\t\t\t\t\t\t\thoverDampness={0.25}\n\t\t\t\t\t\t\t\t\trayCount={24}\n\t\t\t\t\t\t\t\t\tmixBlendMode=\"lighten\"\n\t\t\t\t\t\t\t\t\tcolors={[\"#ff007a\", \"#4d3dff\", \"#ffffff\"]}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</main>\n\t\t\t<div className=\"flex flex-col items-center px-6\">\n\t\t\t\t<footer className=\"flex items-center justify-between py-8 w-full max-w-6xl px-6 gap-6\">\n\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t© {new Date().getFullYear()} jsrepo, All rights reserved.\n\t\t\t\t\t</span>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex items-center gap-4 flex-wrap\",\n\t\t\t\t\t\t\t\"[&_a]:text-sm [&_a]:text-muted-foreground [&_a]:hover:text-primary [&_a]:hover:transition-colors\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Link href=\"https://github.com/jsrepojs/jsrepo\" target=\"_blank\">\n\t\t\t\t\t\t\tGitHub\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t<Link href=\"https://jsrepo.com\">jsrepo.com</Link>\n\t\t\t\t\t</div>\n\t\t\t\t</footer>\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\nfunction HeroSection() {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center mt-[15svh]\">\n\t\t\t<div className=\"flex flex-col gap-6 items-center justify-center max-w-3/4\">\n\t\t\t\t<h1 className=\"text-balance text-4xl font-medium sm:text-5xl md:text-6xl\">\n\t\t\t\t\tThe modern registry toolchain\n\t\t\t\t</h1>\n\t\t\t\t<p className=\"mx-auto max-w-3xl text-pretty text-lg text-muted-foreground\">\n\t\t\t\t\tjsrepo handles the hard parts of registries so you can focus on building.\n\t\t\t\t</p>\n\t\t\t\t<div className=\"flex items-center justify-center gap-2\">\n\t\t\t\t\t<Button variant=\"default\" asChild>\n\t\t\t\t\t\t<Link href=\"/docs\">Get Started</Link>\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button variant=\"outline\" asChild>\n\t\t\t\t\t\t<Link href=\"https://jsrepo.com/\" target=\"_blank\">\n\t\t\t\t\t\t\tPublish your Registry\n\t\t\t\t\t\t\t<ExternalLinkIcon/>\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction PluginsSection() {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t<div className=\"max-w-6xl w-full border-x flex flex-col text-start p-6 gap-6\">\n\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-1\">\n\t\t\t\t\t<h2 className=\"text-2xl font-bold text-center\">Customize your experience with plugins</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tWith a js based config the sky is the limit.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<CodeBlock\n\t\t\t\t\tlang=\"ts\"\n\t\t\t\t\tcode={`import { defineConfig } from \"jsrepo\";\nimport javascript from \"@jsrepo/transform-javascript\";\nimport prettier from \"@jsrepo/transform-prettier\";\n\nexport default defineConfig({\n\ttransforms: [javascript(), prettier()], // [!code highlight]\n});`}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction RestEasySection() {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t<div className=\"max-w-6xl w-full border-x flex flex-col text-start p-6 gap-6\">\n\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-1\">\n\t\t\t\t\t<h2 className=\"text-2xl font-bold text-center\">Rest easy</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tIf your registry builds with jsrepo, it will work for your users.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"grid grid-cols-2 gap-6\">\n\t\t\t\t\t<CodeBlock\n\t\t\t\t\t\tclassName=\"h-full\"\n\t\t\t\t\t\tlang=\"ts\"\n\t\t\t\t\t\tcode={`import { createHighlighterCore } from 'shiki/core';\nimport { createJavaScriptRegexEngine } from 'shiki/engine/javascript';\nimport { customLang } from './langs/custom.ts';\n\nexport const highlighter = await createHighlighterCore({\n  themes: [\n    import('@shikijs/themes/nord'),\n    import('@shikijs/themes/dark-plus'),\n  ],\n  langs: [\n    import('@shikijs/langs/typescript'),\n    import('@shikijs/langs/javascript'),\n\tcustomLang(),\n  ],\n  engine: createJavaScriptRegexEngine()\n});`}\n\t\t\t\t\t/>\n\t\t\t\t\t<CodeBlock\n\t\t\t\t\t\tclassName=\"h-full\"\n\t\t\t\t\t\tlang=\"jsonc\"\n\t\t\t\t\t\tcode={`{\n\tname: \"shiki\",\n\tfiles: [\n\t\t{\n\t\t\tpath: \"shiki.ts\",\n\t\t\tcontent: //...,\n\t\t}\n\t],\n\t// auto detect dependencies within your registry\n\tregistryDependencies: ['custom'],\n\t// automatically detect remote dependencies\n\tdependencies: [\n\t\t'shiki', \n\t\t// including dynamic imports\n\t\t'@shikijs/themes', \n\t\t'@shikijs/langs'\n\t]\n}`}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction LLMsSection() {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6 flex flex-col gap-6\">\n\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-1\">\n\t\t\t\t\t<h2 className=\"text-2xl font-bold text-center\">Built for LLMs</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tjsrepo is optimized for LLMs by giving them demos and documentation alongside registry items.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<CodeBlock\n\t\t\t\t\tlang=\"ts\"\n\t\t\t\t\tcode={`import { defineConfig } from \"jsrepo\";\n\nexport default defineConfig({\n\tregistry: {\n\t\tname: '@registry/kit',\n\t\tdescription: 'Components for your registry.',\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: 'button',\n\t\t\t\ttype: 'component',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/components/button.tsx',\n\t\t\t\t\t},\n\t\t\t\t\t{ // [!code highlight]\n\t\t\t\t\t\tpath: 'src/demos/button.tsx', // [!code highlight]\n\t\t\t\t\t\trole: 'example', // [!code highlight]\n\t\t\t\t\t}, // [!code highlight]\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n});`}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction ShadcnCompatibilitySection() {\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t<div className=\"max-w-6xl w-full border-x place-items-center text-start p-6 flex flex-col gap-6\">\n\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-1\">\n\t\t\t\t\t<h2 className=\"text-2xl font-bold text-center\">Shadcn compatible</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tAdd and update items seamlessly from shadcn registries.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<Terminal startOnView={true} className=\"w-full max-w-2xl\">\n\t\t\t\t\t<TypingAnimation>&gt; jsrepo add shadcn:@react-bits/AnimatedContent-TS-TW</TypingAnimation>\n\n\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">\n\t\t\t\t\t\t✔ Fetched manifest from shadcn:@react-bits/AnimatedContent-TS-TW\n\t\t\t\t\t</AnimatedSpan>\n\n\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">✔ Fetched AnimatedContent-TS-TW.</AnimatedSpan>\n\n\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">\n\t\t\t\t\t\t<span>Added AnimatedContent-TS-TW to your project.</span>\n\t\t\t\t\t\t<span className=\"pl-2\">Updated 1 file.</span>\n\t\t\t\t\t</AnimatedSpan>\n\t\t\t\t</Terminal>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction FeatureAccordionSection() {\n\treturn (\n\t\t<div className=\"md:h-[368px]\">\n\t\t\t<FeatureTabs defaultValue=\"config\">\n\t\t\t\t<FeatureTabsList>\n\t\t\t\t\t<FeatureTabsTrigger\n\t\t\t\t\t\tvalue=\"config\"\n\t\t\t\t\t\tduration={2150}\n\t\t\t\t\t\tdescription=\"Configure your registry in seconds.\"\n\t\t\t\t\t>\n\t\t\t\t\t\tConfigure\n\t\t\t\t\t</FeatureTabsTrigger>\n\t\t\t\t\t<FeatureTabsTrigger\n\t\t\t\t\t\tvalue=\"build\"\n\t\t\t\t\t\tduration={2650}\n\t\t\t\t\t\tdescription=\"Build your registry instantly and watch for changes.\"\n\t\t\t\t\t>\n\t\t\t\t\t\tBuild\n\t\t\t\t\t</FeatureTabsTrigger>\n\t\t\t\t\t<FeatureTabsTrigger\n\t\t\t\t\t\tvalue=\"add\"\n\t\t\t\t\t\tduration={2750}\n\t\t\t\t\t\tdescription=\"Add components to your project with a single command.\"\n\t\t\t\t\t>\n\t\t\t\t\t\tAdd\n\t\t\t\t\t</FeatureTabsTrigger>\n\t\t\t\t\t<FeatureTabsTrigger\n\t\t\t\t\t\tvalue=\"update\"\n\t\t\t\t\t\tduration={3150}\n\t\t\t\t\t\tdescription=\"Update components in your project with interactive diffs.\"\n\t\t\t\t\t>\n\t\t\t\t\t\tUpdate\n\t\t\t\t\t</FeatureTabsTrigger>\n\t\t\t\t</FeatureTabsList>\n\t\t\t\t<FeatureTabsContent value=\"config\">\n\t\t\t\t\t<Terminal className=\"border-none rounded-none w-full text-start h-[368px] [&>pre]:w-full *:data-[slot='terminal-header']:hidden\">\n\t\t\t\t\t\t<TypingAnimation>&gt; jsrepo init</TypingAnimation>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">✔ Wrote config to jsrepo.config.ts</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">✔ Installed dependencies.</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">✔ Initialization complete.</AnimatedSpan>\n\t\t\t\t\t</Terminal>\n\t\t\t\t</FeatureTabsContent>\n\t\t\t\t<FeatureTabsContent value=\"build\">\n\t\t\t\t\t<Terminal className=\"border-none rounded-none h-[368px] max-w-none text-start [&>pre]:w-full *:data-[slot='terminal-header']:hidden w-full\">\n\t\t\t\t\t\t<TypingAnimation>&gt; jsrepo build --watch</TypingAnimation>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\" delay={10}>\n\t\t\t\t\t\t\t<span>✔ Finished in 10.49ms</span>\n\t\t\t\t\t\t\t<span className=\"pl-2\">\n\t\t\t\t\t\t\t\t@ieedan/std: Created 1 output in 10.12ms with 3 items and 4 files.\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-muted-foreground\">Watching for changes...</AnimatedSpan>\n\t\t\t\t\t</Terminal>\n\t\t\t\t</FeatureTabsContent>\n\t\t\t\t<FeatureTabsContent value=\"add\">\n\t\t\t\t\t<Terminal className=\"border-none rounded-none text-start h-[368px] [&>pre]:w-full *:data-[slot='terminal-header']:hidden w-full\">\n\t\t\t\t\t\t<TypingAnimation>&gt; jsrepo add button</TypingAnimation>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">\n\t\t\t\t\t\t\t✔ Fetched manifest from @ieedan/shadcn-svelte-extras\n\t\t\t\t\t\t</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">✔ Fetched button.</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">\n\t\t\t\t\t\t\t<span>Added button, utils to your project.</span>\n\t\t\t\t\t\t\t<span className=\"pl-2\">Updated 2 files.</span>\n\t\t\t\t\t\t</AnimatedSpan>\n\t\t\t\t\t</Terminal>\n\t\t\t\t</FeatureTabsContent>\n\t\t\t\t<FeatureTabsContent value=\"update\">\n\t\t\t\t\t<Terminal className=\"border-none rounded-none h-[368px] max-w-none text-start [&>pre]:w-full *:data-[slot='terminal-header']:hidden w-full\">\n\t\t\t\t\t\t<TypingAnimation>&gt; jsrepo update button</TypingAnimation>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">\n\t\t\t\t\t\t\t✔ Fetched manifest from @ieedan/shadcn-svelte-extras\n\t\t\t\t\t\t</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">✔ Fetched button.</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan>\n\t\t\t\t\t\t\t<div className=\"space-y-1 text-sm font-mono\">\n\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t@ieedan/shadcn-svelte-extras/button {\"->\"} src/components/button.svelte\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"space-y-0.5 pl-4\">\n\t\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground\">+ 20 more unchanged (-E to expand)</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">21</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{\"    \"}link: &apos;text-primary underline-offset\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">22</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{\"  \"}\n\t\t\t\t\t\t\t\t\t\t\t{\"},\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">23</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{\"  \"}size: {\"{\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">24</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{\"    default: 'h-10 px-\"}\n\t\t\t\t\t\t\t\t\t\t\t<span className=\" bg-red-500 text-white\">3</span>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"bg-green-500 text-white\">4</span>\n\t\t\t\t\t\t\t\t\t\t\t{\" py-2',\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">25</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{\"    \"}sm: &apos;h-9 rounded-md px-3&apos;,\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">26</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{\"    \"}lg: &apos;h-11 rounded-md px-8&apos;,\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">27</span>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-foreground\">{\"    \"}icon: &apos;h-10 w-10&apos;</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"text-muted-foreground\">+ 60 more unchanged (-E to expand)</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</AnimatedSpan>\n\n\t\t\t\t\t\t<AnimatedSpan className=\"text-green-500\">\n\t\t\t\t\t\t\t<span>Updated button, utils in your project.</span>\n\t\t\t\t\t\t\t<span className=\"pl-2\">Updated 2 files.</span>\n\t\t\t\t\t\t</AnimatedSpan>\n\t\t\t\t\t</Terminal>\n\t\t\t\t</FeatureTabsContent>\n\t\t\t</FeatureTabs>\n\t\t</div>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/app/(home)/providers-section.tsx",
    "content": "\"use client\";\n\nimport { GitHubLogo, JsrepoLogo, RegistryKitLogo } from \"@/components/logos\";\nimport { AnimatedBeam } from \"@/components/ui/animated-beam\";\nimport { cn } from \"@/lib/utils\";\nimport { Folder } from \"lucide-react\";\nimport { useRef } from \"react\";\n\nfunction Circle({ className, ...props }: React.ComponentProps<\"div\">) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"rounded-full relative bg-accent z-10 [&_svg]:size-5 size-12 flex items-center justify-center\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport function ProvidersSection() {\n\tconst containerRef = useRef<HTMLDivElement>(null);\n\n\tconst githubRef = useRef<HTMLDivElement>(null);\n\tconst httpRef = useRef<HTMLDivElement>(null);\n\tconst jsrepoRef = useRef<HTMLDivElement>(null);\n\n\tconst cliRef = useRef<HTMLDivElement>(null);\n\n\tconst projectRef = useRef<HTMLDivElement>(null);\n\n\treturn (\n\t\t<div className=\"flex flex-col items-center justify-center border-b border-border px-6\">\n\t\t\t<div className=\"max-w-6xl w-full border-x text-start p-6 flex-col gap-6 flex\">\n\t\t\t\t<div className=\"flex flex-col gap-1 items-center justify-center\">\n\t\t\t\t\t<h2 className=\"text-2xl font-bold text-center\">Host your registry anywhere</h2>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tHost your registry publicly or privately wherever you want.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div ref={containerRef} className=\"flex items-center justify-between relative overflow-hidden\">\n\t\t\t\t\t<div className=\"flex flex-col items-center justify-center gap-2\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tref={githubRef}\n\t\t\t\t\t\t\tclassName=\"rounded-full z-10 bg-accent px-3 py-2 flex items-center justify-center gap-2\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<GitHubLogo className=\"size-4\" />\n\t\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">github/jsrepojs/registry-kit</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tref={jsrepoRef}\n\t\t\t\t\t\t\tclassName=\"rounded-full z-10 bg-accent px-3 py-2 flex items-center justify-center gap-2\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<JsrepoLogo className=\"size-4\" />\n\t\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">@jsrepo/registry-kit</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tref={httpRef}\n\t\t\t\t\t\t\tclassName=\"rounded-full z-10 bg-accent px-3 py-2 flex items-center justify-center gap-2\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<RegistryKitLogo className=\"size-4\" />\n\t\t\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">https://registry-kit.dev/r</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<Circle ref={cliRef}>\n\t\t\t\t\t\t<JsrepoLogo />\n\t\t\t\t\t</Circle>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<Circle ref={projectRef}>\n\t\t\t\t\t\t\t<Folder className=\"text-muted-foreground\" />\n\t\t\t\t\t\t</Circle>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<AnimatedBeam containerRef={containerRef} fromRef={githubRef} toRef={cliRef} />\n\t\t\t\t\t<AnimatedBeam containerRef={containerRef} fromRef={httpRef} toRef={cliRef} />\n\t\t\t\t\t<AnimatedBeam containerRef={containerRef} fromRef={jsrepoRef} toRef={cliRef} />\n\t\t\t\t\t<AnimatedBeam containerRef={containerRef} fromRef={projectRef} toRef={cliRef} />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/app/api/search/route.ts",
    "content": "import { source } from '@/lib/source';\nimport { createFromSource } from 'fumadocs-core/search/server';\n\nexport const { GET } = createFromSource(source, {\n  // https://docs.orama.com/docs/orama-js/supported-languages\n  language: 'english',\n});\n"
  },
  {
    "path": "apps/docs/src/app/app-client.tsx",
    "content": "\"use client\";\n\nimport { RootProvider } from \"fumadocs-ui/provider/next\";\nimport { QueryClientProvider, QueryClient } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { useScrollToTop } from \"@/hooks/use-scroll-to-top\";\n\nexport function App({ children }: { children: React.ReactNode }) {\n\tuseScrollToTop();\n\tconst [queryClient] = useState(() => new QueryClient());\n\n\treturn (\n\t\t<QueryClientProvider client={queryClient}>\n\t\t\t<RootProvider>{children}</RootProvider>\n\t\t</QueryClientProvider>\n\t);\n}\n\n"
  },
  {
    "path": "apps/docs/src/app/docs/[[...slug]]/page.tsx",
    "content": "import { getPageImage, source } from \"@/lib/source\";\nimport { DocsBody, DocsDescription, DocsPage, DocsTitle } from \"fumadocs-ui/page\";\nimport { notFound } from \"next/navigation\";\nimport { getMDXComponents } from \"@/mdx-components\";\nimport type { Metadata } from \"next\";\nimport { createRelativeLink } from \"fumadocs-ui/mdx\";\nimport { LLMCopyButton, ViewOptions } from \"@/components/page-actions\";\n\nexport default async function Page(props: PageProps<\"/docs/[[...slug]]\">) {\n\tconst params = await props.params;\n\tconst page = source.getPage(params.slug);\n\tif (!page) notFound();\n\n\tconst MDX = page.data.body;\n\n\treturn (\n\t\t<div className=\"contents group/page\">\n\t\t\t<DocsPage toc={page.data.toc} full={page.data.full}>\n\t\t\t\t<div className=\"flex gap-4 lg:flex-row flex-col lg:place-items-center lg:justify-between\">\n\t\t\t\t\t<DocsTitle className=\"lg:order-1 order-2\">{page.data.title}</DocsTitle>\n\t\t\t\t\t<div className=\"flex place-items-center gap-2 lg:order-2 order-1\">\n\t\t\t\t\t\t<LLMCopyButton markdownUrl={`${page.url}.mdx`} />\n\t\t\t\t\t\t<ViewOptions\n\t\t\t\t\t\t\tmarkdownUrl={`${page.url}.mdx`}\n\t\t\t\t\t\t\tgithubUrl={`https://github.com/jsrepojs/jsrepo/blob/main/apps/docs/content/docs/${page.path}`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<DocsDescription className=\"group-has-data-[slot='badge-group']/page:mb-0\">\n\t\t\t\t\t{page.data.description}\n\t\t\t\t</DocsDescription>\n\t\t\t\t<DocsBody>\n\t\t\t\t\t<MDX\n\t\t\t\t\t\tcomponents={getMDXComponents({\n\t\t\t\t\t\t\t// this allows you to link to other pages with relative file paths\n\t\t\t\t\t\t\ta: createRelativeLink(source, page),\n\t\t\t\t\t\t})}\n\t\t\t\t\t/>\n\t\t\t\t</DocsBody>\n\t\t\t</DocsPage>\n\t\t</div>\n\t);\n}\n\nexport async function generateStaticParams() {\n\treturn source.generateParams();\n}\n\nexport async function generateMetadata(props: PageProps<\"/docs/[[...slug]]\">): Promise<Metadata> {\n\tconst params = await props.params;\n\tconst page = source.getPage(params.slug);\n\tif (!page) notFound();\n\n\treturn {\n\t\ttitle: page.data.title,\n\t\tdescription: page.data.description,\n\t\topenGraph: {\n\t\t\timages: getPageImage(page).url,\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "apps/docs/src/app/docs/layout.tsx",
    "content": "import { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { baseOptions } from '@/lib/layout.shared';\nimport { source } from '@/lib/source';\n\nexport default function Layout({ children }: LayoutProps<'/docs'>) {\n  const base = baseOptions();\n  return (\n    <DocsLayout {...base} tree={source.pageTree}>\n      {children}\n    </DocsLayout>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/app/global.css",
    "content": "@import \"tailwindcss\";\n@import \"fumadocs-ui/css/neutral.css\";\n@import \"fumadocs-ui/css/preset.css\";\n@import \"tw-animate-css\";\n\n@custom-variant dark (&:is(.dark *));\n\n@theme inline {\n\t--font-sans: \"Manrope\", \"Manrope Fallback\";\n\t--font-mono: \"IBM Plex Mono\", \"IBM Plex Mono Fallback\";\n\t--radius-sm: calc(var(--radius) - 4px);\n\t--radius-md: calc(var(--radius) - 2px);\n\t--radius-lg: var(--radius);\n\t--radius-xl: calc(var(--radius) + 4px);\n\t--color-background: var(--background);\n\t--color-foreground: var(--foreground);\n\t--color-card: var(--card);\n\t--color-card-foreground: var(--card-foreground);\n\t--color-popover: var(--popover);\n\t--color-popover-foreground: var(--popover-foreground);\n\t--color-primary: var(--primary);\n\t--color-primary-foreground: var(--primary-foreground);\n\t--color-secondary: var(--secondary);\n\t--color-secondary-foreground: var(--secondary-foreground);\n\t--color-muted: var(--muted);\n\t--color-muted-foreground: var(--muted-foreground);\n\t--color-accent: var(--accent);\n\t--color-accent-foreground: var(--accent-foreground);\n\t--color-destructive: var(--destructive);\n\t--color-border: var(--border);\n\t--color-input: var(--input);\n\t--color-ring: var(--ring);\n\t--color-chart-1: var(--chart-1);\n\t--color-chart-2: var(--chart-2);\n\t--color-chart-3: var(--chart-3);\n\t--color-chart-4: var(--chart-4);\n\t--color-chart-5: var(--chart-5);\n\t--color-sidebar: var(--sidebar);\n\t--color-sidebar-foreground: var(--sidebar-foreground);\n\t--color-sidebar-primary: var(--sidebar-primary);\n\t--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n\t--color-sidebar-accent: var(--sidebar-accent);\n\t--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n\t--color-sidebar-border: var(--sidebar-border);\n\t--color-sidebar-ring: var(--sidebar-ring);\n}\n\n:root {\n\t--radius: 0.625rem;\n\t--background: oklch(1 0 0);\n\t--foreground: oklch(0.141 0.005 285.823);\n\t--card: oklch(1 0 0);\n\t--card-foreground: oklch(0.141 0.005 285.823);\n\t--popover: oklch(1 0 0);\n\t--popover-foreground: oklch(0.141 0.005 285.823);\n\t--primary: oklch(0.21 0.006 285.885);\n\t--primary-foreground: oklch(0.985 0 0);\n\t--secondary: oklch(0.967 0.001 286.375);\n\t--secondary-foreground: oklch(0.21 0.006 285.885);\n\t--muted: oklch(0.967 0.001 286.375);\n\t--muted-foreground: oklch(0.552 0.016 285.938);\n\t--accent: oklch(0.967 0.001 286.375);\n\t--accent-foreground: oklch(0.21 0.006 285.885);\n\t--destructive: oklch(0.577 0.245 27.325);\n\t--border: oklch(0.92 0.004 286.32);\n\t--input: oklch(0.92 0.004 286.32);\n\t--ring: oklch(0.705 0.015 286.067);\n\t--chart-1: oklch(0.646 0.222 41.116);\n\t--chart-2: oklch(0.6 0.118 184.704);\n\t--chart-3: oklch(0.398 0.07 227.392);\n\t--chart-4: oklch(0.828 0.189 84.429);\n\t--chart-5: oklch(0.769 0.188 70.08);\n\t--sidebar: oklch(0.985 0 0);\n\t--sidebar-foreground: oklch(0.141 0.005 285.823);\n\t--sidebar-primary: oklch(0.21 0.006 285.885);\n\t--sidebar-primary-foreground: oklch(0.985 0 0);\n\t--sidebar-accent: oklch(0.967 0.001 286.375);\n\t--sidebar-accent-foreground: oklch(0.21 0.006 285.885);\n\t--sidebar-border: oklch(0.92 0.004 286.32);\n\t--sidebar-ring: oklch(0.705 0.015 286.067);\n}\n\n.dark {\n\t--background: oklch(0.141 0.005 285.823);\n\t--foreground: oklch(0.985 0 0);\n\t--card: oklch(0.21 0.006 285.885);\n\t--card-foreground: oklch(0.985 0 0);\n\t--popover: oklch(0.21 0.006 285.885);\n\t--popover-foreground: oklch(0.985 0 0);\n\t--primary: oklch(0.92 0.004 286.32);\n\t--primary-foreground: oklch(0.21 0.006 285.885);\n\t--secondary: oklch(0.274 0.006 286.033);\n\t--secondary-foreground: oklch(0.985 0 0);\n\t--muted: oklch(0.274 0.006 286.033);\n\t--muted-foreground: oklch(0.705 0.015 286.067);\n\t--accent: oklch(0.274 0.006 286.033);\n\t--accent-foreground: oklch(0.985 0 0);\n\t--destructive: oklch(0.704 0.191 22.216);\n\t--border: oklch(1 0 0 / 10%);\n\t--input: oklch(1 0 0 / 15%);\n\t--ring: oklch(0.552 0.016 285.938);\n\t--chart-1: oklch(0.488 0.243 264.376);\n\t--chart-2: oklch(0.696 0.17 162.48);\n\t--chart-3: oklch(0.769 0.188 70.08);\n\t--chart-4: oklch(0.627 0.265 303.9);\n\t--chart-5: oklch(0.645 0.246 16.439);\n\t--sidebar: oklch(0.21 0.006 285.885);\n\t--sidebar-foreground: oklch(0.985 0 0);\n\t--sidebar-primary: oklch(0.488 0.243 264.376);\n\t--sidebar-primary-foreground: oklch(0.985 0 0);\n\t--sidebar-accent: oklch(0.274 0.006 286.033);\n\t--sidebar-accent-foreground: oklch(0.985 0 0);\n\t--sidebar-border: oklch(1 0 0 / 10%);\n\t--sidebar-ring: oklch(0.552 0.016 285.938);\n}\n\n@layer base {\n\t* {\n\t\t@apply border-border outline-ring/50;\n\t}\n\tbody {\n\t\t@apply bg-background text-foreground;\n\t}\n\t/* Apply monospace font to all code blocks */\n\tpre,\n\tcode,\n\t[data-codeblock],\n\t.fd-codeblock pre,\n\t.fd-codeblock code {\n\t\tfont-family: var(--font-mono), ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;\n\t}\n}\n\n/* For components that need horizontal scrolling */\n.scrollbar-hide {\n\t-ms-overflow-style: none; /* Internet Explorer and Edge */\n\tscrollbar-width: none; /* Firefox */\n}\n\n.scrollbar-hide::-webkit-scrollbar {\n\tdisplay: none; /* Chrome, Safari, and Opera */\n}"
  },
  {
    "path": "apps/docs/src/app/layout.tsx",
    "content": "import \"@/app/global.css\";\nimport { Manrope, IBM_Plex_Mono } from \"next/font/google\";\nimport { App } from \"./app-client\";\n\nconst fontSans = Manrope({\n\tsubsets: [\"latin\"],\n\tvariable: \"--font-sans\",\n});\n\nconst fontMono = IBM_Plex_Mono({\n\tsubsets: [\"latin\"],\n\tvariable: \"--font-mono\",\n\tweight: [\"400\", \"500\", \"600\", \"700\"],\n});\n\nexport default function Layout({ children }: LayoutProps<\"/\">) {\n\treturn (\n\t\t<html\n\t\t\tlang=\"en\"\n\t\t\tclassName={`${fontSans.className} ${fontSans.variable} ${fontMono.variable}`}\n\t\t\tsuppressHydrationWarning\n\t\t>\n\t\t\t<body className=\"flex flex-col min-h-screen\">\n\t\t\t\t<App>{children}</App>\n\t\t\t</body>\n\t\t</html>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/app/llms-full.txt/route.ts",
    "content": "import { getLLMText, source } from '@/lib/source';\n\nexport const revalidate = false;\n\nexport async function GET() {\n  const scan = source.getPages().map(getLLMText);\n  const scanned = await Promise.all(scan);\n\n  return new Response(scanned.join('\\n\\n'));\n}\n"
  },
  {
    "path": "apps/docs/src/app/llms.mdx/[[...slug]]/route.ts",
    "content": "import { getLLMText } from \"@/lib/source\";\nimport { source } from \"@/lib/source\";\nimport { notFound } from \"next/navigation\";\n\nexport const revalidate = false;\n\nexport async function GET(_req: Request, { params }: RouteContext<\"/llms.mdx/[[...slug]]\">) {\n\tconst { slug } = await params;\n\tconst page = source.getPage(slug);\n\tif (!page) notFound();\n\n\treturn new Response(await getLLMText(page), {\n\t\theaders: {\n\t\t\t\"Content-Type\": \"text/markdown\",\n\t\t},\n\t});\n}\n\nexport function generateStaticParams() {\n\treturn source.generateParams();\n}\n"
  },
  {
    "path": "apps/docs/src/app/og/docs/[...slug]/route.tsx",
    "content": "import { getPageImage, source } from \"@/lib/source\";\nimport { notFound } from \"next/navigation\";\nimport { ImageResponse } from \"next/og\";\nimport { loadGoogleFont } from \"@/lib/og\";\n\nexport const revalidate = false;\n\nexport async function GET(_req: Request, { params }: RouteContext<\"/og/docs/[...slug]\">) {\n\tconst { slug } = await params;\n\tconst page = source.getPage(slug.slice(0, -1));\n\tif (!page) notFound();\n\n\treturn new ImageResponse(\n    (\n      <div tw=\"flex flex-col gap-2 items-center justify-center w-[1200px] h-[630px] bg-[#1b1c1e]\">\n        <div tw=\"flex rounded-full text-black bg-yellow-500 px-2 py-1\">\n\t\t\t\t\tjsrepo.dev\n\t\t\t\t</div>\n\t\t\t\t<h1 tw=\"text-8xl font-bold text-white\">\n\t\t\t\t\t$ <span tw=\"text-yellow-500 mx-6\">{page.data.title}</span>\n\t\t\t\t\t<div tw=\"h-lh bg-white w-8 ml-1\"></div>\n\t\t\t\t</h1>\n\t\t\t\t<p tw=\"text-3xl font-bold text-white/50\">\n\t\t\t\t\t{page.data.description}\n\t\t\t\t</p>\n\t\t\t</div>\n    ),\n\t\t{\n\t\t\twidth: 1200,\n\t\t\theight: 630,\n\t\t\tfonts: [\n\t\t\t\t{\n\t\t\t\t\tname: \"IBM Plex Mono\",\n\t\t\t\t\tdata: await loadGoogleFont(\"IBM Plex Mono\", \"$ jsrepo.dev\" + page.data.title + page.data.description),\n\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\t);\n}\n\nexport function generateStaticParams() {\n\treturn source.getPages().map((page) => ({\n\t\tlang: page.locale,\n\t\tslug: getPageImage(page).segments,\n\t}));\n}\n"
  },
  {
    "path": "apps/docs/src/app/og/route.tsx",
    "content": "import { loadGoogleFont } from \"@/lib/og\";\nimport { ImageResponse } from \"next/og\";\n\nexport const revalidate = false;\n\nexport async function GET() {\n\tconst title = \"jsrepo\";\n\tconst description = \"The modern registry toolchain.\";\n\n\treturn new ImageResponse(\n\t\t(\n\t\t\t<div tw=\"flex flex-col gap-2 items-center justify-center w-[1200px] h-[630px] bg-[#1b1c1e]\">\n\t\t\t\t<div tw=\"flex rounded-full text-black bg-yellow-500 px-2 py-1\">\n\t\t\t\t\tjsrepo.dev\n\t\t\t\t</div>\n\t\t\t\t<h1 tw=\"text-8xl font-bold text-white\">\n\t\t\t\t\t$ <span tw=\"text-yellow-500 mx-6\">{title}</span>\n\t\t\t\t\t<div tw=\"h-lh bg-white w-8 ml-1\"></div>\n\t\t\t\t</h1>\n\t\t\t\t<p tw=\"text-3xl font-bold text-white/50\">\n\t\t\t\t\t{description}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t),\n\t\t{\n\t\t\twidth: 1200,\n\t\t\theight: 630,\n\t\t\tfonts: [\n\t\t\t\t{\n\t\t\t\t\tname: \"IBM Plex Mono\",\n\t\t\t\t\tdata: await loadGoogleFont(\"IBM Plex Mono\", \"$ jsrepo.dev\" + title + description),\n\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t},\n\t\t\t],\n\t\t}\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/app/robots.txt",
    "content": "# allow crawling everything by default\nUser-agent: *\nDisallow:"
  },
  {
    "path": "apps/docs/src/components/PrismaticBurst.tsx",
    "content": "'use client';\n\nimport React, { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle, Texture } from 'ogl';\n\ntype Offset = { x?: number | string; y?: number | string };\ntype AnimationType = 'rotate' | 'rotate3d' | 'hover';\n\nexport type PrismaticBurstProps = {\n  intensity?: number;\n  speed?: number;\n  animationType?: AnimationType;\n  colors?: string[];\n  distort?: number;\n  paused?: boolean;\n  offset?: Offset;\n  hoverDampness?: number;\n  rayCount?: number;\n  mixBlendMode?: React.CSSProperties['mixBlendMode'] | 'none';\n};\n\nconst vertexShader = `#version 300 es\nin vec2 position;\nin vec2 uv;\nout vec2 vUv;\nvoid main() {\n    vUv = uv;\n    gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragmentShader = `#version 300 es\nprecision highp float;\nprecision highp int;\n\nout vec4 fragColor;\n\nuniform vec2  uResolution;\nuniform float uTime;\n\nuniform float uIntensity;\nuniform float uSpeed;\nuniform int   uAnimType;\nuniform vec2  uMouse;\nuniform int   uColorCount;\nuniform float uDistort;\nuniform vec2  uOffset;\nuniform sampler2D uGradient;\nuniform float uNoiseAmount;\nuniform int   uRayCount;\n\nfloat hash21(vec2 p){\n    p = floor(p);\n    float f = 52.9829189 * fract(dot(p, vec2(0.065, 0.005)));\n    return fract(f);\n}\n\nmat2 rot30(){ return mat2(0.8, -0.5, 0.5, 0.8); }\n\nfloat layeredNoise(vec2 fragPx){\n    vec2 p = mod(fragPx + vec2(uTime * 30.0, -uTime * 21.0), 1024.0);\n    vec2 q = rot30() * p;\n    float n = 0.0;\n    n += 0.40 * hash21(q);\n    n += 0.25 * hash21(q * 2.0 + 17.0);\n    n += 0.20 * hash21(q * 4.0 + 47.0);\n    n += 0.10 * hash21(q * 8.0 + 113.0);\n    n += 0.05 * hash21(q * 16.0 + 191.0);\n    return n;\n}\n\nvec3 rayDir(vec2 frag, vec2 res, vec2 offset, float dist){\n    float focal = res.y * max(dist, 1e-3);\n    return normalize(vec3(2.0 * (frag - offset) - res, focal));\n}\n\nfloat edgeFade(vec2 frag, vec2 res, vec2 offset){\n    vec2 toC = frag - 0.5 * res - offset;\n    float r = length(toC) / (0.5 * min(res.x, res.y));\n    float x = clamp(r, 0.0, 1.0);\n    float q = x * x * x * (x * (x * 6.0 - 15.0) + 10.0);\n    float s = q * 0.5;\n    s = pow(s, 1.5);\n    float tail = 1.0 - pow(1.0 - s, 2.0);\n    s = mix(s, tail, 0.2);\n    float dn = (layeredNoise(frag * 0.15) - 0.5) * 0.0015 * s;\n    return clamp(s + dn, 0.0, 1.0);\n}\n\nmat3 rotX(float a){ float c = cos(a), s = sin(a); return mat3(1.0,0.0,0.0, 0.0,c,-s, 0.0,s,c); }\nmat3 rotY(float a){ float c = cos(a), s = sin(a); return mat3(c,0.0,s, 0.0,1.0,0.0, -s,0.0,c); }\nmat3 rotZ(float a){ float c = cos(a), s = sin(a); return mat3(c,-s,0.0, s,c,0.0, 0.0,0.0,1.0); }\n\nvec3 sampleGradient(float t){\n    t = clamp(t, 0.0, 1.0);\n    return texture(uGradient, vec2(t, 0.5)).rgb;\n}\n\nvec2 rot2(vec2 v, float a){\n    float s = sin(a), c = cos(a);\n    return mat2(c, -s, s, c) * v;\n}\n\nfloat bendAngle(vec3 q, float t){\n    float a = 0.8 * sin(q.x * 0.55 + t * 0.6)\n            + 0.7 * sin(q.y * 0.50 - t * 0.5)\n            + 0.6 * sin(q.z * 0.60 + t * 0.7);\n    return a;\n}\n\nvoid main(){\n    vec2 frag = gl_FragCoord.xy;\n    float t = uTime * uSpeed;\n    float jitterAmp = 0.1 * clamp(uNoiseAmount, 0.0, 1.0);\n    vec3 dir = rayDir(frag, uResolution, uOffset, 1.0);\n    float marchT = 0.0;\n    vec3 col = vec3(0.0);\n    float n = layeredNoise(frag);\n    vec4 c = cos(t * 0.2 + vec4(0.0, 33.0, 11.0, 0.0));\n    mat2 M2 = mat2(c.x, c.y, c.z, c.w);\n    float amp = clamp(uDistort, 0.0, 50.0) * 0.15;\n\n    mat3 rot3dMat = mat3(1.0);\n    if(uAnimType == 1){\n      vec3 ang = vec3(t * 0.31, t * 0.21, t * 0.17);\n      rot3dMat = rotZ(ang.z) * rotY(ang.y) * rotX(ang.x);\n    }\n    mat3 hoverMat = mat3(1.0);\n    if(uAnimType == 2){\n      vec2 m = uMouse * 2.0 - 1.0;\n      vec3 ang = vec3(m.y * 0.6, m.x * 0.6, 0.0);\n      hoverMat = rotY(ang.y) * rotX(ang.x);\n    }\n\n    for (int i = 0; i < 44; ++i) {\n        vec3 P = marchT * dir;\n        P.z -= 2.0;\n        float rad = length(P);\n        vec3 Pl = P * (10.0 / max(rad, 1e-6));\n\n        if(uAnimType == 0){\n            Pl.xz *= M2;\n        } else if(uAnimType == 1){\n      Pl = rot3dMat * Pl;\n        } else {\n      Pl = hoverMat * Pl;\n        }\n\n        float stepLen = min(rad - 0.3, n * jitterAmp) + 0.1;\n\n        float grow = smoothstep(0.35, 3.0, marchT);\n        float a1 = amp * grow * bendAngle(Pl * 0.6, t);\n        float a2 = 0.5 * amp * grow * bendAngle(Pl.zyx * 0.5 + 3.1, t * 0.9);\n        vec3 Pb = Pl;\n        Pb.xz = rot2(Pb.xz, a1);\n        Pb.xy = rot2(Pb.xy, a2);\n\n        float rayPattern = smoothstep(\n            0.5, 0.7,\n            sin(Pb.x + cos(Pb.y) * cos(Pb.z)) *\n            sin(Pb.z + sin(Pb.y) * cos(Pb.x + t))\n        );\n\n        if (uRayCount > 0) {\n            float ang = atan(Pb.y, Pb.x);\n            float comb = 0.5 + 0.5 * cos(float(uRayCount) * ang);\n            comb = pow(comb, 3.0);\n            rayPattern *= smoothstep(0.15, 0.95, comb);\n        }\n\n        vec3 spectralDefault = 1.0 + vec3(\n            cos(marchT * 3.0 + 0.0),\n            cos(marchT * 3.0 + 1.0),\n            cos(marchT * 3.0 + 2.0)\n        );\n\n        float saw = fract(marchT * 0.25);\n        float tRay = saw * saw * (3.0 - 2.0 * saw);\n        vec3 userGradient = 2.0 * sampleGradient(tRay);\n        vec3 spectral = (uColorCount > 0) ? userGradient : spectralDefault;\n        vec3 base = (0.05 / (0.4 + stepLen))\n                  * smoothstep(5.0, 0.0, rad)\n                  * spectral;\n\n        col += base * rayPattern;\n        marchT += stepLen;\n    }\n\n    col *= edgeFade(frag, uResolution, uOffset);\n    col *= uIntensity;\n\n    fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);\n}`;\n\nconst hexToRgb01 = (hex: string): [number, number, number] => {\n  let h = hex.trim();\n  if (h.startsWith('#')) h = h.slice(1);\n  if (h.length === 3) {\n    const r = h[0],\n      g = h[1],\n      b = h[2];\n    h = r + r + g + g + b + b;\n  }\n  const intVal = parseInt(h, 16);\n  if (isNaN(intVal) || (h.length !== 6 && h.length !== 8)) return [1, 1, 1];\n  const r = ((intVal >> 16) & 255) / 255;\n  const g = ((intVal >> 8) & 255) / 255;\n  const b = (intVal & 255) / 255;\n  return [r, g, b];\n};\n\nconst toPx = (v: number | string | undefined): number => {\n  if (v == null) return 0;\n  if (typeof v === 'number') return v;\n  const s = String(v).trim();\n  const num = parseFloat(s.replace('px', ''));\n  return isNaN(num) ? 0 : num;\n};\n\nconst PrismaticBurst = ({\n  intensity = 2,\n  speed = 0.5,\n  animationType = 'rotate3d',\n  colors,\n  distort = 0,\n  paused = false,\n  offset = { x: 0, y: 0 },\n  hoverDampness = 0,\n  rayCount,\n  mixBlendMode = 'lighten'\n}: PrismaticBurstProps) => {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const programRef = useRef<Program | null>(null);\n  const rendererRef = useRef<Renderer | null>(null);\n  const mouseTargetRef = useRef<[number, number]>([0.5, 0.5]);\n  const mouseSmoothRef = useRef<[number, number]>([0.5, 0.5]);\n  const pausedRef = useRef<boolean>(paused);\n  const gradTexRef = useRef<Texture | null>(null);\n  const hoverDampRef = useRef<number>(hoverDampness);\n  const isVisibleRef = useRef<boolean>(true);\n  const meshRef = useRef<Mesh | null>(null);\n  const triRef = useRef<Triangle | null>(null);\n\n  useEffect(() => {\n    pausedRef.current = paused;\n  }, [paused]);\n  useEffect(() => {\n    hoverDampRef.current = hoverDampness;\n  }, [hoverDampness]);\n\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const dpr = Math.min(window.devicePixelRatio || 1, 2);\n    const renderer = new Renderer({ dpr, alpha: false, antialias: false });\n    rendererRef.current = renderer;\n\n    const gl = renderer.gl;\n    gl.canvas.style.position = 'absolute';\n    gl.canvas.style.inset = '0';\n    gl.canvas.style.width = '100%';\n    gl.canvas.style.height = '100%';\n    gl.canvas.style.mixBlendMode = mixBlendMode && mixBlendMode !== 'none' ? mixBlendMode : '';\n    container.appendChild(gl.canvas);\n\n    const white = new Uint8Array([255, 255, 255, 255]);\n    const gradientTex = new Texture(gl, {\n      image: white,\n      width: 1,\n      height: 1,\n      generateMipmaps: false,\n      flipY: false\n    });\n\n    gradientTex.minFilter = gl.LINEAR;\n    gradientTex.magFilter = gl.LINEAR;\n    gradientTex.wrapS = gl.CLAMP_TO_EDGE;\n    gradientTex.wrapT = gl.CLAMP_TO_EDGE;\n    gradTexRef.current = gradientTex;\n\n    const program = new Program(gl, {\n      vertex: vertexShader,\n      fragment: fragmentShader,\n      uniforms: {\n        uResolution: { value: [1, 1] as [number, number] },\n        uTime: { value: 0 },\n\n        uIntensity: { value: 1 },\n        uSpeed: { value: 1 },\n        uAnimType: { value: 0 },\n        uMouse: { value: [0.5, 0.5] as [number, number] },\n        uColorCount: { value: 0 },\n        uDistort: { value: 0 },\n        uOffset: { value: [0, 0] as [number, number] },\n        uGradient: { value: gradientTex },\n        uNoiseAmount: { value: 0.8 },\n        uRayCount: { value: 0 }\n      }\n    });\n\n    programRef.current = program;\n\n    const triangle = new Triangle(gl);\n    const mesh = new Mesh(gl, { geometry: triangle, program });\n    triRef.current = triangle;\n    meshRef.current = mesh;\n\n    const resize = () => {\n      const w = container.clientWidth || 1;\n      const h = container.clientHeight || 1;\n      renderer.setSize(w, h);\n      program.uniforms.uResolution.value = [gl.drawingBufferWidth, gl.drawingBufferHeight];\n    };\n\n    let ro: ResizeObserver | null = null;\n    if ('ResizeObserver' in window) {\n      ro = new ResizeObserver(resize);\n      ro.observe(container);\n    } else {\n      (window as Window).addEventListener('resize', resize);\n    }\n    resize();\n\n    const onPointer = (e: PointerEvent) => {\n      const rect = container.getBoundingClientRect();\n      const x = (e.clientX - rect.left) / Math.max(rect.width, 1);\n      const y = (e.clientY - rect.top) / Math.max(rect.height, 1);\n      mouseTargetRef.current = [Math.min(Math.max(x, 0), 1), Math.min(Math.max(y, 0), 1)];\n    };\n    container.addEventListener('pointermove', onPointer, { passive: true });\n\n    let io: IntersectionObserver | null = null;\n    if ('IntersectionObserver' in window) {\n      io = new IntersectionObserver(\n        entries => {\n          if (entries[0]) isVisibleRef.current = entries[0].isIntersecting;\n        },\n        { root: null, threshold: 0.01 }\n      );\n      io.observe(container);\n    }\n    const onVis = () => {};\n    document.addEventListener('visibilitychange', onVis);\n\n    let raf = 0;\n    let last = performance.now();\n    let accumTime = 0;\n\n    const update = (now: number) => {\n      const dt = Math.max(0, now - last) * 0.001;\n      last = now;\n      const visible = isVisibleRef.current && !document.hidden;\n      if (!pausedRef.current) accumTime += dt;\n      if (!visible) {\n        raf = requestAnimationFrame(update);\n        return;\n      }\n      const tau = 0.02 + Math.max(0, Math.min(1, hoverDampRef.current)) * 0.5;\n      const alpha = 1 - Math.exp(-dt / tau);\n      const tgt = mouseTargetRef.current;\n      const sm = mouseSmoothRef.current;\n      sm[0] += (tgt[0] - sm[0]) * alpha;\n      sm[1] += (tgt[1] - sm[1]) * alpha;\n      program.uniforms.uMouse.value = sm as any;\n      program.uniforms.uTime.value = accumTime;\n      renderer.render({ scene: meshRef.current! });\n      raf = requestAnimationFrame(update);\n    };\n    raf = requestAnimationFrame(update);\n\n    return () => {\n      cancelAnimationFrame(raf);\n      container.removeEventListener('pointermove', onPointer);\n      ro?.disconnect();\n      if (!ro) window.removeEventListener('resize', resize);\n      io?.disconnect();\n      document.removeEventListener('visibilitychange', onVis);\n      try {\n        container.removeChild(gl.canvas);\n      } catch (e) {\n        void e;\n      }\n      meshRef.current = null;\n      triRef.current = null;\n      programRef.current = null;\n      try {\n        const glCtx = rendererRef.current?.gl;\n        if (glCtx && gradTexRef.current?.texture) glCtx.deleteTexture(gradTexRef.current.texture);\n      } catch (e) {\n        void e;\n      }\n      rendererRef.current = null;\n      gradTexRef.current = null;\n    };\n  }, []);\n\n  useEffect(() => {\n    const canvas = rendererRef.current?.gl?.canvas as HTMLCanvasElement | undefined;\n    if (canvas) {\n      canvas.style.mixBlendMode = mixBlendMode && mixBlendMode !== 'none' ? mixBlendMode : '';\n    }\n  }, [mixBlendMode]);\n\n  useEffect(() => {\n    const program = programRef.current;\n    const renderer = rendererRef.current;\n    const gradTex = gradTexRef.current;\n    if (!program || !renderer || !gradTex) return;\n\n    program.uniforms.uIntensity.value = intensity ?? 1;\n    program.uniforms.uSpeed.value = speed ?? 1;\n\n    const animTypeMap: Record<AnimationType, number> = {\n      rotate: 0,\n      rotate3d: 1,\n      hover: 2\n    };\n    program.uniforms.uAnimType.value = animTypeMap[animationType ?? 'rotate'];\n\n    program.uniforms.uDistort.value = typeof distort === 'number' ? distort : 0;\n\n    const ox = toPx(offset?.x);\n    const oy = toPx(offset?.y);\n    program.uniforms.uOffset.value = [ox, oy];\n    program.uniforms.uRayCount.value = Math.max(0, Math.floor(rayCount ?? 0));\n\n    let count = 0;\n    if (Array.isArray(colors) && colors.length > 0) {\n      const gl = renderer.gl;\n      const capped = colors.slice(0, 64);\n      count = capped.length;\n      const data = new Uint8Array(count * 4);\n      for (let i = 0; i < count; i++) {\n        const [r, g, b] = hexToRgb01(capped[i]);\n        data[i * 4 + 0] = Math.round(r * 255);\n        data[i * 4 + 1] = Math.round(g * 255);\n        data[i * 4 + 2] = Math.round(b * 255);\n        data[i * 4 + 3] = 255;\n      }\n      gradTex.image = data;\n      gradTex.width = count;\n      gradTex.height = 1;\n      gradTex.minFilter = gl.LINEAR;\n      gradTex.magFilter = gl.LINEAR;\n      gradTex.wrapS = gl.CLAMP_TO_EDGE;\n      gradTex.wrapT = gl.CLAMP_TO_EDGE;\n      gradTex.flipY = false;\n      gradTex.generateMipmaps = false;\n      gradTex.format = gl.RGBA;\n      gradTex.type = gl.UNSIGNED_BYTE;\n      gradTex.needsUpdate = true;\n    } else {\n      count = 0;\n    }\n    program.uniforms.uColorCount.value = count;\n  }, [intensity, speed, animationType, colors, distort, offset, rayCount]);\n\n  return <div className=\"w-full h-full relative overflow-hidden\" ref={containerRef} />;\n};\n\nexport default PrismaticBurst;\n"
  },
  {
    "path": "apps/docs/src/components/badges-table.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from \"@/components/ui/table\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { CheckIcon, CopyIcon, EllipsisIcon } from \"lucide-react\";\nimport { Button, buttonVariants } from \"./ui/button\";\nimport { Input } from \"./ui/input\";\nimport { useCopyToClipboard } from \"@/hooks/use-copy-to-clipboard\";\nimport { Label } from \"@/components/ui/label\";\n\ntype Props = {\n\tbadges: { alt: string; hrefTemplate: string }[];\n\tdefaultRegistry: string;\n};\n\nfunction buildBadgeUrl(template: string, registry: string) {\n\treturn template.replace(\"{{registry}}\", registry);\n}\n\ntype RegistryContextType = {\n    registry: string;\n    setRegistry: (registry: string) => void;\n}\nexport const RegistryContext = React.createContext<RegistryContextType>({\n    registry: \"\",\n    setRegistry: () => {},\n});\n\nexport function useRegistry() {\n    const ctx = React.useContext(RegistryContext);\n    if (!ctx) throw new Error(\"RegistryContext not found\");\n    return ctx;\n}\n\nexport function BadgesTable({ badges, defaultRegistry }: Props) {\n    const [registry, setRegistry] = React.useState<string>(defaultRegistry);\n    const ctx = React.useMemo(() => ({ registry, setRegistry }), [registry, setRegistry]);\n\treturn (\n\t\t<RegistryContext value={ctx}>\n            <Table>\n\t\t\t<TableHeader>\n\t\t\t\t<TableRow>\n\t\t\t\t\t<TableHead>Badge</TableHead>\n\t\t\t\t\t<TableHead></TableHead>\n\t\t\t\t</TableRow>\n\t\t\t</TableHeader>\n\t\t\t<TableBody>\n\t\t\t\t{badges.map((badge) => (\n\t\t\t\t\t<TableRow key={badge.alt}>\n\t\t\t\t\t\t<TableCell>\n\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\tsrc={buildBadgeUrl(badge.hrefTemplate, defaultRegistry)}\n\t\t\t\t\t\t\t\talt={badge.alt}\n\t\t\t\t\t\t\t\theight={20}\n\t\t\t\t\t\t\t\tclassName=\"not-prose\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t\t<TableCell>\n\t\t\t\t\t\t\t<BadgeActions badge={badge} />\n\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t</TableRow>\n\t\t\t\t))}\n\t\t\t</TableBody>\n\t\t</Table>\n        </RegistryContext>\n\t);\n}\n\nexport function BadgeActions({ badge }: { badge: { alt: string; hrefTemplate: string } }) {\n\tconst [copy, isCopied] = useCopyToClipboard();\n\tconst [copyUrl, isUrlCopied] = useCopyToClipboard();\n    const { registry, setRegistry } = useRegistry();\n\n\treturn (\n\t\t<Popover>\n\t\t\t<PopoverTrigger className={buttonVariants({ variant: \"ghost\" })}>\n\t\t\t\t<EllipsisIcon />\n\t\t\t</PopoverTrigger>\n\t\t\t<PopoverContent align=\"end\">\n\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t<Label>Registry</Label>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tplaceholder=\"@<scope>/<registry>\"\n\t\t\t\t\t\t\tvalue={registry}\n\t\t\t\t\t\t\tonChange={(e) => setRegistry(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t<Label>Preview</Label>\n\t\t\t\t\t\t<div className=\"p-2 border rounded-md h-9\">\n\t\t\t\t\t\t\t{registry === \"\" ? (\n\t\t\t\t\t\t\t\t<></>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\tsrc={buildBadgeUrl(badge.hrefTemplate, registry)}\n\t\t\t\t\t\t\t\t\talt={badge.alt}\n\t\t\t\t\t\t\t\t\theight={20}\n\t\t\t\t\t\t\t\t\tclassName=\"not-prose\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tclassName=\"w-full\"\n                        disabled={registry === \"\"}\n\t\t\t\t\t\tonClick={() => copyUrl(buildBadgeUrl(badge.hrefTemplate, registry))}\n\t\t\t\t\t>\n\t\t\t\t\t\t{isUrlCopied ? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<CheckIcon /> <span>Copied</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<CopyIcon /> <span>Copy URL</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\tdisabled={registry === \"\"}\n\t\t\t\t\t\tonClick={() => copy(buildBadgeUrl(badge.hrefTemplate, registry))}\n\t\t\t\t\t>\n\t\t\t\t\t\t{isCopied ? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<CheckIcon /> <span>Copied</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<CopyIcon /> <span>Copy Markdown</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</PopoverContent>\n\t\t</Popover>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/components/feature-tabs.tsx",
    "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport React, { createContext, useContext, useEffect, useMemo, useState } from \"react\";\n\ntype FeatureTabsContext = {\n\tactiveTab: string;\n\tsetActiveTab: (tab: string, fromUser?: boolean) => void;\n\ttabs: { value: string; duration: number }[];\n\tregisterTab: (tab: { value: string; duration: number }) => void;\n\tmode: \"auto\" | \"manual\";\n};\n\nconst FeatureTabsContext = createContext<FeatureTabsContext>({\n\tactiveTab: \"\",\n\tsetActiveTab: () => {},\n\ttabs: [],\n\tregisterTab: () => {},\n\tmode: \"auto\",\n});\n\nfunction useFeatureTabs() {\n\tconst ctx = useContext(FeatureTabsContext);\n\tif (!ctx) throw new Error(\"FeatureTabsContext not found\");\n\treturn ctx;\n}\n\nfunction FeatureTabs({\n\tdefaultValue,\n\tclassName,\n\tchildren,\n\tsubClassName,\n\t...props\n}: React.HTMLAttributes<HTMLDivElement> & { defaultValue: string; subClassName?: string }) {\n\tconst [activeTab, setActiveTab] = useState(defaultValue);\n\tconst [mode, setMode] = useState<\"auto\" | \"manual\">(\"auto\");\n\tconst [tabs, setTabs] = useState<{ value: string; duration: number }[]>([]);\n\tfunction registerTab(tab: { value: string; duration: number }) {\n\t\tsetTabs((prev) => [...prev, tab]);\n\t}\n\tfunction _setActiveTab(tab: string, fromUser: boolean = false) {\n\t\tif (fromUser) {\n\t\t\tsetMode(\"manual\");\n\t\t}\n\t\tsetActiveTab(tab);\n\t}\n\tconst ctx = useMemo(\n\t\t() => ({ activeTab, setActiveTab: _setActiveTab, tabs, registerTab, mode }),\n\t\t[activeTab, tabs, mode]\n\t);\n\tuseEffect(() => {\n\t\tif (mode === \"manual\" || tabs.length === 0) return;\n\n\t\tconst t = tabs.map((tab, i) => ({ tab, index: i })).find(({ tab }) => tab.value === activeTab);\n\t\tif (!t) return;\n\n\t\tconst { index, tab } = t;\n\t\tconst timeoutId = setTimeout(() => {\n\t\t\tif (index + 1 === tabs.length) {\n\t\t\t\t_setActiveTab(tabs[0].value, false);\n\t\t\t} else {\n\t\t\t\t_setActiveTab(tabs[index + 1].value, false);\n\t\t\t}\n\t\t}, tab.duration);\n\n\t\treturn () => clearTimeout(timeoutId);\n\t}, [activeTab, mode, tabs]);\n\treturn (\n\t\t<FeatureTabsContext value={ctx}>\n\t\t\t<div\n\t\t\t\tclassName={cn(\"flex items-center justify-center w-full border-y border-border px-6\", className)}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"max-w-6xl flex flex-col border-x border-border md:grid md:grid-cols-2 w-full\",\n\t\t\t\t\t\tsubClassName\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</FeatureTabsContext>\n\t);\n}\n\nfunction FeatureTabsList({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex flex-col md:h-[368px] border-border border-b md:border-b-0 md:border-r w-full\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</div>\n\t);\n}\n\nfunction FeatureTabsTrigger({\n\tvalue,\n\tduration,\n\tdescription,\n\tclassName,\n\tchildren,\n\t...props\n}: React.HTMLAttributes<HTMLButtonElement> & { value: string; duration: number; description: string }) {\n\tconst { registerTab, setActiveTab, activeTab } = useFeatureTabs();\n\n\t// we don't want to re-register the tab if the value changes\n\tuseEffect(() => {\n\t\tregisterTab({ value, duration });\n\t}, []);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"group border-border border-b last:border-b-0\"\n\t\t\tdata-value={value}\n\t\t\tdata-state={activeTab === value ? \"active\" : \"inactive\"}\n\t\t>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"border-border py-4 px-6 transition-all duration-700 group w-full flex items-center gap-4\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tonClick={() => setActiveTab(value, true)}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<div className=\"size-6 border p-1\">\n\t\t\t\t\t<div className=\"size-full group-hover:bg-primary/50 group-data-[state=active]:bg-primary transition-colors duration-300\"></div>\n\t\t\t\t</div>\n\t\t\t\t{children}\n\t\t\t</button>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"hidden md:block px-6 [--radix-accordion-content-height:144px] duration-300 h-0\",\n\t\t\t\t\t\"group-data-[state=inactive]:py-0 group-data-[state=active]:py-4 group-data-[state=inactive]:h-0\",\n\t\t\t\t\t\"group-data-[state=active]:h-[144px] group-data-[state=inactive]:animate-accordion-up group-data-[state=active]:animate-accordion-down\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<p className=\"text-start text-muted-foreground group-data-[state=inactive]:hidden\">{description}</p>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction FeatureTabsContent({\n\tvalue,\n\tclassName,\n\tchildren,\n\t...props\n}: React.HTMLAttributes<HTMLDivElement> & { value: string }) {\n\tconst { activeTab } = useFeatureTabs();\n\treturn activeTab !== value ? (\n\t\t<></>\n\t) : (\n\t\t<div className={cn(\"w-full bg-card\", className)} {...props}>\n\t\t\t{children}\n\t\t</div>\n\t);\n}\n\nexport { FeatureTabs, FeatureTabsList, FeatureTabsTrigger, FeatureTabsContent };\n"
  },
  {
    "path": "apps/docs/src/components/files.tsx",
    "content": "'use client';\n\nimport { cva } from 'class-variance-authority';\nimport {\n  FileIcon as LucideFileIcon,\n  FolderIcon,\n  FolderOpenIcon,\n} from 'lucide-react';\nimport { type HTMLAttributes, type ReactNode, useState } from 'react';\nimport { cn } from '@/lib/utils';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from './ui/collapsible';\nimport { JavaScriptLogo, SvelteLogo, TypeScriptLogo, VueLogo } from './logos';\n\nconst itemVariants = cva(\n  'flex flex-row items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground [&_svg]:size-4',\n);\n\nexport function Files({\n  className,\n  ...props\n}: HTMLAttributes<HTMLDivElement>): React.ReactElement {\n  return (\n    <div\n      className={cn('not-prose rounded-md border bg-fd-card p-2', className)}\n      {...props}\n    >\n      {props.children}\n    </div>\n  );\n}\n\nexport interface FileProps extends HTMLAttributes<HTMLDivElement> {\n  name: string;\n  icon?: ReactNode;\n}\n\nexport interface FolderProps extends HTMLAttributes<HTMLDivElement> {\n  name: string;\n\n  disabled?: boolean;\n\n  /**\n   * Open folder by default\n   *\n   * @defaultValue false\n   */\n  defaultOpen?: boolean;\n}\n\nexport function File({\n  name,\n  className,\n  ...rest\n}: FileProps): React.ReactElement {\n  return (\n    <div className={cn(itemVariants({ className }))} {...rest}>\n      <FileIcon name={name} />\n      {name}\n    </div>\n  );\n}\n\nexport function FileIcon({ name }: { name: string }) {\n  const extension = name.includes('.') ? name.split('.').pop() : null;\n  switch (extension) {\n    case 'svelte':\n      return <SvelteLogo />;\n    case 'js':\n      return <JavaScriptLogo />;\n    case 'ts':\n      return <TypeScriptLogo />;\n    case 'vue':\n      return <VueLogo />;\n    default:\n      return <LucideFileIcon />;\n  }\n}\n\nexport function Folder({\n  name,\n  defaultOpen = false,\n  ...props\n}: FolderProps): React.ReactElement {\n  const [open, setOpen] = useState(defaultOpen);\n\n  return (\n    <Collapsible open={open} onOpenChange={setOpen} {...props}>\n      <CollapsibleTrigger className={cn(itemVariants({ className: 'w-full' }))}>\n        {open ? <FolderOpenIcon /> : <FolderIcon />}\n        {name}\n      </CollapsibleTrigger>\n      <CollapsibleContent>\n        <div className=\"ms-2 flex flex-col border-l ps-2\">{props.children}</div>\n      </CollapsibleContent>\n    </Collapsible>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/language-toggle.tsx",
    "content": "'use client';\nimport { type ButtonHTMLAttributes, type HTMLAttributes } from 'react';\nimport { useI18n } from 'fumadocs-ui/contexts/i18n';\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from './ui/popover';\nimport { cn } from '../lib/cn';\nimport { buttonVariants } from './ui/button';\n\nexport type LanguageSelectProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function LanguageToggle(props: LanguageSelectProps): React.ReactElement {\n  const context = useI18n();\n  if (!context.locales) throw new Error('Missing `<I18nProvider />`');\n\n  return (\n    <Popover>\n      <PopoverTrigger\n        aria-label={context.text.chooseLanguage}\n        {...props}\n        className={cn(\n          buttonVariants({\n            variant: 'ghost',\n            className: 'gap-1.5 p-1.5',\n          }),\n          props.className,\n        )}\n      >\n        {props.children}\n      </PopoverTrigger>\n      <PopoverContent className=\"flex flex-col overflow-x-hidden p-0\">\n        <p className=\"mb-1 p-2 text-xs font-medium text-fd-muted-foreground\">\n          {context.text.chooseLanguage}\n        </p>\n        {context.locales.map((item) => (\n          <button\n            key={item.locale}\n            type=\"button\"\n            className={cn(\n              'p-2 text-start text-sm',\n              item.locale === context.locale\n                ? 'bg-fd-primary/10 font-medium text-fd-primary'\n                : 'hover:bg-fd-accent hover:text-fd-accent-foreground',\n            )}\n            onClick={() => {\n              context.onChange?.(item.locale);\n            }}\n          >\n            {item.name}\n          </button>\n        ))}\n      </PopoverContent>\n    </Popover>\n  );\n}\n\nexport function LanguageToggleText(\n  props: HTMLAttributes<HTMLSpanElement>,\n): React.ReactElement {\n  const context = useI18n();\n  const text = context.locales?.find(\n    (item) => item.locale === context.locale,\n  )?.name;\n\n  return <span {...props}>{text}</span>;\n}\n"
  },
  {
    "path": "apps/docs/src/components/layout/home/client.tsx",
    "content": "'use client';\nimport { type ComponentProps, Fragment, useState } from 'react';\nimport { cva } from 'class-variance-authority';\nimport Link from 'fumadocs-core/link';\nimport { cn } from '../../../lib/cn';\nimport { BaseLinkItem, type LinkItemType } from '../shared/index';\nimport {\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  NavigationMenuViewport,\n} from '../../navigation-menu';\nimport { useNav } from 'fumadocs-ui/contexts/layout';\nimport { buttonVariants } from '../../ui/button';\n\nexport const navItemVariants = cva('[&_svg]:size-4', {\n  variants: {\n    variant: {\n      main: 'inline-flex items-center gap-1 p-2 text-fd-muted-foreground transition-colors hover:text-fd-accent-foreground data-[active=true]:text-fd-primary',\n      button: buttonVariants({\n        variant: 'secondary',\n        className: 'gap-1.5',\n      }),\n      icon: buttonVariants({\n        variant: 'ghost',\n        size: 'icon',\n      }),\n    },\n  },\n  defaultVariants: {\n    variant: 'main',\n  },\n});\n\nexport function Navbar(props: ComponentProps<'div'>) {\n  const [value, setValue] = useState('');\n  const { isTransparent } = useNav();\n\n  return (\n    <NavigationMenu value={value} onValueChange={setValue} asChild>\n      <header\n        id=\"nd-nav\"\n        {...props}\n        className={cn(\n          'fixed top-(--fd-banner-height) z-40 left-0 right-(--removed-body-scroll-bar-size,0) backdrop-blur-lg border-b transition-colors *:mx-auto *:max-w-fd-container',\n          value.length > 0 && 'max-lg:shadow-lg max-lg:rounded-b-2xl',\n          (!isTransparent || value.length > 0) && 'bg-fd-background/80',\n          props.className,\n        )}\n      >\n        <NavigationMenuList\n          className=\"flex h-14 w-full items-center px-4\"\n          asChild\n        >\n          <nav>{props.children}</nav>\n        </NavigationMenuList>\n\n        <NavigationMenuViewport />\n      </header>\n    </NavigationMenu>\n  );\n}\n\nexport { NavigationMenuItem };\n\nexport function NavigationMenuLinkItem({\n  item,\n  ...props\n}: {\n  item: LinkItemType;\n  className?: string;\n}) {\n  if (item.type === 'custom') return <div {...props}>{item.children}</div>;\n\n  if (item.type === 'menu') {\n    const children = item.items.map((child, j) => {\n      if (child.type === 'custom') {\n        return <Fragment key={j}>{child.children}</Fragment>;\n      }\n\n      const {\n        banner = child.icon ? (\n          <div className=\"w-fit rounded-md border bg-fd-muted p-1 [&_svg]:size-4\">\n            {child.icon}\n          </div>\n        ) : null,\n        ...rest\n      } = child.menu ?? {};\n\n      return (\n        <NavigationMenuLink key={`${j}-${child.url}`} asChild>\n          <Link\n            href={child.url}\n            external={child.external}\n            {...rest}\n            className={cn(\n              'flex flex-col gap-2 rounded-lg border bg-fd-card p-3 transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',\n              rest.className,\n            )}\n          >\n            {rest.children ?? (\n              <>\n                {banner}\n                <p className=\"text-base font-medium\">{child.text}</p>\n                <p className=\"text-sm text-fd-muted-foreground empty:hidden\">\n                  {child.description}\n                </p>\n              </>\n            )}\n          </Link>\n        </NavigationMenuLink>\n      );\n    });\n\n    return (\n      <NavigationMenuItem {...props}>\n        <NavigationMenuTrigger className={cn(navItemVariants(), 'rounded-md')}>\n          {item.url ? (\n            <Link href={item.url} external={item.external}>\n              {item.text}\n            </Link>\n          ) : (\n            item.text\n          )}\n        </NavigationMenuTrigger>\n        <NavigationMenuContent className=\"grid grid-cols-1 gap-2 p-4 md:grid-cols-2 lg:grid-cols-3\">\n          {children}\n        </NavigationMenuContent>\n      </NavigationMenuItem>\n    );\n  }\n\n  return (\n    <NavigationMenuItem {...props}>\n      <NavigationMenuLink asChild>\n        <BaseLinkItem\n          item={item}\n          aria-label={item.type === 'icon' ? item.label : undefined}\n          className={cn(navItemVariants({ variant: item.type }))}\n        >\n          {item.type === 'icon' ? item.icon : item.text}\n        </BaseLinkItem>\n      </NavigationMenuLink>\n    </NavigationMenuItem>\n  );\n}\n\nexport function MobileNavigationMenuLinkItem({\n  item,\n  ...props\n}: {\n  item: LinkItemType;\n  className?: string;\n}) {\n  if (item.type === 'custom')\n    return <div className={cn('grid', props.className)}>{item.children}</div>;\n\n  if (item.type === 'menu') {\n    const header = (\n      <>\n        {item.icon}\n        {item.text}\n      </>\n    );\n\n    return (\n      <div className={cn('mb-4 flex flex-col', props.className)}>\n        <p className=\"mb-1 text-sm text-fd-muted-foreground\">\n          {item.url ? (\n            <NavigationMenuLink asChild>\n              <Link href={item.url} external={item.external}>\n                {header}\n              </Link>\n            </NavigationMenuLink>\n          ) : (\n            header\n          )}\n        </p>\n        {item.items.map((child, i) => (\n          <MobileNavigationMenuLinkItem key={i} item={child} />\n        ))}\n      </div>\n    );\n  }\n\n  return (\n    <NavigationMenuLink asChild>\n      <BaseLinkItem\n        item={item}\n        className={cn(\n          {\n            main: 'inline-flex items-center gap-2 py-1.5 transition-colors hover:text-fd-popover-foreground/50 data-[active=true]:font-medium data-[active=true]:text-fd-primary [&_svg]:size-4',\n            icon: buttonVariants({\n              size: 'icon',\n              variant: 'ghost',\n            }),\n            button: buttonVariants({\n              variant: 'secondary',\n              className: 'gap-1.5 [&_svg]:size-4',\n            }),\n          }[item.type ?? 'main'],\n          props.className,\n        )}\n        aria-label={item.type === 'icon' ? item.label : undefined}\n      >\n        {item.icon}\n        {item.type === 'icon' ? undefined : item.text}\n      </BaseLinkItem>\n    </NavigationMenuLink>\n  );\n}\n\nexport function MobileNavigationMenuTrigger({\n  enableHover = false,\n  ...props\n}: ComponentProps<typeof NavigationMenuTrigger> & {\n  /**\n   * Enable hover to trigger\n   */\n  enableHover?: boolean;\n}) {\n  return (\n    <NavigationMenuTrigger\n      {...props}\n      onPointerMove={enableHover ? undefined : (e) => e.preventDefault()}\n    >\n      {props.children}\n    </NavigationMenuTrigger>\n  );\n}\n\nexport function MobileNavigationMenuContent(\n  props: ComponentProps<typeof NavigationMenuContent>,\n) {\n  return (\n    <NavigationMenuContent\n      {...props}\n      className={cn('flex flex-col p-4', props.className)}\n    >\n      {props.children}\n    </NavigationMenuContent>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/layout/home/index.tsx",
    "content": "import { type HTMLAttributes, useMemo } from 'react';\nimport { cn } from '../../../lib/cn';\nimport {\n  type BaseLayoutProps,\n  getLinks,\n  type LinkItemType,\n  type NavOptions,\n} from '../shared/index';\nimport { NavProvider } from 'fumadocs-ui/contexts/layout';\nimport {\n  LargeSearchToggle,\n  SearchToggle,\n} from '../../search-toggle';\nimport { ThemeToggle } from '../../theme-toggle';\nimport {\n  LanguageToggle,\n  LanguageToggleText,\n} from '../../language-toggle';\nimport { ChevronDown, Languages } from 'lucide-react';\nimport Link from 'fumadocs-core/link';\nimport {\n  Navbar,\n  NavigationMenuLinkItem,\n  MobileNavigationMenuContent,\n  MobileNavigationMenuLinkItem,\n  MobileNavigationMenuTrigger,\n  NavigationMenuItem,\n} from './client';\nimport { buttonVariants } from '../../ui/button';\nimport { GitHubButton } from '@/components/ui/github-button';\n\nexport interface HomeLayoutProps extends BaseLayoutProps {\n  nav?: Partial<\n    NavOptions & {\n      /**\n       * Open mobile menu when hovering the trigger\n       */\n      enableHoverToOpen?: boolean;\n    }\n  >;\n}\n\nexport function HomeLayout(\n  props: HomeLayoutProps & HTMLAttributes<HTMLElement>,\n) {\n  const {\n    nav = {},\n    links,\n    githubUrl,\n    i18n,\n    themeSwitch = {},\n    searchToggle,\n    ...rest\n  } = props;\n\n  return (\n    <NavProvider transparentMode={nav?.transparentMode}>\n      <main\n        id=\"nd-home-layout\"\n        {...rest}\n        className={cn('flex flex-1 flex-col pt-14', rest.className)}\n      >\n        {nav.enabled !== false &&\n          (nav.component ?? (\n            <Header\n              links={links}\n              nav={nav}\n              themeSwitch={themeSwitch}\n              searchToggle={searchToggle}\n              i18n={i18n}\n              githubUrl={githubUrl}\n            />\n          ))}\n        {props.children}\n      </main>\n    </NavProvider>\n  );\n}\n\nexport function Header({\n  nav = {},\n  i18n = false,\n  links,\n  githubUrl,\n  themeSwitch = {},\n  searchToggle = {},\n}: HomeLayoutProps) {\n  const finalLinks = useMemo(\n    () => getLinks(links),\n    [links],\n  );\n\n  const navItems = finalLinks.filter((item) =>\n    ['nav', 'all'].includes(item.on ?? 'all'),\n  );\n  const menuItems = finalLinks.filter((item) =>\n    ['menu', 'all'].includes(item.on ?? 'all'),\n  );\n\n  return (\n    <Navbar>\n      <Link\n        href={nav.url ?? '/'}\n        className=\"inline-flex items-center gap-2.5 font-semibold\"\n      >\n        {nav.title}\n      </Link>\n      {nav.children}\n      <ul className=\"flex flex-row items-center gap-2 px-6 max-sm:hidden\">\n        {navItems\n          .filter((item) => !isSecondary(item))\n          .map((item, i) => (\n            <NavigationMenuLinkItem key={i} item={item} className=\"text-sm\" />\n          ))}\n      </ul>\n      <div className=\"flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden\">\n        {searchToggle.enabled !== false &&\n          (searchToggle.components?.lg ?? (\n            <LargeSearchToggle\n              className=\"w-full rounded-full ps-2.5 max-w-[240px]\"\n              hideIfDisabled\n            />\n          ))}\n        {themeSwitch.enabled !== false &&\n          (themeSwitch.component ?? <ThemeToggle mode={themeSwitch?.mode} />)}\n        {i18n && (\n          <LanguageToggle>\n            <Languages className=\"size-5\" />\n          </LanguageToggle>\n        )}\n        <ul className=\"flex flex-row gap-2 items-center empty:hidden\">\n          {navItems.filter(isSecondary).map((item, i) => (\n            <NavigationMenuLinkItem\n              key={i}\n              className={cn(\n                item.type === 'icon' && '-mx-1 first:ms-0 last:me-0',\n              )}\n              item={item}\n            />\n          ))}\n          <GitHubButton repo={{ owner: 'jsrepojs', name: 'jsrepo' }} fallback={593} />\n        </ul>\n      </div>\n      <ul className=\"flex flex-row items-center ms-auto -me-1.5 lg:hidden\">\n        {searchToggle.enabled !== false &&\n          (searchToggle.components?.sm ?? (\n            <SearchToggle className=\"p-2\" hideIfDisabled />\n          ))}\n        <NavigationMenuItem>\n          <MobileNavigationMenuTrigger\n            aria-label=\"Toggle Menu\"\n            className={cn(\n              buttonVariants({\n                size: 'icon',\n                variant: 'ghost',\n                className: 'group [&_svg]:size-5.5',\n              }),\n            )}\n            enableHover={nav.enableHoverToOpen}\n          >\n            <ChevronDown className=\"transition-transform duration-300 group-data-[state=open]:rotate-180\" />\n          </MobileNavigationMenuTrigger>\n          <MobileNavigationMenuContent className=\"sm:flex-row sm:items-center sm:justify-end\">\n            {menuItems\n              .filter((item) => !isSecondary(item))\n              .map((item, i) => (\n                <MobileNavigationMenuLinkItem\n                  key={i}\n                  item={item}\n                  className=\"sm:hidden\"\n                />\n              ))}\n            <div className=\"-ms-1.5 flex flex-row items-center gap-2 max-sm:mt-2\">\n              {menuItems.filter(isSecondary).map((item, i) => (\n                <MobileNavigationMenuLinkItem\n                  key={i}\n                  item={item}\n                  className={cn(item.type === 'icon' && '-mx-1 first:ms-0')}\n                />\n              ))}\n              <GitHubButton repo={{ owner: 'jsrepojs', name: 'jsrepo' }} fallback={593} />\n              <div role=\"separator\" className=\"flex-1\" />\n              {i18n && (\n                <LanguageToggle>\n                  <Languages className=\"size-5\" />\n                  <LanguageToggleText />\n                  <ChevronDown className=\"size-3 text-fd-muted-foreground\" />\n                </LanguageToggle>\n              )}\n              {themeSwitch.enabled !== false &&\n                (themeSwitch.component ?? (\n                  <ThemeToggle mode={themeSwitch?.mode} />\n                ))}\n            </div>\n          </MobileNavigationMenuContent>\n        </NavigationMenuItem>\n      </ul>\n    </Navbar>\n  );\n}\n\nfunction isSecondary(item: LinkItemType): boolean {\n  if ('secondary' in item && item.secondary != null) return item.secondary;\n\n  return item.type === 'icon';\n}\n"
  },
  {
    "path": "apps/docs/src/components/layout/shared/client.tsx",
    "content": "'use client';\nimport type { ComponentProps } from 'react';\nimport { usePathname } from 'fumadocs-core/framework';\nimport { isActive } from '../../../lib/is-active';\nimport Link from 'fumadocs-core/link';\nimport type { BaseLinkType } from './index';\n\nexport function BaseLinkItem({\n  ref,\n  item,\n  ...props\n}: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {\n  const pathname = usePathname();\n  const activeType = item.active ?? 'url';\n  const active =\n    activeType !== 'none' &&\n    isActive(item.url, pathname, activeType === 'nested-url');\n\n  return (\n    <Link\n      ref={ref}\n      href={item.url}\n      external={item.external}\n      {...props}\n      data-active={active}\n    >\n      {props.children}\n    </Link>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/layout/shared/index.tsx",
    "content": "import type { HTMLAttributes, ReactNode } from 'react';\nimport type { NavProviderProps } from 'fumadocs-ui/contexts/layout';\nimport type { I18nConfig } from 'fumadocs-core/i18n';\n\nexport interface NavOptions extends NavProviderProps {\n  enabled: boolean;\n  component: ReactNode;\n\n  title?: ReactNode;\n\n  /**\n   * Redirect url of title\n   * @defaultValue '/'\n   */\n  url?: string;\n\n  children?: ReactNode;\n}\n\nexport interface BaseLayoutProps {\n  themeSwitch?: {\n    enabled?: boolean;\n    component?: ReactNode;\n    mode?: 'light-dark' | 'light-dark-system';\n  };\n\n  searchToggle?: Partial<{\n    enabled: boolean;\n    components: Partial<{\n      sm: ReactNode;\n      lg: ReactNode;\n    }>;\n  }>;\n\n  /**\n   * I18n options\n   *\n   * @defaultValue false\n   */\n  i18n?: boolean | I18nConfig;\n\n  /**\n   * GitHub url\n   */\n  githubUrl?: string;\n\n  links?: LinkItemType[];\n  /**\n   * Replace or disable navbar\n   */\n  nav?: Partial<NavOptions>;\n\n  children?: ReactNode;\n}\n\ninterface BaseItem {\n  /**\n   * Restrict where the item is displayed\n   *\n   * @defaultValue 'all'\n   */\n  on?: 'menu' | 'nav' | 'all';\n}\n\nexport interface BaseLinkType extends BaseItem {\n  url: string;\n  /**\n   * When the item is marked as active\n   *\n   * @defaultValue 'url'\n   */\n  active?: 'url' | 'nested-url' | 'none';\n  external?: boolean;\n}\n\nexport interface MainItemType extends BaseLinkType {\n  type?: 'main';\n  icon?: ReactNode;\n  text: ReactNode;\n  description?: ReactNode;\n}\n\nexport interface IconItemType extends BaseLinkType {\n  type: 'icon';\n  /**\n   * `aria-label` of icon button\n   */\n  label?: string;\n  icon: ReactNode;\n  text: ReactNode;\n  /**\n   * @defaultValue true\n   */\n  secondary?: boolean;\n}\n\nexport interface ButtonItemType extends BaseLinkType {\n  type: 'button';\n  icon?: ReactNode;\n  text: ReactNode;\n  /**\n   * @defaultValue false\n   */\n  secondary?: boolean;\n}\n\nexport interface MenuItemType extends Partial<BaseLinkType> {\n  type: 'menu';\n  icon?: ReactNode;\n  text: ReactNode;\n\n  items: (\n    | (MainItemType & {\n        /**\n         * Options when displayed on navigation menu\n         */\n        menu?: HTMLAttributes<HTMLElement> & {\n          banner?: ReactNode;\n        };\n      })\n    | CustomItemType\n  )[];\n\n  /**\n   * @defaultValue false\n   */\n  secondary?: boolean;\n}\n\nexport interface CustomItemType extends BaseItem {\n  type: 'custom';\n  /**\n   * @defaultValue false\n   */\n  secondary?: boolean;\n  children: ReactNode;\n}\n\nexport type LinkItemType =\n  | MainItemType\n  | IconItemType\n  | ButtonItemType\n  | MenuItemType\n  | CustomItemType;\n\n/**\n * Get Links Items with shortcuts\n */\nexport function getLinks(\n  links: LinkItemType[] = [],\n  githubUrl?: string,\n): LinkItemType[] {\n  let result = links ?? [];\n\n  if (githubUrl)\n    result = [\n      ...result,\n      {\n        type: 'icon',\n        url: githubUrl,\n        text: 'Github',\n        label: 'GitHub',\n        icon: (\n          <svg role=\"img\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n            <path d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" />\n          </svg>\n        ),\n        external: true,\n      },\n    ];\n\n  return result;\n}\n\nexport { BaseLinkItem } from './client';\n"
  },
  {
    "path": "apps/docs/src/components/logos/antigravity.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nfunction Antigravity(props: SVGProps<SVGSVGElement>) {\n\treturn (\n\t\t<svg {...props} viewBox=\"0 0 25 25\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t<path\n\t\t\t\td=\"M21.5159 22.2565C22.7548 23.1857 24.6132 22.5662 22.9097 20.8627C17.7992 15.9071 18.8833 2.2793 12.534 2.2793C6.1846 2.2793 7.26863 15.9071 2.15819 20.8627C0.299846 22.7211 2.31304 23.1857 3.55194 22.2565C8.35268 19.0044 8.04295 13.2745 12.534 13.2745C17.0249 13.2745 16.7152 19.0044 21.5159 22.2565Z\"\n\t\t\t\tfill=\"#3186FF\"\n\t\t\t/>\n\t\t\t<mask\n\t\t\t\tid=\"mask0_442_52\"\n\t\t\t\tstyle={{ maskType: \"alpha\" }}\n\t\t\t\tmaskUnits=\"userSpaceOnUse\"\n\t\t\t\tx=\"1\"\n\t\t\t\ty=\"2\"\n\t\t\t\twidth=\"23\"\n\t\t\t\theight=\"21\"\n\t\t\t>\n\t\t\t\t<path\n\t\t\t\t\td=\"M21.5159 22.2565C22.7548 23.1857 24.6132 22.5662 22.9097 20.8627C17.7992 15.9071 18.8833 2.2793 12.534 2.2793C6.1846 2.2793 7.26863 15.9071 2.15819 20.8627C0.299846 22.7211 2.31304 23.1857 3.55194 22.2565C8.35268 19.0044 8.04295 13.2745 12.534 13.2745C17.0249 13.2745 16.7152 19.0044 21.5159 22.2565Z\"\n\t\t\t\t\tfill=\"black\"\n\t\t\t\t/>\n\t\t\t</mask>\n\t\t\t<g mask=\"url(#mask0_442_52)\">\n\t\t\t\t<g filter=\"url(#filter0_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M0.463707 -2.33473C0.0868897 0.985011 2.94766 4.03558 6.85341 4.4789C10.7592 4.92222 14.2309 2.59043 14.6077 -0.729313C14.9845 -4.04905 12.1237 -7.09962 8.21799 -7.54294C4.31223 -7.98627 0.840525 -5.65447 0.463707 -2.33473Z\"\n\t\t\t\t\t\tfill=\"#FFE432\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter1_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M15.5226 8.51788C16.5012 12.7311 20.785 15.3359 25.0908 14.3358C29.3967 13.3357 32.094 9.10951 31.1154 4.89629C30.1368 0.683067 25.853 -1.9217 21.5472 -0.921626C17.2413 0.0784496 14.544 4.30466 15.5226 8.51788Z\"\n\t\t\t\t\t\tfill=\"#FC413D\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter2_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M-10.0992 11.3457C-8.86269 15.6939 -3.29395 17.9202 2.33894 16.3184C7.97183 14.7165 11.5358 9.8931 10.2993 5.54493C9.06281 1.19676 3.49407 -1.02958 -2.13882 0.572261C-7.77171 2.1741 -11.3357 6.99753 -10.0992 11.3457Z\"\n\t\t\t\t\t\tfill=\"#00B95C\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter3_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M-10.0992 11.3457C-8.86269 15.6939 -3.29395 17.9202 2.33894 16.3184C7.97183 14.7165 11.5358 9.8931 10.2993 5.54493C9.06281 1.19676 3.49407 -1.02958 -2.13882 0.572261C-7.77171 2.1741 -11.3357 6.99753 -10.0992 11.3457Z\"\n\t\t\t\t\t\tfill=\"#00B95C\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter4_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M-5.62929 14.9499C-2.52956 18.1159 2.80887 17.9161 6.29441 14.5036C9.77996 11.0911 10.0927 5.7581 6.99299 2.59204C3.89326 -0.574025 -1.44516 -0.374226 -4.93071 3.0383C-8.41626 6.45083 -8.72902 11.7838 -5.62929 14.9499Z\"\n\t\t\t\t\t\tfill=\"#00B95C\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter5_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M10.588 26.8897C11.5506 31.0339 15.566 33.6421 19.5568 32.7152C23.5475 31.7883 26.0024 27.6773 25.0398 23.5331C24.0773 19.3889 20.0619 16.7807 16.0711 17.7076C12.0803 18.6345 9.62549 22.7454 10.588 26.8897Z\"\n\t\t\t\t\t\tfill=\"#3186FF\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter6_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M3.78359 -6.21356C1.76807 -1.72377 3.97319 3.6393 8.70886 5.76518C13.4445 7.89107 18.9175 5.97475 20.933 1.48495C22.9485 -3.00484 20.7434 -8.3679 16.0077 -10.4938C11.272 -12.6197 5.79911 -10.7034 3.78359 -6.21356Z\"\n\t\t\t\t\t\tfill=\"#FBBC04\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter7_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M-1.61544 37.1238C-6.49482 35.2083 1.84391 19.4486 3.91745 14.1667C5.99102 8.88476 11.6275 6.15573 16.5068 8.07123C21.3862 9.98673 27.1698 20.537 25.0961 25.8189C23.0225 31.1009 3.26395 39.0392 -1.61544 37.1238Z\"\n\t\t\t\t\t\tfill=\"#3186FF\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter8_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M27.9498 17.5092C26.6434 19.0338 23.2345 18.2564 20.3356 15.7726C17.4367 13.2889 16.1457 10.0395 17.452 8.51483C18.7583 6.9902 22.1673 7.76767 25.0662 10.2514C27.9649 12.7351 29.2562 15.9845 27.9498 17.5092Z\"\n\t\t\t\t\t\tfill=\"#749BFF\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter9_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M18.1982 9.74778C23.571 13.3822 29.758 13.621 32.0173 10.2811C34.2765 6.94117 31.7525 1.28737 26.3797 -2.34704C21.0069 -5.98146 14.8199 -6.22022 12.5607 -2.88033C10.3014 0.459567 12.8254 6.11336 18.1982 9.74778Z\"\n\t\t\t\t\t\tfill=\"#FC413D\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t\t<g filter=\"url(#filter10_f_442_52)\">\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M0.559041 3.83691C-0.772032 7.04786 -0.337304 10.2784 1.53003 11.0525C3.39737 11.8266 5.99019 9.85108 7.32126 6.64012C8.65234 3.42917 8.21761 0.19865 6.35027 -0.575437C4.48294 -1.34952 1.89011 0.625949 0.559041 3.83691Z\"\n\t\t\t\t\t\tfill=\"#FFEE48\"\n\t\t\t\t\t/>\n\t\t\t\t</g>\n\t\t\t</g>\n\t\t\t<defs>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter0_f_442_52\"\n\t\t\t\t\tx=\"-1.63524\"\n\t\t\t\t\ty=\"-9.66325\"\n\t\t\t\t\twidth=\"18.3419\"\n\t\t\t\t\theight=\"16.2621\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"1.0328\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter1_f_442_52\"\n\t\t\t\t\tx=\"5.33592\"\n\t\t\t\t\ty=\"-11.1209\"\n\t\t\t\t\twidth=\"35.9662\"\n\t\t\t\t\theight=\"35.6558\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"4.99305\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter2_f_442_52\"\n\t\t\t\t\tx=\"-18.8334\"\n\t\t\t\t\ty=\"-8.43639\"\n\t\t\t\t\twidth=\"37.8669\"\n\t\t\t\t\theight=\"33.7634\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"4.24456\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter3_f_442_52\"\n\t\t\t\t\tx=\"-18.8334\"\n\t\t\t\t\ty=\"-8.43639\"\n\t\t\t\t\twidth=\"37.8669\"\n\t\t\t\t\theight=\"33.7634\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"4.24456\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter4_f_442_52\"\n\t\t\t\t\tx=\"-16.2532\"\n\t\t\t\t\ty=\"-8.14733\"\n\t\t\t\t\twidth=\"33.87\"\n\t\t\t\t\theight=\"33.8357\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"4.24456\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter5_f_442_52\"\n\t\t\t\t\tx=\"2.31192\"\n\t\t\t\t\ty=\"9.45431\"\n\t\t\t\t\twidth=\"31.004\"\n\t\t\t\t\theight=\"31.5152\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"4.03359\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter6_f_442_52\"\n\t\t\t\t\tx=\"-4.27428\"\n\t\t\t\t\ty=\"-18.6725\"\n\t\t\t\t\twidth=\"33.2651\"\n\t\t\t\t\theight=\"32.6164\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"3.65557\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter7_f_442_52\"\n\t\t\t\t\tx=\"-9.65318\"\n\t\t\t\t\ty=\"0.929829\"\n\t\t\t\t\twidth=\"41.7116\"\n\t\t\t\t\theight=\"43.0153\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"3.26458\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter8_f_442_52\"\n\t\t\t\t\tx=\"11.0998\"\n\t\t\t\t\ty=\"1.89196\"\n\t\t\t\t\twidth=\"23.2021\"\n\t\t\t\t\theight=\"22.2395\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"2.92121\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter9_f_442_52\"\n\t\t\t\t\tx=\"6.79823\"\n\t\t\t\t\ty=\"-10.1709\"\n\t\t\t\t\twidth=\"30.9815\"\n\t\t\t\t\theight=\"27.7442\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"2.46731\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t\t<filter\n\t\t\t\t\tid=\"filter10_f_442_52\"\n\t\t\t\t\tx=\"-6.31976\"\n\t\t\t\t\ty=\"-6.85156\"\n\t\t\t\t\twidth=\"20.5198\"\n\t\t\t\t\theight=\"24.1797\"\n\t\t\t\t\tfilterUnits=\"userSpaceOnUse\"\n\t\t\t\t\tcolor-interpolation-filters=\"sRGB\"\n\t\t\t\t>\n\t\t\t\t\t<feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n\t\t\t\t\t<feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n\t\t\t\t\t<feGaussianBlur stdDeviation=\"3.05371\" result=\"effect1_foregroundBlur_442_52\" />\n\t\t\t\t</filter>\n\t\t\t</defs>\n\t\t</svg>\n\t);\n}\n\nexport { Antigravity };\n"
  },
  {
    "path": "apps/docs/src/components/logos/azure-devops.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst AzureDevops = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\">\n\t\t<defs>\n\t\t\t<linearGradient\n\t\t\t\tid=\"a\"\n\t\t\t\tgradientUnits=\"userSpaceOnUse\"\n\t\t\t\tx1=\"9\"\n\t\t\t\ty1=\"16.97\"\n\t\t\t\tx2=\"9\"\n\t\t\t\ty2=\"1.03\"\n\t\t\t\tgradientTransform=\"scale(7.11111)\"\n\t\t\t>\n\t\t\t\t<stop offset=\"0\" stopColor=\"#0078d4\" />\n\t\t\t\t<stop offset=\".16\" stopColor=\"#1380da\" />\n\t\t\t\t<stop offset=\".53\" stopColor=\"#3c91e5\" />\n\t\t\t\t<stop offset=\".82\" stopColor=\"#559cec\" />\n\t\t\t\t<stop offset=\"1\" stopColor=\"#5ea0ef\" />\n\t\t\t</linearGradient>\n\t\t</defs>\n\t\t<path\n\t\t\tfill=\"url(#a)\"\n\t\t\td=\"M120.89 28.445v69.262l-28.445 23.324-44.09-16.07v15.93L23.395 88.25l72.746 5.688V31.574ZM96.64 31.93 55.82 7.11v16.285L18.348 34.418 7.109 48.852v32.785l16.075 7.11V46.718Zm0 0\"\n\t\t/>\n\t</svg>\n);\n\nexport { AzureDevops };"
  },
  {
    "path": "apps/docs/src/components/logos/biome.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Biomejs = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 64 55.425\" version=\"1.0\">\n    <rect width=\"100%\" height=\"100%\" fill=\"none\" />\n    <path\n      d=\"m32 0-14.255 24.69c5.409-1.6676 11.228-1.9148 16.869-0.58434l4.8177 1.1372-4.5328 19.22-4.8247-1.1372c-5.9293-1.3987-11.628 1.716-14.036 6.6851l-4.4595-2.1575c3.4034-7.0291 11.424-11.285 19.636-9.3476l2.2595-9.579c-8.0938-1.9081-16.624-9e-3 -23.145 5.153-6.5204 5.1607-10.329 13.028-10.329 21.344l64 7.9e-4z\"\n      fill=\"#60a5fa\"\n      style={{ paintOrder: \"markers fill stroke\" }}\n    />\n  </svg>\n);\n\nexport { Biomejs };\n"
  },
  {
    "path": "apps/docs/src/components/logos/bitbucket.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Bitbucket = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\">\n\t\t<defs>\n\t\t\t<linearGradient\n\t\t\t\tid=\"bitbucket-original-a\"\n\t\t\t\tgradientUnits=\"userSpaceOnUse\"\n\t\t\t\tx1=\"28.593\"\n\t\t\t\ty1=\"14.226\"\n\t\t\t\tx2=\"16.672\"\n\t\t\t\ty2=\"23.532\"\n\t\t\t\tgradientTransform=\"scale(4)\"\n\t\t\t>\n\t\t\t\t<stop offset=\".176\" stopColor=\"#0052cc\" />\n\t\t\t\t<stop offset=\"1\" stopColor=\"#2684ff\" />\n\t\t\t</linearGradient>\n\t\t</defs>\n\t\t<path\n\t\t\td=\"M19.082 20c-1.918 0-3.355 1.758-3.039 3.516l12.95 79.289c.32 2.078 2.077 3.515 4.155 3.515h62.66c1.442 0 2.72-1.12 3.04-2.558l13.109-80.086c.316-1.918-1.121-3.516-3.039-3.516zM74.07 77.227H54.09l-5.278-28.293h30.215zm0 0\"\n\t\t\tfill=\"#2684ff\"\n\t\t/>\n\t\t<path\n\t\t\td=\"M107.64 48.934H78.868L74.07 77.227H54.09l-23.5 27.972s1.12.961 2.719.961h62.66c1.441 0 2.719-1.12 3.039-2.558zm0 0\"\n\t\t\tfill=\"url(#bitbucket-original-a)\"\n\t\t/>\n\t</svg>\n);\n\nexport { Bitbucket };\n"
  },
  {
    "path": "apps/docs/src/components/logos/claude.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Claude = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} preserveAspectRatio=\"xMidYMid\" viewBox=\"0 0 256 257\">\n    <path\n      fill=\"#D97757\"\n      d=\"m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z\"\n    />\n  </svg>\n);\n\nexport { Claude };\n"
  },
  {
    "path": "apps/docs/src/components/logos/css.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst CSSNew = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    {...props}\n    aria-labelledby=\"css-logo-title css-logo-description\"\n    viewBox=\"0 0 1000 1000\"\n  >\n    <path\n      fill=\"#639\"\n      d=\"M0 0h840a160 160 0 0 1 160 160v680a160 160 0 0 1-160 160H160A160 160 0 0 1 0 840V0Z\"\n    />\n    <path\n      fill=\"#fff\"\n      d=\"M816.54 919.9c-32.39 0-57.16-9.42-74.5-28.35-17.15-19.03-26.08-46.18-26.88-81.64h69.8c.4 31.36 11.42 47.08 33.08 47.08 11.04 0 18.86-3.5 23.37-10.42 4.41-6.9 6.72-17.93 6.72-33.05 0-12.02-3.01-22.04-8.83-29.95a73.2 73.2 0 0 0-29.48-21.14L783.95 750c-23.06-11.02-39.81-24.04-50.14-39.27-10.03-15.13-15.04-36.36-15.04-63.5 0-30.36 8.83-55 26.37-73.94 18.05-18.93 42.62-28.34 74-28.34 30.3 0 53.76 9.31 70.3 27.84 16.85 18.64 25.67 45.28 26.38 80.14h-67.19c.4-11.4-1.9-22.72-6.72-33.06-3.8-7.6-11.23-11.41-22.26-11.41-19.65 0-29.48 11.71-29.48 35.05 0 11.83 2.4 21.04 7.22 28.05A65.18 65.18 0 0 0 822.76 689l24.77 10.92c25.57 11.72 44.02 26.05 55.35 43.38 11.43 17.23 17.05 40.27 17.05 69.12 0 34.56-9.03 61.1-27.38 79.63-18.25 18.53-43.62 27.85-76 27.85Zm-225.42 0c-32.4 0-57.16-9.42-74.51-28.35-17.15-19.03-26.07-46.18-26.87-81.64h69.79c.4 31.36 11.43 47.08 33.1 47.08 11.02 0 18.84-3.5 23.25-10.42 4.52-6.9 6.72-17.93 6.72-33.05 0-12.02-2.9-22.04-8.72-29.95a73.2 73.2 0 0 0-29.48-21.14L558.53 750c-23.07-11.02-39.81-24.04-50.14-39.27-10.03-15.13-15.04-36.36-15.04-63.5 0-30.36 8.82-55 26.37-73.94 18.05-18.93 42.62-28.34 74-28.34 30.29 0 53.75 9.31 70.2 27.84 17.05 18.64 25.77 45.28 26.47 80.14h-67.18c.4-11.4-1.9-22.72-6.72-33.06-3.81-7.6-11.23-11.41-22.26-11.41-19.66 0-29.49 11.71-29.49 35.05 0 11.83 2.41 21.04 7.22 28.05A65.18 65.18 0 0 0 597.33 689l24.77 10.92c25.57 11.72 44.02 26.05 55.36 43.38 11.33 17.23 17.04 40.27 17.04 69.12 0 34.56-9.12 61.1-27.37 79.63-18.25 18.53-43.62 27.85-76.01 27.85Zm-234.75 0c-31.7 0-56.86-8.62-75.51-25.85-18.65-17.12-27.88-42.87-27.88-76.93V648.83c0-33.85 9.83-59.5 29.48-77.13 19.96-17.43 46.13-26.24 78.52-26.24 31.39 0 56.15 9.01 74.5 26.84 18.56 17.93 27.88 44.58 27.88 80.14v13.32h-73.9v-12.92c0-13.72-3.01-23.84-8.83-30.45a26.46 26.46 0 0 0-21.66-10.32c-12.03 0-20.55 4.1-25.37 12.42a79.04 79.04 0 0 0-6.72 36.66v146.26c0 30.55 10.74 46.08 32.1 46.38 10.02 0 17.54-3.61 22.76-10.82a51.74 51.74 0 0 0 7.72-30.46V801.6h73.9v11.42c0 23.74-4.61 43.57-13.94 59.4a88.8 88.8 0 0 1-38.2 35.66 121.46 121.46 0 0 1-54.85 11.82Z\"\n    />\n  </svg>\n);\n\nexport { CSSNew };\n"
  },
  {
    "path": "apps/docs/src/components/logos/cursor.tsx",
    "content": "import { cn } from \"@/lib/utils\";\nimport type { SVGProps } from \"react\";\n\nconst Cursor = ({ className, ...props }: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} className={cn(\"fill-black dark:fill-white\", className)} viewBox=\"0 0 466.73 532.09\">\n\t\t<path d=\"M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z\" />\n\t</svg>\n);\n\nexport { Cursor };\n"
  },
  {
    "path": "apps/docs/src/components/logos/github.tsx",
    "content": "import { cn } from \"@/lib/utils\";\nimport type { SVGProps } from \"react\";\n\nconst GitHub = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} className={cn(props.className, 'fill-black dark:fill-white')} viewBox=\"0 0 1024 1024\" fill=\"none\">\n\t\t<path\n\t\t\tfillRule=\"evenodd\"\n\t\t\tclipRule=\"evenodd\"\n\t\t\td=\"M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z\"\n\t\t\ttransform=\"scale(64)\"\n\t\t/>\n\t</svg>\n);\n\nexport { GitHub };\n"
  },
  {
    "path": "apps/docs/src/components/logos/gitlab.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst GitLab = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} viewBox=\"0 0 32 32\" fill=\"none\" aria-labelledby=\"tanukiHomeDesktop\">\n\t\t<path\n\t\t\td=\"m31.46 12.78-.04-.12-4.35-11.35A1.14 1.14 0 0 0 25.94.6c-.24 0-.47.1-.66.24-.19.15-.33.36-.39.6l-2.94 9h-11.9l-2.94-9A1.14 1.14 0 0 0 6.07.58a1.15 1.15 0 0 0-1.14.72L.58 12.68l-.05.11a8.1 8.1 0 0 0 2.68 9.34l.02.01.04.03 6.63 4.97 3.28 2.48 2 1.52a1.35 1.35 0 0 0 1.62 0l2-1.52 3.28-2.48 6.67-5h.02a8.09 8.09 0 0 0 2.7-9.36Z\"\n\t\t\tfill=\"#E24329\"\n\t\t/>\n\t\t<path\n\t\t\td=\"m31.46 12.78-.04-.12a14.75 14.75 0 0 0-5.86 2.64l-9.55 7.24 6.09 4.6 6.67-5h.02a8.09 8.09 0 0 0 2.67-9.36Z\"\n\t\t\tfill=\"#FC6D26\"\n\t\t/>\n\t\t<path\n\t\t\td=\"m9.9 27.14 3.28 2.48 2 1.52a1.35 1.35 0 0 0 1.62 0l2-1.52 3.28-2.48-6.1-4.6-6.07 4.6Z\"\n\t\t\tfill=\"#FCA326\"\n\t\t/>\n\t\t<path\n\t\t\td=\"M6.44 15.3a14.71 14.71 0 0 0-5.86-2.63l-.05.12a8.1 8.1 0 0 0 2.68 9.34l.02.01.04.03 6.63 4.97 6.1-4.6-9.56-7.24Z\"\n\t\t\tfill=\"#FC6D26\"\n\t\t/>\n\t</svg>\n);\n\nexport { GitLab };\n"
  },
  {
    "path": "apps/docs/src/components/logos/html.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst HTML5 = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 452 520\">\n    <path fill=\"#e34f26\" d=\"M41 460L0 0h451l-41 460-185 52\" />\n    <path fill=\"#ef652a\" d=\"M226 472l149-41 35-394H226\" />\n    <path\n      fill=\"#ecedee\"\n      d=\"M226 208h-75l-5-58h80V94H84l15 171h127zm0 147l-64-17-4-45h-56l7 89 117 32z\"\n    />\n    <path\n      fill=\"#fff\"\n      d=\"M226 265h69l-7 73-62 17v59l115-32 16-174H226zm0-171v56h136l5-56z\"\n    />\n  </svg>\n);\n\nexport { HTML5 };\n"
  },
  {
    "path": "apps/docs/src/components/logos/index.ts",
    "content": "import { GitHub } from \"./github\";\nimport { GitLab } from \"./gitlab\";\nimport { AzureDevops } from \"./azure-devops\";\nimport { Bitbucket } from \"./bitbucket\";\nimport { Jsrepo } from \"./jsrepo-com\";\nimport { JavaScript } from \"./javascript\";\nimport { Svelte } from \"./svelte\";\nimport { Vue } from \"./vue\";\nimport { CSSNew } from \"./css\";\nimport { HTML5 } from \"./html\";\nimport { Claude } from \"./claude\";\nimport { OpenAI } from \"./openai\";\nimport { VSCode } from \"./vscode\";\nimport { Cursor } from \"./cursor\";\nimport { Shadcn } from \"./shadcn\";\nimport { Biomejs } from \"./biome\";\nimport { Prettier } from \"./prettier\";\nimport { Oxfmt } from \"./oxfmt\";\nimport { NPM } from \"./npm\";\nimport { TypeScript } from \"./typescript\";\nimport { RegistryKit } from \"./registry-kit\";\nimport { Antigravity } from \"./antigravity\";\n\nexport {\n\tGitHub as GitHubLogo,\n\tGitLab as GitLabLogo,\n\tAzureDevops as AzureDevopsLogo,\n\tBitbucket as BitbucketLogo,\n\tJsrepo as JsrepoLogo,\n\tJavaScript as JavaScriptLogo,\n\tSvelte as SvelteLogo,\n\tVue as VueLogo,\n\tCSSNew as CSSLogo,\n\tHTML5 as HTML5Logo,\n\tClaude as ClaudeLogo,\n\tOpenAI as OpenAILogo,\n\tVSCode as VSCodeLogo,\n\tCursor as CursorLogo,\n\tShadcn as ShadcnLogo,\n\tBiomejs as BiomeLogo,\n\tPrettier as PrettierLogo,\n\tOxfmt as OxfmtLogo,\n\tNPM as NpmLogo,\n\tTypeScript as TypeScriptLogo,\n\tRegistryKit as RegistryKitLogo,\n\tAntigravity as AntigravityLogo,\n};\n"
  },
  {
    "path": "apps/docs/src/components/logos/javascript.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst JavaScript = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} viewBox=\"0 0 1052 1052\">\n\t\t<path fill=\"#f0db4f\" d=\"M0 0h1052v1052H0z\" />\n\t\t<path\n\t\t\td=\"M965.9 801.1c-7.7-48-39-88.3-131.7-125.9-32.2-14.8-68.1-25.399-78.8-49.8-3.8-14.2-4.3-22.2-1.9-30.8 6.9-27.9 40.2-36.6 66.6-28.6 17 5.7 33.1 18.801 42.8 39.7 45.4-29.399 45.3-29.2 77-49.399-11.6-18-17.8-26.301-25.4-34-27.3-30.5-64.5-46.2-124-45-10.3 1.3-20.699 2.699-31 4-29.699 7.5-58 23.1-74.6 44-49.8 56.5-35.6 155.399 25 196.1 59.7 44.8 147.4 55 158.6 96.9 10.9 51.3-37.699 67.899-86 62-35.6-7.4-55.399-25.5-76.8-58.4-39.399 22.8-39.399 22.8-79.899 46.1 9.6 21 19.699 30.5 35.8 48.7 76.2 77.3 266.899 73.5 301.1-43.5 1.399-4.001 10.6-30.801 3.199-72.101zm-394-317.6h-98.4c0 85-.399 169.4-.399 254.4 0 54.1 2.8 103.7-6 118.9-14.4 29.899-51.7 26.2-68.7 20.399-17.3-8.5-26.1-20.6-36.3-37.699-2.8-4.9-4.9-8.7-5.601-9-26.699 16.3-53.3 32.699-80 49 13.301 27.3 32.9 51 58 66.399 37.5 22.5 87.9 29.4 140.601 17.3 34.3-10 63.899-30.699 79.399-62.199 22.4-41.3 17.6-91.3 17.4-146.6.5-90.2 0-180.4 0-270.9z\"\n\t\t\tfill=\"#323330\"\n\t\t/>\n\t</svg>\n);\n\nexport { JavaScript };\n"
  },
  {
    "path": "apps/docs/src/components/logos/jsrepo-com.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Jsrepo = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 3153 3153\">\n\t\t<g clipPath=\"url(#clip0_393_3)\">\n\t\t\t<path\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t\td=\"M2861 72C2982 72 3080 170 3080 291V2860C3080 2981 2982 3079 2861 3079H292C171 3079 73 2981 73 2860V291C73 170 171 72 292 72H2861ZM1362 983V1107H1731V1813C1731 1871 1717 1926 1674 1968C1631 2010 1574 2023 1516 2023H1313V2148H1516C1611 2148 1702 2124 1772 2058C1841 1993 1867 1907 1867 1814V985H1361L1362 983ZM429 2368C383 2368 346 2405 346 2451C346 2497 383 2534 429 2534H2723C2769 2534 2806 2497 2806 2451C2806 2405 2769 2368 2723 2368H429ZM1682 719C1682 747 1690 771 1711 790C1733 810 1763 816 1792 816C1821 816 1851 810 1873 790C1894 771 1902 747 1902 719C1902 690 1894 665 1873 646C1851 626 1822 619 1792 619C1763 619 1733 626 1711 646C1690 666 1682 691 1682 719ZM2262 1650H2125C2130 1692 2145 1732 2171 1766C2198 1802 2234 1827 2275 1844C2324 1864 2378 1872 2430 1872H2497C2576 1872 2668 1858 2730 1803C2785 1753 2807 1687 2807 1614C2807 1550 2792 1486 2743 1440C2690 1390 2607 1369 2537 1358L2418 1339C2384 1334 2342 1323 2315 1301C2289 1280 2281 1249 2281 1218C2281 1182 2291 1147 2320 1123C2352 1097 2402 1092 2442 1092H2503C2539 1092 2579 1098 2610 1118C2635 1134 2652 1156 2659 1184H2796C2785 1122 2756 1070 2705 1031C2648 987 2575 972 2504 972H2443C2367 972 2279 985 2219 1036C2165 1083 2145 1147 2145 1217C2145 1279 2160 1341 2207 1385C2257 1431 2333 1454 2398 1465L2517 1484C2557 1490 2600 1502 2631 1529C2659 1553 2670 1585 2670 1621C2670 1659 2657 1692 2627 1716C2592 1745 2541 1751 2498 1751H2431C2392 1751 2349 1745 2316 1723C2288 1705 2270 1681 2263 1650H2262ZM529 824C479 786 408 796 370 846C355 866 347 890 347 914C347 950 364 983 392 1005L931 1412L392 1819C363 1841 347 1874 347 1910C347 1935 355 1959 370 1978C408 2028 479 2038 529 2000L1188 1503C1216 1482 1233 1448 1233 1412C1233 1376 1216 1343 1188 1321L529 824Z\"\n\t\t\t\tfill=\"#F7DD1D\"\n\t\t\t\tstroke=\"#373435\"\n\t\t\t\tstrokeWidth=\"6.94488\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M2723 2329H429C362 2329 307 2384 307 2451C307 2518 362 2573 429 2573H2723C2790 2573 2845 2518 2845 2451C2845 2384 2790 2329 2723 2329ZM2603 1685C2582 1703 2546 1712 2498 1712H2431C2391 1712 2359 1705 2337 1690C2316 1677 2305 1660 2300 1638L2294 1610H2082L2086 1649C2092 1702 2110 1749 2140 1788C2170 1827 2210 1857 2260 1878C2309 1898 2366 1909 2430 1909H2497C2610 1909 2697 1883 2755 1830C2814 1777 2844 1704 2844 1612C2844 1525 2818 1457 2768 1410C2719 1364 2645 1334 2542 1318L2423 1299C2373 1291 2349 1278 2338 1269C2333 1265 2319 1254 2319 1216C2319 1186 2327 1165 2344 1151C2362 1136 2396 1128 2442 1128H2503C2540 1128 2569 1135 2589 1147C2607 1159 2618 1173 2622 1192L2628 1219H2841L2835 1179C2823 1103 2787 1041 2729 996C2672 952 2596 929 2504 929H2443C2334 929 2250 954 2194 1002C2136 1052 2106 1123 2106 1212C2106 1297 2131 1363 2181 1409C2228 1453 2299 1483 2392 1499L2511 1518C2554 1524 2585 1537 2605 1554C2623 1569 2631 1589 2631 1617C2631 1645 2622 1666 2602 1683L2603 1685ZM1686 818C1713 842 1749 854 1793 854C1837 854 1873 842 1900 818C1928 793 1942 759 1942 718C1942 676 1928 642 1900 616C1873 591 1837 579 1793 579C1749 579 1713 592 1686 616C1658 642 1644 676 1644 718C1644 759 1658 793 1686 818ZM1800 2084C1871 2016 1907 1925 1907 1812V944H1324V1145C1324 1145 1636 1145 1693 1145C1693 1208 1693 1812 1693 1812C1693 1868 1678 1910 1648 1939C1618 1969 1575 1983 1517 1983H1275V2186H1517C1634 2186 1729 2152 1799 2085L1800 2084ZM308 1909C308 1941 318 1973 339 2001C390 2068 485 2082 553 2031L1212 1534C1250 1505 1273 1460 1273 1412C1273 1364 1251 1319 1212 1290L553 793C486 742 390 756 339 823C318 851 308 883 308 915C308 961 329 1007 369 1037C369 1037 683 1274 867 1413C683 1552 369 1789 369 1789C329 1819 308 1865 308 1911V1909Z\"\n\t\t\t\tfill=\"black\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M111 2861V292C111 192 192 111 292 111H2861C2961 111 3042 192 3042 292V2861C3042 2961 2961 3042 2861 3042H292C192 3042 111 2961 111 2861ZM292 0C131 0 0 131 0 292V2861C0 3022 131 3153 292 3153H2861C3022 3153 3153 3022 3153 2861V292C3153 131 3022 0 2861 0H292Z\"\n\t\t\t\tfill=\"#F7DD1D\"\n\t\t\t/>\n\t\t</g>\n\t\t<defs>\n\t\t\t<clipPath id=\"clip0_393_3\">\n\t\t\t\t<rect width=\"3153\" height=\"3153\" fill=\"white\" />\n\t\t\t</clipPath>\n\t\t</defs>\n\t</svg>\n);\n\nconst JsrepoWordmark = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 6303 2541\" fill=\"none\">\n\t\t<g clipPath=\"url(#clip0_393_47)\">\n\t\t\t<path\n\t\t\t\td=\"M6081 40H235C131.17 40 47 124.17 47 228V2301C47 2404.83 131.17 2489 235 2489H6081C6184.83 2489 6269 2404.83 6269 2301V228C6269 124.17 6184.83 40 6081 40Z\"\n\t\t\t\tfill=\"#F7DD1D\"\n\t\t\t\tstroke=\"black\"\n\t\t\t\tstrokeWidth=\"6.94488\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M495 614.001C477 638.001 468 666.001 468 694.001C468 734.001 486 774.001 521 800.001C521 800.001 794 1006 955 1127C795 1248 521 1454 521 1454C486 1480 468 1520 468 1560C468 1588 477 1616 495 1640C539 1699 623 1710 681 1666L1255 1233C1288 1208 1308 1169 1308 1127C1308 1085 1288 1046 1255 1021L681 588.001C622 544.001 539 555.001 495 614.001Z\"\n\t\t\t\tfill=\"black\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M5648 1237C5648 1288 5635 1327 5611 1351C5586 1376 5551 1388 5504 1388C5458 1388 5423 1376 5397 1351C5372 1326 5359 1288 5359 1237V1033C5359 982 5372 944 5397 919C5423 894 5458 882 5504 882C5551 882 5586 894 5611 919C5636 944 5648 982 5648 1033V1237ZM5793 860C5765 811 5726 773 5676 747C5627 722 5569 709 5503 709C5437 709 5378 722 5330 747C5280 773 5241 811 5213 860C5186 908 5172 966 5172 1033V1237C5172 1303 5186 1361 5213 1409C5241 1458 5280 1497 5330 1523C5379 1548 5437 1561 5503 1561C5569 1561 5628 1548 5676 1523C5726 1497 5765 1459 5793 1410C5820 1361 5834 1303 5834 1238V1032C5834 966 5820 909 5793 860ZM4864 1240C4864 1291 4852 1330 4827 1356C4802 1382 4769 1394 4724 1394C4681 1394 4648 1381 4623 1355C4597 1328 4585 1290 4585 1240V1030C4585 980 4597 942 4623 915C4648 888 4681 876 4724 876C4769 876 4803 889 4827 914C4852 940 4864 979 4864 1030V1240ZM4766 706C4695 706 4637 726 4593 765C4590 768 4588 771 4585 774C4585 768 4585 721 4585 721H4399V1803H4585V1549C4585 1549 4585 1518 4584 1496C4587 1499 4590 1503 4593 1506C4638 1545 4696 1565 4765 1565C4823 1565 4873 1552 4916 1526C4959 1500 4992 1462 5015 1414C5038 1367 5050 1311 5050 1248V1023C5050 927 5024 850 4973 794C4921 737 4851 708 4765 708L4766 706ZM3789 1030C3789 978 3802 938 3827 911C3852 884 3888 872 3937 872C3986 872 4022 885 4047 911C4072 938 4084 978 4084 1030C4084 1030 4084 1037 4084 1040H3789C3789 1036 3789 1030 3789 1030ZM4079 1318C4074 1344 4061 1362 4039 1375C4014 1389 3980 1397 3937 1397C3888 1397 3852 1384 3827 1358C3802 1332 3789 1292 3789 1240C3789 1240 3789 1217 3789 1196C3842 1196 4267 1196 4267 1196V1030C4267 965 4253 908 4226 860C4199 811 4159 773 4109 746C4060 719 4001 706 3936 706C3871 706 3812 719 3763 746C3713 773 3674 811 3646 860C3619 908 3605 965 3605 1030V1240C3605 1304 3619 1361 3646 1409C3673 1458 3713 1497 3763 1524C3812 1551 3871 1564 3936 1564C4023 1564 4097 1543 4157 1502C4219 1459 4255 1401 4264 1328L4268 1294H4083L4078 1319L4079 1318ZM3187 706C3113 706 3056 728 3014 771C3014 766 3014 720 3014 720H2830V1550H3016V1030C3016 978 3029 939 3053 912C3078 886 3111 873 3156 873C3202 873 3235 886 3259 911C3283 937 3295 977 3295 1030V1095H3481V1019C3481 925 3454 848 3401 792C3347 735 3275 706 3185 706H3187ZM2417 1046L2314 1029C2271 1022 2250 1011 2240 1003C2236 1000 2223 990 2223 957C2223 931 2230 913 2245 901C2261 888 2291 881 2330 881H2383C2415 881 2440 887 2458 898C2474 908 2483 921 2487 937L2492 960H2677L2671 925C2660 859 2629 805 2578 766C2528 727 2462 708 2382 708H2329C2234 708 2161 730 2112 772C2061 816 2036 877 2036 955C2036 1029 2058 1086 2101 1126C2142 1164 2204 1190 2285 1204L2389 1221C2426 1227 2454 1237 2471 1252C2486 1265 2493 1283 2493 1307C2493 1332 2485 1350 2468 1364C2449 1379 2419 1387 2377 1387H2318C2283 1387 2255 1381 2236 1368C2218 1356 2208 1342 2204 1323L2199 1299H2015L2019 1333C2024 1379 2040 1420 2066 1454C2092 1488 2127 1514 2171 1533C2213 1551 2263 1560 2319 1560H2378C2477 1560 2552 1537 2603 1491C2655 1445 2681 1381 2681 1301C2681 1225 2659 1166 2615 1125C2572 1085 2508 1059 2418 1044L2417 1046ZM1762 402C1724 402 1692 413 1669 434C1645 456 1633 486 1633 523C1633 559 1645 588 1670 610C1693 631 1725 641 1763 641C1801 641 1833 630 1856 610C1880 588 1893 559 1893 523C1893 486 1881 456 1857 434C1834 412 1802 402 1764 402H1762ZM1353 895C1353 895 1625 895 1675 895C1675 950 1675 1476 1675 1476C1675 1525 1662 1561 1636 1587C1609 1613 1572 1625 1522 1625H1311V1802H1522C1624 1802 1707 1772 1768 1714C1830 1655 1861 1575 1861 1477V721H1353V896V895Z\"\n\t\t\t\tfill=\"black\"\n\t\t\t/>\n\t\t\t<g clipPath=\"url(#clip1_393_47)\">\n\t\t\t\t<path\n\t\t\t\t\td=\"M574 1975C542 1975 516 2001 516 2033C516 2065 542 2091 574 2091H5719C5751 2091 5777 2065 5777 2033C5777 2001 5751 1975 5719 1975H574Z\"\n\t\t\t\t\tfill=\"black\"\n\t\t\t\t/>\n\t\t\t</g>\n\t\t\t<path\n\t\t\t\td=\"M565 2033C565 2028 569 2024 574 2024H5719C5724 2024 5728 2028 5728 2033C5728 2038 5724 2042 5719 2042H574C569 2042 565 2038 565 2033ZM574 1927C515 1927 468 1975 468 2033C468 2092 516 2139 574 2139H5719C5778 2139 5825 2091 5825 2033C5825 1974 5777 1927 5719 1927H574Z\"\n\t\t\t\tfill=\"black\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M97 2303V238C97 160 160 97 238 97H6065C6143 97 6206 160 6206 238V2303C6206 2381 6143 2444 6065 2444H238C160 2444 97 2381 97 2303ZM238 0C107 0 0 107 0 238V2303C0 2434 107 2541 238 2541H6065C6196 2541 6303 2434 6303 2303V238C6303 107 6196 0 6065 0H238Z\"\n\t\t\t\tfill=\"#F7DD1D\"\n\t\t\t/>\n\t\t</g>\n\t\t<defs>\n\t\t\t<clipPath id=\"clip0_393_47\">\n\t\t\t\t<rect width=\"6303\" height=\"2541\" fill=\"white\" />\n\t\t\t</clipPath>\n\t\t\t<clipPath id=\"clip1_393_47\">\n\t\t\t\t<rect width=\"5260\" height=\"116\" fill=\"white\" transform=\"translate(517 1975)\" />\n\t\t\t</clipPath>\n\t\t</defs>\n\t</svg>\n);\n\nexport { Jsrepo, JsrepoWordmark };"
  },
  {
    "path": "apps/docs/src/components/logos/npm.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst NPM = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 2500 2500\">\n    <path fill=\"#c00\" d=\"M0 0h2500v2500H0z\" />\n    <path\n      fill=\"#fff\"\n      d=\"M1241.5 268.5h-973v1962.9h972.9V763.5h495v1467.9h495V268.5z\"\n    />\n  </svg>\n);\n\nexport { NPM };\n"
  },
  {
    "path": "apps/docs/src/components/logos/openai.tsx",
    "content": "import { cn } from \"@/lib/utils\";\nimport type { SVGProps } from \"react\";\n\nconst OpenAI = ({ className, ...props }: SVGProps<SVGSVGElement>) => (\n  <svg {...props} className={cn(\"text-black dark:text-white\", className)} preserveAspectRatio=\"xMidYMid\" viewBox=\"0 0 256 260\">\n    <path\n      fill=\"currentColor\"\n      d=\"M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z\"\n    />\n  </svg>\n);\n\nexport { OpenAI };\n"
  },
  {
    "path": "apps/docs/src/components/logos/oxfmt.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Oxfmt = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} fill=\"none\" viewBox=\"0 0 48 46\">\n    <path\n      fill=\"#32f3ef\"\n      d=\"M30.316 6.789a2.26 2.26 0 0 0 2.262 2.262h8.582c1.009 0 1.513 1.219.8 1.93L30.978 21.963a2.26 2.26 0 0 0-.664 1.6v3.755c0 1.562 1.55 2.645 2.92 1.9a20.4 20.4 0 0 0 3.868-2.727c.465-.416 1.18-.42 1.622.023l7.999 7.999a1.1 1.1 0 0 1-.013 1.585 33.82 33.82 0 0 1-23.184 9.153A33.82 33.82 0 0 1 .343 36.098a1.1 1.1 0 0 1-.013-1.585l7.999-7.999c.441-.441 1.157-.44 1.622-.023a20.4 20.4 0 0 0 3.869 2.727c1.372.745 2.919-.338 2.919-1.9v-3.755c0-.6-.24-1.176-.664-1.6L5.093 10.98c-.713-.713-.209-1.93.8-1.93h8.582a2.26 2.26 0 0 0 2.262-2.262V1.132a1.13 1.13 0 0 1 1.13-1.131H29.18a1.13 1.13 0 0 1 1.131 1.13V6.79z\"\n    />\n    <mask\n      id=\"oxc__mask0_2002_17235\"\n      width=\"48\"\n      height=\"46\"\n      x=\"0\"\n      y=\"0\"\n      maskUnits=\"userSpaceOnUse\"\n      style={{ maskType: \"alpha\" }}\n    >\n      <path\n        fill=\"#d9d9d9\"\n        d=\"M30.316 6.788a2.26 2.26 0 0 0 2.262 2.262h8.582c1.009 0 1.513 1.219.8 1.93L30.978 21.962a2.26 2.26 0 0 0-.664 1.6v3.755c0 1.562 1.55 2.645 2.92 1.9a20.4 20.4 0 0 0 3.868-2.727c.465-.416 1.18-.42 1.622.024l7.999 7.998a1.1 1.1 0 0 1-.013 1.585 33.82 33.82 0 0 1-23.184 9.153A33.82 33.82 0 0 1 .343 36.097a1.1 1.1 0 0 1-.013-1.585l7.999-7.998c.441-.442 1.157-.44 1.622-.024a20.4 20.4 0 0 0 3.869 2.727c1.372.745 2.919-.338 2.919-1.9v-3.755c0-.6-.24-1.176-.664-1.6L5.093 10.98c-.713-.713-.209-1.93.8-1.93h8.582a2.26 2.26 0 0 0 2.262-2.262V1.13a1.13 1.13 0 0 1 1.13-1.131H29.18a1.13 1.13 0 0 1 1.131 1.13v5.658z\"\n      />\n    </mask>\n    <g mask=\"url(#oxc__mask0_2002_17235)\">\n      <g filter=\"url(#oxc__filter0_f_2002_17235)\">\n        <ellipse\n          cx=\"4.618\"\n          cy=\"21.501\"\n          fill=\"#aefffb\"\n          rx=\"4.618\"\n          ry=\"21.501\"\n          transform=\"matrix(0 -1 -1 0 44.898 50.594)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter1_f_2002_17235)\">\n        <ellipse\n          cx=\"4.618\"\n          cy=\"21.501\"\n          fill=\"#0f8\"\n          rx=\"4.618\"\n          ry=\"21.501\"\n          transform=\"rotate(68.34 1.902 66.786)scale(-1 1)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter2_f_2002_17235)\">\n        <ellipse\n          cx=\"5.201\"\n          cy=\"44.012\"\n          fill=\"#195eff\"\n          rx=\"4.618\"\n          ry=\"21.501\"\n          transform=\"rotate(-68.34 5.201 44.012)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter3_f_2002_17235)\">\n        <ellipse\n          cx=\"31.927\"\n          cy=\"28.892\"\n          fill=\"#aefffb\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(-135.197 31.927 28.892)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter4_f_2002_17235)\">\n        <ellipse\n          cx=\"33.843\"\n          cy=\"25.445\"\n          fill=\"#195eff\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(-135.197 33.843 25.445)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter5_f_2002_17235)\">\n        <ellipse\n          cx=\"33.459\"\n          cy=\"6.297\"\n          fill=\"#aefffb\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(-135.197 33.46 6.297)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter6_f_2002_17235)\">\n        <ellipse\n          cx=\"34.608\"\n          cy=\"4.765\"\n          fill=\"#195eff\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(-135.197 34.608 4.765)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter7_f_2002_17235)\">\n        <ellipse\n          cx=\"3.715\"\n          cy=\"6.268\"\n          fill=\"#aefffb\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(135.197 1.052 21.915)scale(-1 1)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter8_f_2002_17235)\">\n        <ellipse\n          cx=\"3.715\"\n          cy=\"6.268\"\n          fill=\"#195eff\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(135.197 .422 19.64)scale(-1 1)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter9_f_2002_17235)\">\n        <ellipse\n          cx=\"3.715\"\n          cy=\"6.268\"\n          fill=\"#aefffb\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(135.197 4.526 9.682)scale(-1 1)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter10_f_2002_17235)\">\n        <ellipse\n          cx=\"3.715\"\n          cy=\"6.268\"\n          fill=\"#195eff\"\n          rx=\"3.715\"\n          ry=\"6.268\"\n          transform=\"rotate(135.197 4.808 8.455)scale(-1 1)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter11_f_2002_17235)\">\n        <ellipse\n          cx=\"3.715\"\n          cy=\"11.949\"\n          fill=\"#195eff\"\n          rx=\"3.715\"\n          ry=\"11.949\"\n          transform=\"rotate(135.197 1.56 17.42)scale(-1 1)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter12_f_2002_17235)\">\n        <ellipse\n          cx=\"38.995\"\n          cy=\"14.521\"\n          fill=\"#0f8\"\n          rx=\"3.715\"\n          ry=\"11.949\"\n          transform=\"rotate(-135.197 38.995 14.521)\"\n        />\n      </g>\n      <g filter=\"url(#oxc__filter13_f_2002_17235)\">\n        <ellipse\n          cx=\"4.618\"\n          cy=\"21.501\"\n          fill=\"#195eff\"\n          rx=\"4.618\"\n          ry=\"21.501\"\n          transform=\"matrix(0 -1 -1 0 44.898 52.509)\"\n        />\n      </g>\n    </g>\n    <defs>\n      <filter\n        id=\"oxc__filter0_f_2002_17235\"\n        width=\"61.384\"\n        height=\"27.618\"\n        x=\"-7.294\"\n        y=\"32.167\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter1_f_2002_17235\"\n        width=\"58.495\"\n        height=\"36.43\"\n        x=\"12.335\"\n        y=\"25.797\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter2_f_2002_17235\"\n        width=\"58.495\"\n        height=\"36.43\"\n        x=\"-24.046\"\n        y=\"25.797\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter3_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"17.592\"\n        y=\"14.539\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter4_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"19.507\"\n        y=\"11.093\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter5_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"19.123\"\n        y=\"-8.055\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter6_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"20.272\"\n        y=\"-9.587\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter7_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"1.124\"\n        y=\"15.305\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter8_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"-1.557\"\n        y=\"11.859\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter9_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"-1.557\"\n        y=\"-8.055\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter10_f_2002_17235\"\n        width=\"28.672\"\n        height=\"28.706\"\n        x=\"-1.939\"\n        y=\"-10.353\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter11_f_2002_17235\"\n        width=\"36.034\"\n        height=\"36.133\"\n        x=\"-8.858\"\n        y=\"-.482\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter12_f_2002_17235\"\n        width=\"36.034\"\n        height=\"36.133\"\n        x=\"20.978\"\n        y=\"-3.545\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n      <filter\n        id=\"oxc__filter13_f_2002_17235\"\n        width=\"61.384\"\n        height=\"27.618\"\n        x=\"-7.294\"\n        y=\"34.082\"\n        colorInterpolationFilters=\"sRGB\"\n        filterUnits=\"userSpaceOnUse\"\n      >\n        <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n        <feBlend in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n        <feGaussianBlur\n          result=\"effect1_foregroundBlur_2002_17235\"\n          stdDeviation=\"4.596\"\n        />\n      </filter>\n    </defs>\n  </svg>\n);\n\nexport { Oxfmt };\n"
  },
  {
    "path": "apps/docs/src/components/logos/prettier.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Prettier = (props: SVGProps<SVGSVGElement>) => (\n\t<svg {...props} viewBox=\"0 0 210 210\">\n\t\t<g fill=\"none\" fillRule=\"evenodd\">\n\t\t\t<g transform=\"translate(0 200)\">\n\t\t\t\t<rect width=\"60\" height=\"10\" x=\"150\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"70\" height=\"10\" x=\"70\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"60\" height=\"10\" fill=\"#EA5E5E\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 180)\">\n\t\t\t\t<rect width=\"50\" height=\"10\" x=\"160\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"20\" height=\"10\" x=\"130\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"50\" height=\"10\" x=\"70\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"20\" height=\"10\" x=\"40\" fill=\"#F7B93E\" rx=\"5\" />\n\t\t\t\t<rect width=\"30\" height=\"10\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 160)\">\n\t\t\t\t<rect width=\"100\" height=\"10\" x=\"110\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"30\" height=\"10\" x=\"70\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"60\" height=\"10\" fill=\"#BF85BF\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 140)\">\n\t\t\t\t<rect width=\"30\" height=\"10\" x=\"180\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"30\" height=\"10\" x=\"140\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"100\" height=\"10\" x=\"30\" fill=\"#F7B93E\" rx=\"5\" />\n\t\t\t\t<rect width=\"20\" height=\"10\" fill=\"#BF85BF\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 120)\">\n\t\t\t\t<rect width=\"40\" height=\"10\" x=\"170\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"40\" height=\"10\" x=\"120\" fill=\"#BF85BF\" rx=\"5\" />\n\t\t\t\t<rect width=\"50\" height=\"10\" x=\"60\" fill=\"#EA5E5E\" rx=\"5\" />\n\t\t\t\t<rect width=\"50\" height=\"10\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 100)\">\n\t\t\t\t<rect width=\"30\" height=\"10\" x=\"180\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"90\" height=\"10\" x=\"80\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t\t\t<rect width=\"40\" height=\"10\" x=\"30\" fill=\"#F7B93E\" rx=\"5\" />\n\t\t\t\t<rect width=\"20\" height=\"10\" fill=\"#EA5E5E\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 80)\">\n\t\t\t\t<rect width=\"20\" height=\"10\" x=\"190\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"60\" height=\"10\" x=\"120\" fill=\"#F7B93E\" rx=\"5\" />\n\t\t\t\t<rect width=\"40\" height=\"10\" x=\"70\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"60\" height=\"10\" fill=\"#BF85BF\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 60)\">\n\t\t\t\t<rect width=\"20\" height=\"10\" x=\"190\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"60\" height=\"10\" x=\"120\" fill=\"#EA5E5E\" rx=\"5\" />\n\t\t\t\t<rect width=\"40\" height=\"10\" x=\"70\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"20\" height=\"10\" x=\"40\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t\t\t<rect width=\"30\" height=\"10\" fill=\"#F7B93E\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 40)\">\n\t\t\t\t<rect width=\"30\" height=\"10\" x=\"180\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"20\" height=\"10\" x=\"150\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t\t\t<rect width=\"50\" height=\"10\" x=\"90\" fill=\"#BF85BF\" rx=\"5\" />\n\t\t\t\t<rect width=\"80\" height=\"10\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<g transform=\"translate(0 20)\">\n\t\t\t\t<rect width=\"40\" height=\"10\" x=\"170\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t\t<rect width=\"110\" height=\"10\" x=\"50\" fill=\"#F7B93E\" rx=\"5\" />\n\t\t\t\t<rect width=\"40\" height=\"10\" fill=\"#EA5E5E\" rx=\"5\" />\n\t\t\t</g>\n\t\t\t<rect width=\"70\" height=\"10\" x=\"140\" fill=\"#CFD4D7\" opacity=\".5\" rx=\"5\" />\n\t\t\t<rect width=\"130\" height=\"10\" fill=\"#56B3B4\" rx=\"5\" />\n\t\t</g>\n\t</svg>\n);\n\nexport { Prettier };\n"
  },
  {
    "path": "apps/docs/src/components/logos/registry-kit.tsx",
    "content": "import { cn } from \"@/lib/utils\";\nimport type { SVGProps } from \"react\";\n\nconst RegistryKit = ({ className, ...props }: SVGProps<SVGSVGElement>) => (\n\t<svg\n\t\t{...props}\n\t\tclassName={cn(className)}\n\t\tviewBox=\"0 0 96 96\"\n\t\tfill=\"none\"\n\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t>\n\t\t<g clipPath=\"url(#clip0_437_28)\">\n\t\t\t<path\n\t\t\t\td=\"M42.144 93V3.39999H59.68V41.16H60.32L76.32 3.39999H94.368L75.68 46.152L95.264 93H75.808L60.576 53.832H59.68V93H42.144Z\"\n\t\t\t\tclassName=\"fill-black dark:fill-white\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\td=\"M6.144 3.39999H40.192C45.568 5.192 50.1333 8.52 53.888 13.384C57.728 18.1627 59.648 24.0507 59.648 31.048C59.648 42.4827 55.1253 50.888 46.08 56.264L59.008 93H41.088L30.208 60.36H23.424V93H6.144V3.39999ZM23.424 18.76V46.024H34.432C35.8827 45.6827 37.4613 44.232 39.168 41.672C40.96 39.0267 41.856 35.9547 41.856 32.456C41.856 28.9573 41.088 25.8853 39.552 23.24C38.1013 20.5947 36.6507 19.1013 35.2 18.76H23.424Z\"\n\t\t\t\tfill=\"#F0DB4F\"\n\t\t\t/>\n\t\t</g>\n\t</svg>\n);\n\nexport { RegistryKit };\n"
  },
  {
    "path": "apps/docs/src/components/logos/shadcn.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Shadcn = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 256 256\">\n    <path fill=\"none\" d=\"M0 0h256v256H0z\" />\n    <path\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"25\"\n      strokeLinecap=\"round\"\n      d=\"M208 128l-80 80M192 40L40 192\"\n    />\n  </svg>\n);\n\nexport { Shadcn };\n"
  },
  {
    "path": "apps/docs/src/components/logos/svelte.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Svelte = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 256 308\" preserveAspectRatio=\"xMidYMid\">\n    <path\n      d=\"M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056 86.566 86.566 0 0 0 8.536 55.576 82.425 82.425 0 0 0-12.296 30.719 87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057 86.601 86.601 0 0 0-8.53-55.577 82.409 82.409 0 0 0 12.29-30.718 87.573 87.573 0 0 0-14.963-66.244\"\n      fill=\"#FF3E00\"\n    />\n    <path\n      d=\"M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85 49.978 49.978 0 0 1 1.713-6.693l1.35-4.115 3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808-.245 2.659a16.067 16.067 0 0 0 2.89 10.656 17.143 17.143 0 0 0 18.397 6.828 15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977 15.923 15.923 0 0 0-2.713-12.011 17.156 17.156 0 0 0-18.404-6.832 15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849 49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85 50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809.246-2.658a16.099 16.099 0 0 0-2.89-10.656 17.143 17.143 0 0 0-18.398-6.828 15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975 15.9 15.9 0 0 0 2.709 12.012 17.156 17.156 0 0 0 18.404 6.832 15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848 49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398\"\n      fill=\"#FFF\"\n    />\n  </svg>\n);\n\nexport { Svelte };\n"
  },
  {
    "path": "apps/docs/src/components/logos/typescript.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst TypeScript = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 256 256\" preserveAspectRatio=\"xMidYMid\">\n    <path\n      d=\"M20 0h216c11.046 0 20 8.954 20 20v216c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20V20C0 8.954 8.954 0 20 0Z\"\n      fill=\"#3178C6\"\n    />\n    <path\n      d=\"M150.518 200.475v27.62c4.492 2.302 9.805 4.028 15.938 5.179 6.133 1.151 12.597 1.726 19.393 1.726 6.622 0 12.914-.633 18.874-1.899 5.96-1.266 11.187-3.352 15.678-6.257 4.492-2.906 8.048-6.704 10.669-11.394 2.62-4.689 3.93-10.486 3.93-17.391 0-5.006-.749-9.394-2.246-13.163a30.748 30.748 0 0 0-6.479-10.055c-2.821-2.935-6.205-5.567-10.149-7.898-3.945-2.33-8.394-4.531-13.347-6.602-3.628-1.497-6.881-2.949-9.761-4.359-2.879-1.41-5.327-2.848-7.342-4.316-2.016-1.467-3.571-3.021-4.665-4.661-1.094-1.64-1.641-3.495-1.641-5.567 0-1.899.489-3.61 1.468-5.135s2.362-2.834 4.147-3.927c1.785-1.094 3.973-1.942 6.565-2.547 2.591-.604 5.471-.906 8.638-.906 2.304 0 4.737.173 7.299.518 2.563.345 5.14.877 7.732 1.597a53.669 53.669 0 0 1 7.558 2.719 41.7 41.7 0 0 1 6.781 3.797v-25.807c-4.204-1.611-8.797-2.805-13.778-3.582-4.981-.777-10.697-1.165-17.147-1.165-6.565 0-12.784.705-18.658 2.115-5.874 1.409-11.043 3.61-15.506 6.602-4.463 2.993-7.99 6.805-10.582 11.437-2.591 4.632-3.887 10.17-3.887 16.615 0 8.228 2.375 15.248 7.127 21.06 4.751 5.811 11.963 10.731 21.638 14.759a291.458 291.458 0 0 1 10.625 4.575c3.283 1.496 6.119 3.049 8.509 4.66 2.39 1.611 4.276 3.366 5.658 5.265 1.382 1.899 2.073 4.057 2.073 6.474a9.901 9.901 0 0 1-1.296 4.963c-.863 1.524-2.174 2.848-3.93 3.97-1.756 1.122-3.945 1.999-6.565 2.632-2.62.633-5.687.95-9.2.95-5.989 0-11.92-1.05-17.794-3.151-5.875-2.1-11.317-5.25-16.327-9.451Zm-46.036-68.733H140V109H41v22.742h35.345V233h28.137V131.742Z\"\n      fill=\"#FFF\"\n    />\n  </svg>\n);\n\nexport { TypeScript };\n"
  },
  {
    "path": "apps/docs/src/components/logos/vscode.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst VSCode = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} fill=\"none\" viewBox=\"0 0 100 100\">\n    <g mask=\"url(#vscode__a)\">\n      <path\n        fill=\"#0065A9\"\n        d=\"M96.461 10.796 75.857.876a6.23 6.23 0 0 0-7.107 1.207l-67.451 61.5a4.167 4.167 0 0 0 .004 6.162l5.51 5.009a4.167 4.167 0 0 0 5.32.236l81.228-61.62c2.725-2.067 6.639-.124 6.639 3.297v-.24a6.25 6.25 0 0 0-3.539-5.63Z\"\n      />\n      <g filter=\"url(#vscode__b)\">\n        <path\n          fill=\"#007ACC\"\n          d=\"m96.461 89.204-20.604 9.92a6.229 6.229 0 0 1-7.107-1.207l-67.451-61.5a4.167 4.167 0 0 1 .004-6.162l5.51-5.009a4.167 4.167 0 0 1 5.32-.236l81.228 61.62c2.725 2.067 6.639.124 6.639-3.297v.24a6.25 6.25 0 0 1-3.539 5.63Z\"\n        />\n      </g>\n      <g filter=\"url(#vscode__c)\">\n        <path\n          fill=\"#1F9CF0\"\n          d=\"M75.858 99.126a6.232 6.232 0 0 1-7.108-1.21c2.306 2.307 6.25.674 6.25-2.588V4.672c0-3.262-3.944-4.895-6.25-2.589a6.232 6.232 0 0 1 7.108-1.21l20.6 9.908A6.25 6.25 0 0 1 100 16.413v67.174a6.25 6.25 0 0 1-3.541 5.633l-20.601 9.906Z\"\n        />\n      </g>\n      <path\n        fill=\"url(#vscode__d)\"\n        fillRule=\"evenodd\"\n        d=\"M70.851 99.317a6.224 6.224 0 0 0 4.96-.19L96.4 89.22a6.25 6.25 0 0 0 3.54-5.633V16.413a6.25 6.25 0 0 0-3.54-5.632L75.812.874a6.226 6.226 0 0 0-7.104 1.21L29.294 38.04 12.126 25.01a4.162 4.162 0 0 0-5.317.236l-5.507 5.009a4.168 4.168 0 0 0-.004 6.162L16.186 50 1.298 63.583a4.168 4.168 0 0 0 .004 6.162l5.507 5.009a4.162 4.162 0 0 0 5.317.236L29.294 61.96l39.414 35.958a6.218 6.218 0 0 0 2.143 1.4ZM74.954 27.3 45.048 50l29.906 22.701V27.3Z\"\n        clipRule=\"evenodd\"\n        opacity=\".25\"\n        style={{ mixBlendMode: \"overlay\" }}\n      />\n    </g>\n  </svg>\n);\n\nexport { VSCode };\n"
  },
  {
    "path": "apps/docs/src/components/logos/vue.tsx",
    "content": "import type { SVGProps } from \"react\";\n\nconst Vue = (props: SVGProps<SVGSVGElement>) => (\n  <svg {...props} viewBox=\"0 0 256 221\" preserveAspectRatio=\"xMidYMid\">\n    <path\n      d=\"M204.8 0H256L128 220.8 0 0h97.92L128 51.2 157.44 0h47.36Z\"\n      fill=\"#41B883\"\n    />\n    <path d=\"m0 0 128 220.8L256 0h-51.2L128 132.48 50.56 0H0Z\" fill=\"#41B883\" />\n    <path\n      d=\"M50.56 0 128 133.12 204.8 0h-47.36L128 51.2 97.92 0H50.56Z\"\n      fill=\"#35495E\"\n    />\n  </svg>\n);\n\nexport { Vue };\n"
  },
  {
    "path": "apps/docs/src/components/navigation-menu.tsx",
    "content": "'use client';\nimport * as React from 'react';\nimport * as Primitive from '@radix-ui/react-navigation-menu';\nimport { cn } from '../lib/cn';\n\nconst NavigationMenu = Primitive.Root;\n\nconst NavigationMenuList = Primitive.List;\n\nconst NavigationMenuItem = React.forwardRef<\n  React.ComponentRef<typeof Primitive.NavigationMenuItem>,\n  React.ComponentPropsWithoutRef<typeof Primitive.NavigationMenuItem>\n>(({ className, children, ...props }, ref) => (\n  <Primitive.NavigationMenuItem\n    ref={ref}\n    className={cn('list-none', className)}\n    {...props}\n  >\n    {children}\n  </Primitive.NavigationMenuItem>\n));\n\nNavigationMenuItem.displayName = Primitive.NavigationMenuItem.displayName;\n\nconst NavigationMenuTrigger = React.forwardRef<\n  React.ComponentRef<typeof Primitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof Primitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n  <Primitive.Trigger\n    ref={ref}\n    className={cn('data-[state=open]:bg-fd-accent/50', className)}\n    {...props}\n  >\n    {children}\n  </Primitive.Trigger>\n));\nNavigationMenuTrigger.displayName = Primitive.Trigger.displayName;\n\nconst NavigationMenuContent = React.forwardRef<\n  React.ComponentRef<typeof Primitive.Content>,\n  React.ComponentPropsWithoutRef<typeof Primitive.Content>\n>(({ className, ...props }, ref) => (\n  <Primitive.Content\n    ref={ref}\n    className={cn(\n      'absolute inset-x-0 top-0 overflow-auto fd-scroll-container max-h-[80svh] data-[motion=from-end]:animate-fd-enterFromRight data-[motion=from-start]:animate-fd-enterFromLeft data-[motion=to-end]:animate-fd-exitToRight data-[motion=to-start]:animate-fd-exitToLeft',\n      className,\n    )}\n    {...props}\n  />\n));\nNavigationMenuContent.displayName = Primitive.Content.displayName;\n\nconst NavigationMenuLink = Primitive.Link;\n\nconst NavigationMenuViewport = React.forwardRef<\n  React.ComponentRef<typeof Primitive.Viewport>,\n  React.ComponentPropsWithoutRef<typeof Primitive.Viewport>\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className=\"flex w-full justify-center\">\n    <Primitive.Viewport\n      {...props}\n      className={cn(\n        'relative h-(--radix-navigation-menu-viewport-height) w-full origin-[top_center] overflow-hidden transition-[width,height] duration-300 data-[state=closed]:animate-fd-nav-menu-out data-[state=open]:animate-fd-nav-menu-in',\n        className,\n      )}\n    />\n  </div>\n));\nNavigationMenuViewport.displayName = Primitive.Viewport.displayName;\n\nexport {\n  NavigationMenu,\n  NavigationMenuList,\n  NavigationMenuItem,\n  NavigationMenuContent,\n  NavigationMenuTrigger,\n  NavigationMenuLink,\n  NavigationMenuViewport,\n};\n"
  },
  {
    "path": "apps/docs/src/components/page-actions.tsx",
    "content": "'use client';\nimport { useMemo, useState } from 'react';\nimport {\n  Check,\n  ChevronDown,\n  Copy,\n  ExternalLinkIcon,\n  Loader2,\n  MessageCircleIcon,\n} from 'lucide-react';\nimport { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';\nimport { buttonVariants } from './ui/button';\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from 'fumadocs-ui/components/ui/popover';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\n\nconst cache = new Map<string, string>();\n\nexport function LLMCopyButton({\n  /**\n   * A URL to fetch the raw Markdown/MDX content of page\n   */\n  markdownUrl,\n}: {\n  markdownUrl: string;\n}) {\n  const [isLoading, setLoading] = useState(false);\n  const [checked, onClick] = useCopyButton(async () => {\n    const cached = cache.get(markdownUrl);\n    if (cached) return navigator.clipboard.writeText(cached);\n\n    setLoading(true);\n\n    try {\n      await navigator.clipboard.write([\n        new ClipboardItem({\n          'text/plain': fetch(markdownUrl).then(async (res) => {\n            const content = await res.text();\n            cache.set(markdownUrl, content);\n\n            return content;\n          }),\n        }),\n      ]);\n    } finally {\n      setLoading(false);\n    }\n  });\n\n  return (\n    <button\n      disabled={isLoading}\n      className={cn(\n        buttonVariants({\n          variant: 'secondary',\n          size: 'sm',\n          className: 'gap-2 [&_svg]:size-3.5 [&_svg]:text-fd-muted-foreground',\n        }),\n      )}\n      onClick={onClick}\n    >\n      {checked ? <Check /> : <Copy />}\n      Copy Markdown\n    </button>\n  );\n}\n\nconst optionVariants = cva(\n  'text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4',\n);\n\nexport function ViewOptions({\n  markdownUrl,\n  githubUrl,\n}: {\n  /**\n   * A URL to the raw Markdown/MDX content of page\n   */\n  markdownUrl: string;\n\n  /**\n   * Source file URL on GitHub\n   */\n  githubUrl: string;\n}) {\n  const items = useMemo(() => {\n    const fullMarkdownUrl =\n      typeof window !== 'undefined'\n        ? new URL(markdownUrl, window.location.origin)\n        : 'loading';\n    const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`;\n\n    return [\n      {\n        title: 'Open in GitHub',\n        href: githubUrl,\n        icon: (\n          <svg fill=\"currentColor\" role=\"img\" viewBox=\"0 0 24 24\">\n            <title>GitHub</title>\n            <path d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in Scira AI',\n        href: `https://scira.ai/?${new URLSearchParams({\n          q,\n        })}`,\n        icon: (\n          <svg\n            width=\"910\"\n            height=\"934\"\n            viewBox=\"0 0 910 934\"\n            fill=\"none\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <title>Scira AI</title>\n            <path\n              d=\"M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M516.774 304.217C510.299 275.491 498.208 252.087 480.335 234.214C462.462 216.341 439.058 204.251 410.333 197.775C439.059 191.3 462.462 179.209 480.335 161.336C498.208 143.463 510.299 120.06 516.774 91.334C523.25 120.059 535.34 143.463 553.213 161.336C571.086 179.209 594.49 191.3 623.216 197.775C594.49 204.251 571.086 216.341 553.213 234.214C535.34 252.087 523.25 275.491 516.774 304.217Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M857.5 508.116C763.259 497.644 710.903 445.288 700.432 351.047C689.961 445.288 637.605 497.644 543.364 508.116C637.605 518.587 689.961 570.943 700.432 665.184C710.903 570.943 763.259 518.587 857.5 508.116Z\"\n              stroke=\"currentColor\"\n              strokeWidth=\"20\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M700.432 615.957C691.848 589.05 678.575 566.357 660.383 548.165C642.191 529.973 619.499 516.7 592.593 508.116C619.499 499.533 642.191 486.258 660.383 468.066C678.575 449.874 691.848 427.181 700.432 400.274C709.015 427.181 722.289 449.874 740.481 468.066C758.673 486.258 781.365 499.533 808.271 508.116C781.365 516.7 758.673 529.973 740.481 548.165C722.289 566.357 709.015 589.05 700.432 615.957Z\"\n              stroke=\"currentColor\"\n              strokeWidth=\"20\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M889.949 121.237C831.049 114.692 798.326 81.9698 791.782 23.0692C785.237 81.9698 752.515 114.692 693.614 121.237C752.515 127.781 785.237 160.504 791.782 219.404C798.326 160.504 831.049 127.781 889.949 121.237Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M791.782 196.795C786.697 176.937 777.869 160.567 765.16 147.858C752.452 135.15 736.082 126.322 716.226 121.237C736.082 116.152 752.452 107.324 765.16 94.6152C777.869 81.9065 786.697 65.5368 791.782 45.6797C796.867 65.5367 805.695 81.9066 818.403 94.6152C831.112 107.324 847.481 116.152 867.338 121.237C847.481 126.322 831.112 135.15 818.403 147.858C805.694 160.567 796.867 176.937 791.782 196.795Z\"\n              fill=\"currentColor\"\n              stroke=\"currentColor\"\n              strokeWidth=\"8\"\n              strokeLinejoin=\"round\"\n            />\n            <path\n              d=\"M760.632 764.337C720.719 814.616 669.835 855.1 611.872 882.692C553.91 910.285 490.404 924.255 426.213 923.533C362.022 922.812 298.846 907.419 241.518 878.531C184.19 849.643 134.228 808.026 95.4548 756.863C56.6815 705.7 30.1238 646.346 17.8129 583.343C5.50207 520.339 7.76433 455.354 24.4266 393.359C41.089 331.364 71.7099 274.001 113.947 225.658C156.184 177.315 208.919 139.273 268.117 114.442\"\n              stroke=\"currentColor\"\n              strokeWidth=\"30\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in ChatGPT',\n        href: `https://chatgpt.com/?${new URLSearchParams({\n          hints: 'search',\n          q,\n        })}`,\n        icon: (\n          <svg\n            role=\"img\"\n            viewBox=\"0 0 24 24\"\n            fill=\"currentColor\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <title>OpenAI</title>\n            <path d=\"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z\" />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in Claude',\n        href: `https://claude.ai/new?${new URLSearchParams({\n          q,\n        })}`,\n        icon: (\n          <svg\n            fill=\"currentColor\"\n            role=\"img\"\n            viewBox=\"0 0 24 24\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <title>Anthropic</title>\n            <path d=\"M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z\" />\n          </svg>\n        ),\n      },\n      {\n        title: 'Open in T3 Chat',\n        href: `https://t3.chat/new?${new URLSearchParams({\n          q,\n        })}`,\n        icon: <MessageCircleIcon />,\n      },\n      {\n        title: 'Open in Finalchat',\n        href: `https://finalchat.app/chat?${new URLSearchParams({\n          q,\n        })}`,\n        icon: (\n          <svg\n            fill=\"currentColor\"\n            role=\"img\"\n            viewBox=\"0 -47.5 464 464\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            xmlSpace=\"preserve\"\n          >\n            <title>Finalchat</title>\n            <g transform=\"matrix(1,0,0,1,-744.674384,-723.689943)\">\n              <g transform=\"matrix(0.990844,-0.130447,0.140819,1.069625,308.164017,323.729868)\">\n                <path\n                  d=\"M770.129,607.794C774.18,607.794 777.966,609.661 780.228,612.775C782.489,615.89 782.93,619.842 781.402,623.319C773.191,642.009 760.491,670.918 760.491,670.918C756.73,679.479 747.954,685.245 737.989,685.703L537.829,694.901C534.462,695.055 531.226,696.158 528.553,698.062L456.554,749.342C453.254,751.693 449.308,753.138 445.164,753.514L356.235,761.579C348.198,762.308 340.518,758.334 336.973,751.613C333.428,744.891 334.764,736.837 340.324,731.412L379.001,693.674C380.018,692.681 380.936,691.605 381.744,690.459L527.505,483.649C531.748,477.629 538.985,474 546.747,474L819.359,474C825.548,474 831.332,476.853 834.787,481.611C838.241,486.368 838.914,492.406 836.581,497.717C836.581,497.717 825.908,522.01 819.369,536.897C816.363,543.739 809.183,548.213 801.209,548.213C761.203,548.213 643.945,548.213 643.945,548.213C640.897,548.213 638.014,549.495 636.108,551.697L593.802,600.589C592.651,601.919 592.425,603.743 593.222,605.279C594.018,606.816 595.695,607.794 597.535,607.794L770.129,607.794Z\"\n                  fillRule=\"evenodd\"\n                  clipRule=\"evenodd\"\n                  strokeLinejoin=\"round\"\n                  strokeMiterlimit={2}\n                />\n              </g>\n            </g>\n          </svg>\n        ),\n      }\n    ];\n  }, [githubUrl, markdownUrl]);\n\n  return (\n    <Popover>\n      <PopoverTrigger\n        className={cn(\n          buttonVariants({\n            variant: 'secondary',\n            size: 'sm',\n            className: 'gap-2',\n          }),\n        )}\n      >\n        Open\n        <ChevronDown className=\"size-3.5 text-fd-muted-foreground\" />\n      </PopoverTrigger>\n      <PopoverContent className=\"flex flex-col overflow-auto\" align=\"end\">\n        {items.map((item) => (\n          <a\n            key={item.href}\n            href={item.href}\n            rel=\"noreferrer noopener\"\n            target=\"_blank\"\n            className={cn(optionVariants())}\n          >\n            {item.icon}\n            {item.title}\n            <ExternalLinkIcon className=\"text-fd-muted-foreground size-3.5 ms-auto\" />\n          </a>\n        ))}\n      </PopoverContent>\n    </Popover>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/registry-index.tsx",
    "content": "\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { z } from \"zod\";\nimport { InputGroup, InputGroupAddon, InputGroupInput } from \"./ui/input-group\";\nimport { CheckIcon, CopyIcon, SearchIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport fuzzysort from \"fuzzysort\";\nimport { Item, ItemActions, ItemContent } from \"./ui/item\";\nimport { Button } from \"./ui\";\nimport { useCopyToClipboard } from \"@/hooks/use-copy-to-clipboard\";\n\nconst RegistryIndexEntrySchema = z.object({\n\tname: z.string(),\n\thomepage: z.string(),\n\turl: z.string(),\n\tdescription: z.string(),\n\tlogo: z.string(),\n});\n\ntype RegistryIndexEntry = z.infer<typeof RegistryIndexEntrySchema>;\n\nexport function RegistryDirectory() {\n\tconst [search, setSearch] = useState(\"\");\n\tconst query = useQuery({\n\t\tqueryKey: [\"registry-index\"],\n\t\tqueryFn: async () => {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\t\"https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/directory.json\"\n\t\t\t\t);\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\n\t\t\t\treturn (await response.json()) as RegistryIndexEntry[];\n\t\t\t} catch {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\t\tstaleTime: 1000 * 60 * 60 * 6, // 6 hours\n\t\trefetchOnMount: false,\n\t\trefetchOnWindowFocus: false,\n\t});\n\n\tconst filteredItems =\n\t\tsearch.trim().length > 0\n\t\t\t? fuzzysort\n\t\t\t\t\t.go(search, query.data ?? [], {\n\t\t\t\t\t\tkeys: [\"name\", \"description\"],\n\t\t\t\t\t})\n\t\t\t\t\t.map((res) => res.obj)\n\t\t\t: query.data ?? [];\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t<InputGroup>\n\t\t\t\t<InputGroupAddon>\n\t\t\t\t\t<SearchIcon />\n\t\t\t\t</InputGroupAddon>\n\t\t\t\t<InputGroupInput placeholder=\"Search...\" value={search} onChange={(e) => setSearch(e.target.value)} />\n\t\t\t\t{search.trim().length > 0 && (\n\t\t\t\t\t<InputGroupAddon align=\"inline-end\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{filteredItems.length} {filteredItems.length === 1 ? \"result\" : \"results\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</InputGroupAddon>\n\t\t\t\t)}\n\t\t\t</InputGroup>\n\t\t\t<div className=\"flex flex-col gap-2 not-prose\">\n\t\t\t\t{filteredItems.map((registry) => (\n\t\t\t\t\t<Registry key={registry.name} registry={registry} />\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction Registry({ registry }: { registry: RegistryIndexEntry }) {\n\tconst [copyInit, initCopied] = useCopyToClipboard(1500);\n\treturn (\n\t\t<Item className=\"bg-card border border-border\">\n\t\t\t<ItemContent>\n\t\t\t\t<div className=\"flex place-items-center gap-3\">\n\t\t\t\t\t<div className=\"size-8 overflow-hidden rounded-md\">\n\t\t\t\t\t\t{registry.logo && (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"[&_svg]:size-8 fill-current\"\n\t\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: registry.logo }}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<h3 className=\"text-2xl font-medium\">{registry.name}</h3>\n\t\t\t\t</div>\n\t\t\t\t<p className=\"text-muted-foreground\">{registry.description}</p>\n\t\t\t</ItemContent>\n\t\t\t<ItemActions>\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => copyInit(`jsrepo init shadcn:${registry.name}`)}>\n\t\t\t\t\t{initCopied ? <CheckIcon /> : <CopyIcon />}\n\t\t\t\t\tCopy Init\n\t\t\t\t</Button>\n\t\t\t</ItemActions>\n\t\t</Item>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/components/registry-kit/demo-example.tsx",
    "content": "import { Demo, DemoCode, DemoPreview } from \"./demo\";\n\nexport function DemoExample() {\n\treturn (\n\t\t<Demo>\n\t\t\t<DemoPreview>\n\t\t\t\t<div>Hello</div>\n\t\t\t</DemoPreview>\n\t\t\t<DemoCode>\n\t\t\t\t<div>Hello</div>\n\t\t\t</DemoCode>\n\t\t</Demo>\n\t);\n}"
  },
  {
    "path": "apps/docs/src/components/registry-kit/demo.tsx",
    "content": "import { Tabs, TabsList, TabsTrigger, TabsContent } from \"@/components/ui/tabs\";\nimport { ComponentProps, useState } from \"react\";\n\n// a new comment\nexport function Demo({ className, ...props }: ComponentProps<typeof Tabs>): React.ReactElement {\n\tconst [tab, setTab] = useState(\"preview\");\n\n\treturn (\n\t\t<Tabs value={tab} onValueChange={setTab} className={className} {...props}>\n\t\t\t<TabsList>\n\t\t\t\t<TabsTrigger value=\"preview\">Preview</TabsTrigger>\n\t\t\t\t<TabsTrigger value=\"code\">Code</TabsTrigger>\n\t\t\t</TabsList>\n            {props.children}\n\t\t</Tabs>\n\t);\n}\n\nexport function DemoPreview({ className, ...props }: Omit<ComponentProps<typeof TabsContent>, 'value'>): React.ReactElement {\n\treturn (\n\t\t<TabsContent value=\"preview\" className={className} {...props}>\n\t\t\t{props.children}\n\t\t</TabsContent>\n\t);\n}\n\nexport function DemoCode({ className, ...props }: Omit<ComponentProps<typeof TabsContent>, 'value'>): React.ReactElement {\n\treturn (\n\t\t<TabsContent value=\"code\" className={className} {...props}>\n\t\t\t{props.children}\n\t\t</TabsContent>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/components/search-toggle.tsx",
    "content": "'use client';\nimport type { ComponentProps } from 'react';\nimport { Search } from 'lucide-react';\nimport { useSearchContext } from 'fumadocs-ui/contexts/search';\nimport { useI18n } from 'fumadocs-ui/contexts/i18n';\nimport { cn } from '../lib/cn';\nimport { type ButtonProps, buttonVariants } from './ui/button';\n\ninterface SearchToggleProps\n  extends Omit<ComponentProps<'button'>, 'color'>,\n    ButtonProps {\n  hideIfDisabled?: boolean;\n}\n\nexport function SearchToggle({\n  hideIfDisabled,\n  size = 'icon-sm',\n  variant = 'ghost',\n  ...props\n}: SearchToggleProps) {\n  const { setOpenSearch, enabled } = useSearchContext();\n  if (hideIfDisabled && !enabled) return null;\n\n  return (\n    <button\n      type=\"button\"\n      className={cn(\n        buttonVariants({\n          size,\n          variant,\n        }),\n        props.className,\n      )}\n      data-search=\"\"\n      aria-label=\"Open Search\"\n      onClick={() => {\n        setOpenSearch(true);\n      }}\n    >\n      <Search />\n    </button>\n  );\n}\n\nexport function LargeSearchToggle({\n  hideIfDisabled,\n  ...props\n}: ComponentProps<'button'> & {\n  hideIfDisabled?: boolean;\n}) {\n  const { enabled, hotKey, setOpenSearch } = useSearchContext();\n  const { text } = useI18n();\n  if (hideIfDisabled && !enabled) return null;\n\n  return (\n    <button\n      type=\"button\"\n      data-search-full=\"\"\n      {...props}\n      className={cn(\n        'inline-flex items-center gap-2 rounded-lg border bg-fd-secondary/50 p-1.5 ps-2 text-sm text-fd-muted-foreground transition-colors hover:bg-fd-accent hover:text-fd-accent-foreground',\n        props.className,\n      )}\n      onClick={() => {\n        setOpenSearch(true);\n      }}\n    >\n      <Search className=\"size-4\" />\n      {text.search}\n      <div className=\"ms-auto inline-flex gap-0.5\">\n        {hotKey.map((k, i) => (\n          <kbd key={i} className=\"rounded-md border bg-fd-background px-1.5\">\n            {k.display}\n          </kbd>\n        ))}\n      </div>\n    </button>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/sidebar.tsx",
    "content": "'use client';\nimport { ChevronDown, ExternalLink } from 'lucide-react';\nimport { usePathname } from 'fumadocs-core/framework';\nimport {\n  type ComponentProps,\n  createContext,\n  type FC,\n  Fragment,\n  type ReactNode,\n  useContext,\n  useMemo,\n  useRef,\n  useState,\n} from 'react';\nimport Link, { type LinkProps } from 'fumadocs-core/link';\nimport { useOnChange } from 'fumadocs-core/utils/use-on-change';\nimport { cn } from '../lib/cn';\nimport { ScrollArea, ScrollViewport } from './ui/scroll-area';\nimport { isActive } from '../lib/is-active';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from './ui/collapsible';\nimport { type ScrollAreaProps } from '@radix-ui/react-scroll-area';\nimport { useSidebar } from 'fumadocs-ui/contexts/sidebar';\nimport { cva } from 'class-variance-authority';\nimport type {\n  CollapsibleContentProps,\n  CollapsibleTriggerProps,\n} from '@radix-ui/react-collapsible';\nimport type * as PageTree from 'fumadocs-core/page-tree';\nimport { useTreeContext, useTreePath } from 'fumadocs-ui/contexts/tree';\nimport { useMediaQuery } from 'fumadocs-core/utils/use-media-query';\nimport { Presence } from '@radix-ui/react-presence';\n\nexport interface SidebarProps {\n  /**\n   * Open folders by default if their level is lower or equal to a specific level\n   * (Starting from 1)\n   *\n   * @defaultValue 0\n   */\n  defaultOpenLevel?: number;\n\n  /**\n   * Prefetch links\n   *\n   * @defaultValue true\n   */\n  prefetch?: boolean;\n\n  /**\n   * Children to render\n   */\n  Content: ReactNode;\n\n  /**\n   * Alternative children for mobile\n   */\n  Mobile?: ReactNode;\n}\n\ninterface InternalContext {\n  defaultOpenLevel: number;\n  prefetch: boolean;\n  level: number;\n}\n\nconst itemVariants = cva(\n  'relative flex flex-row items-center gap-2 rounded-lg p-2 ps-(--sidebar-item-offset) text-start text-fd-muted-foreground [overflow-wrap:anywhere] [&_svg]:size-4 [&_svg]:shrink-0',\n  {\n    variants: {\n      active: {\n        true: 'bg-fd-primary/10 text-fd-primary',\n        false:\n          'transition-colors hover:bg-fd-accent/50 hover:text-fd-accent-foreground/80 hover:transition-none',\n      },\n    },\n  },\n);\n\nconst Context = createContext<InternalContext | null>(null);\nconst FolderContext = createContext<{\n  open: boolean;\n  setOpen: React.Dispatch<React.SetStateAction<boolean>>;\n} | null>(null);\n\nexport function Sidebar({\n  defaultOpenLevel = 0,\n  prefetch = true,\n  Mobile,\n  Content,\n}: SidebarProps) {\n  const isMobile = useMediaQuery('(width < 768px)') ?? false;\n  const context = useMemo<InternalContext>(() => {\n    return {\n      defaultOpenLevel,\n      prefetch,\n      level: 1,\n    };\n  }, [defaultOpenLevel, prefetch]);\n\n  return (\n    <Context.Provider value={context}>\n      {isMobile && Mobile != null ? Mobile : Content}\n    </Context.Provider>\n  );\n}\n\nexport function SidebarContent(props: ComponentProps<'aside'>) {\n  const { collapsed } = useSidebar();\n  const [hover, setHover] = useState(false);\n  const timerRef = useRef(0);\n  const closeTimeRef = useRef(0);\n\n  useOnChange(collapsed, () => {\n    setHover(false);\n    closeTimeRef.current = Date.now() + 150;\n  });\n\n  return (\n    <aside\n      id=\"nd-sidebar\"\n      {...props}\n      data-collapsed={collapsed}\n      className={cn(\n        'fixed left-0 rtl:left-auto rtl:right-(--removed-body-scroll-bar-size,0) flex flex-col items-end top-(--fd-sidebar-top) bottom-(--fd-sidebar-margin) z-20 bg-fd-card text-sm border-e transition-[top,opacity,translate,width] duration-200 max-md:hidden *:w-(--fd-sidebar-width)',\n        collapsed && [\n          'rounded-xl border translate-x-(--fd-sidebar-offset) rtl:-translate-x-(--fd-sidebar-offset)',\n          hover ? 'z-50 shadow-lg' : 'opacity-0',\n        ],\n        props.className,\n      )}\n      style={\n        {\n          ...props.style,\n          '--fd-sidebar-offset': hover\n            ? 'calc(var(--spacing) * 2)'\n            : 'calc(16px - 100%)',\n          '--fd-sidebar-margin': collapsed ? '0.5rem' : '0px',\n          '--fd-sidebar-top': `calc(var(--fd-banner-height) + var(--fd-nav-height) + var(--fd-sidebar-margin))`,\n          width: collapsed\n            ? 'var(--fd-sidebar-width)'\n            : 'calc(var(--spacing) + var(--fd-sidebar-width) + var(--fd-layout-offset))',\n        } as object\n      }\n      onPointerEnter={(e) => {\n        if (\n          !collapsed ||\n          e.pointerType === 'touch' ||\n          closeTimeRef.current > Date.now()\n        )\n          return;\n        window.clearTimeout(timerRef.current);\n        setHover(true);\n      }}\n      onPointerLeave={(e) => {\n        if (!collapsed || e.pointerType === 'touch') return;\n        window.clearTimeout(timerRef.current);\n\n        timerRef.current = window.setTimeout(\n          () => {\n            setHover(false);\n            closeTimeRef.current = Date.now() + 150;\n          },\n          Math.min(e.clientX, document.body.clientWidth - e.clientX) > 100\n            ? 0\n            : 500,\n        );\n      }}\n    >\n      {props.children}\n    </aside>\n  );\n}\n\nexport function SidebarContentMobile({\n  className,\n  children,\n  ...props\n}: ComponentProps<'aside'>) {\n  const { open, setOpen } = useSidebar();\n  const state = open ? 'open' : 'closed';\n\n  return (\n    <>\n      <Presence present={open}>\n        <div\n          data-state={state}\n          className=\"fixed z-40 inset-0 backdrop-blur-xs data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out\"\n          onClick={() => setOpen(false)}\n        />\n      </Presence>\n      <Presence present={open}>\n        {({ present }) => (\n          <aside\n            id=\"nd-sidebar-mobile\"\n            {...props}\n            data-state={state}\n            className={cn(\n              'fixed text-[0.9375rem] flex flex-col shadow-lg border-s end-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out',\n              !present && 'invisible',\n              className,\n            )}\n          >\n            {children}\n          </aside>\n        )}\n      </Presence>\n    </>\n  );\n}\n\nexport function SidebarHeader(props: ComponentProps<'div'>) {\n  return (\n    <div\n      {...props}\n      className={cn('flex flex-col gap-3 p-4 pb-2', props.className)}\n    >\n      {props.children}\n    </div>\n  );\n}\n\nexport function SidebarFooter(props: ComponentProps<'div'>) {\n  return (\n    <div\n      {...props}\n      className={cn('flex flex-col border-t p-4 pt-2', props.className)}\n    >\n      {props.children}\n    </div>\n  );\n}\n\nexport function SidebarViewport(props: ScrollAreaProps) {\n  return (\n    <ScrollArea {...props} className={cn('h-full', props.className)}>\n      <ScrollViewport\n        className=\"p-4 overscroll-contain\"\n        style={\n          {\n            '--sidebar-item-offset': 'calc(var(--spacing) * 2)',\n            maskImage:\n              'linear-gradient(to bottom, transparent, white 12px, white calc(100% - 12px), transparent)',\n          } as object\n        }\n      >\n        {props.children}\n      </ScrollViewport>\n    </ScrollArea>\n  );\n}\n\nexport function SidebarSeparator(props: ComponentProps<'p'>) {\n  return (\n    <p\n      {...props}\n      className={cn(\n        'inline-flex items-center gap-2 mb-1.5 px-2 ps-(--sidebar-item-offset) empty:mb-0 [&_svg]:size-4 [&_svg]:shrink-0',\n        props.className,\n      )}\n    >\n      {props.children}\n    </p>\n  );\n}\n\nexport function SidebarItem({\n  icon,\n  ...props\n}: LinkProps & {\n  icon?: ReactNode;\n}) {\n  const pathname = usePathname();\n  const active =\n    props.href !== undefined && isActive(props.href, pathname, false);\n  const { prefetch } = useInternalContext();\n\n  return (\n    <Link\n      {...props}\n      data-active={active}\n      className={cn(itemVariants({ active }), props.className)}\n      prefetch={prefetch}\n    >\n      {icon ?? (props.external ? <ExternalLink /> : null)}\n      {props.children}\n    </Link>\n  );\n}\n\nexport function SidebarFolder({\n  defaultOpen = false,\n  ...props\n}: ComponentProps<'div'> & {\n  defaultOpen?: boolean;\n}) {\n  const [open, setOpen] = useState(defaultOpen);\n\n  useOnChange(defaultOpen, (v) => {\n    if (v) setOpen(v);\n  });\n\n  return (\n    <Collapsible open={open} onOpenChange={setOpen} {...props}>\n      <FolderContext.Provider\n        value={useMemo(() => ({ open, setOpen }), [open])}\n      >\n        {props.children}\n      </FolderContext.Provider>\n    </Collapsible>\n  );\n}\n\nexport function SidebarFolderTrigger({\n  className,\n  ...props\n}: CollapsibleTriggerProps) {\n  const { open } = useFolderContext();\n\n  return (\n    <CollapsibleTrigger\n      className={cn(itemVariants({ active: false }), 'w-full', className)}\n      {...props}\n    >\n      {props.children}\n      <ChevronDown\n        data-icon\n        className={cn('ms-auto transition-transform', !open && '-rotate-90')}\n      />\n    </CollapsibleTrigger>\n  );\n}\n\nexport function SidebarFolderLink(props: LinkProps) {\n  const { open, setOpen } = useFolderContext();\n  const { prefetch } = useInternalContext();\n\n  const pathname = usePathname();\n  const active =\n    props.href !== undefined && isActive(props.href, pathname, false);\n\n  return (\n    <Link\n      {...props}\n      data-active={active}\n      className={cn(itemVariants({ active }), 'w-full', props.className)}\n      onClick={(e) => {\n        if (\n          e.target instanceof Element &&\n          e.target.matches('[data-icon], [data-icon] *')\n        ) {\n          setOpen(!open);\n          e.preventDefault();\n        } else {\n          setOpen(active ? !open : true);\n        }\n      }}\n      prefetch={prefetch}\n    >\n      {props.children}\n      <ChevronDown\n        data-icon\n        className={cn('ms-auto transition-transform', !open && '-rotate-90')}\n      />\n    </Link>\n  );\n}\n\nexport function SidebarFolderContent(props: CollapsibleContentProps) {\n  const { level, ...ctx } = useInternalContext();\n\n  return (\n    <CollapsibleContent\n      {...props}\n      className={cn(\n        'relative',\n        level === 1 && [\n          \"before:content-[''] before:absolute before:w-px before:inset-y-1 before:bg-fd-border before:start-2.5\",\n          \"**:data-[active=true]:before:content-[''] **:data-[active=true]:before:bg-fd-primary **:data-[active=true]:before:absolute **:data-[active=true]:before:w-px **:data-[active=true]:before:inset-y-2.5 **:data-[active=true]:before:start-2.5\",\n        ],\n        props.className,\n      )}\n      style={\n        {\n          '--sidebar-item-offset': `calc(var(--spacing) * ${(level + 1) * 3})`,\n          ...props.style,\n        } as object\n      }\n    >\n      <Context.Provider\n        value={useMemo(\n          () => ({\n            ...ctx,\n            level: level + 1,\n          }),\n          [ctx, level],\n        )}\n      >\n        {props.children}\n      </Context.Provider>\n    </CollapsibleContent>\n  );\n}\n\nexport function SidebarTrigger({\n  children,\n  ...props\n}: ComponentProps<'button'>) {\n  const { setOpen } = useSidebar();\n\n  return (\n    <button\n      {...props}\n      aria-label=\"Open Sidebar\"\n      onClick={() => setOpen((prev) => !prev)}\n    >\n      {children}\n    </button>\n  );\n}\n\nexport function SidebarCollapseTrigger(props: ComponentProps<'button'>) {\n  const { collapsed, setCollapsed } = useSidebar();\n\n  return (\n    <button\n      type=\"button\"\n      aria-label=\"Collapse Sidebar\"\n      data-collapsed={collapsed}\n      {...props}\n      onClick={() => {\n        setCollapsed((prev) => !prev);\n      }}\n    >\n      {props.children}\n    </button>\n  );\n}\n\nfunction useFolderContext() {\n  const ctx = useContext(FolderContext);\n  if (!ctx) throw new Error('Missing sidebar folder');\n\n  return ctx;\n}\n\nfunction useInternalContext() {\n  const ctx = useContext(Context);\n  if (!ctx) throw new Error('<Sidebar /> component required.');\n\n  return ctx;\n}\n\nexport interface SidebarComponents {\n  Item: FC<{ item: PageTree.Item }>;\n  Folder: FC<{ item: PageTree.Folder; level: number; children: ReactNode }>;\n  Separator: FC<{ item: PageTree.Separator }>;\n}\n\n/**\n * Render sidebar items from page tree\n */\nexport function SidebarPageTree(props: {\n  components?: Partial<SidebarComponents>;\n}) {\n  const { root } = useTreeContext();\n\n  return useMemo(() => {\n    const { Separator, Item, Folder } = props.components ?? {};\n\n    function renderSidebarList(\n      items: PageTree.Node[],\n      level: number,\n    ): ReactNode[] {\n      return items.map((item, i) => {\n        if (item.type === 'separator') {\n          if (Separator) return <Separator key={i} item={item} />;\n          return (\n            <SidebarSeparator key={i} className={cn(i !== 0 && 'mt-6')}>\n              {item.icon}\n              {item.name}\n            </SidebarSeparator>\n          );\n        }\n\n        if (item.type === 'folder') {\n          const children = renderSidebarList(item.children, level + 1);\n\n          if (Folder)\n            return (\n              <Folder key={i} item={item} level={level}>\n                {children}\n              </Folder>\n            );\n          return (\n            <PageTreeFolder key={i} item={item}>\n              {children}\n            </PageTreeFolder>\n          );\n        }\n\n        if (Item) return <Item key={item.url} item={item} />;\n        return (\n          <SidebarItem\n            key={item.url}\n            href={item.url}\n            external={item.external}\n            icon={item.icon}\n          >\n            {item.name}\n          </SidebarItem>\n        );\n      });\n    }\n\n    return (\n      <Fragment key={root.$id}>{renderSidebarList(root.children, 1)}</Fragment>\n    );\n  }, [props.components, root]);\n}\n\nfunction PageTreeFolder({\n  item,\n  ...props\n}: {\n  item: PageTree.Folder;\n  children: ReactNode;\n}) {\n  const { defaultOpenLevel, level } = useInternalContext();\n  const path = useTreePath();\n\n  return (\n    <SidebarFolder\n      defaultOpen={\n        (item.defaultOpen ?? defaultOpenLevel >= level) || path.includes(item)\n      }\n    >\n      {item.index ? (\n        <SidebarFolderLink\n          href={item.index.url}\n          external={item.index.external}\n          {...props}\n        >\n          {item.icon}\n          {item.name}\n        </SidebarFolderLink>\n      ) : (\n        <SidebarFolderTrigger {...props}>\n          {item.icon}\n          {item.name}\n        </SidebarFolderTrigger>\n      )}\n      <SidebarFolderContent>{props.children}</SidebarFolderContent>\n    </SidebarFolder>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/theme-toggle.tsx",
    "content": "'use client';\nimport { cva } from 'class-variance-authority';\nimport { Moon, Sun, Airplay } from 'lucide-react';\nimport { useTheme } from 'next-themes';\nimport { type HTMLAttributes, useLayoutEffect, useState } from 'react';\nimport { cn } from '../lib/cn';\n\nconst itemVariants = cva(\n  'size-6.5 rounded-full p-1.5 text-fd-muted-foreground',\n  {\n    variants: {\n      active: {\n        true: 'bg-fd-accent text-fd-accent-foreground',\n        false: 'text-fd-muted-foreground',\n      },\n    },\n  },\n);\n\nconst full = [\n  ['light', Sun] as const,\n  ['dark', Moon] as const,\n  ['system', Airplay] as const,\n];\n\nexport function ThemeToggle({\n  className,\n  mode = 'light-dark',\n  ...props\n}: HTMLAttributes<HTMLElement> & {\n  mode?: 'light-dark' | 'light-dark-system';\n}) {\n  const { setTheme, theme, resolvedTheme } = useTheme();\n  const [mounted, setMounted] = useState(false);\n\n  useLayoutEffect(() => {\n    setMounted(true);\n  }, []);\n\n  const container = cn(\n    'inline-flex items-center rounded-full border p-1',\n    className,\n  );\n\n  if (mode === 'light-dark') {\n    const value = mounted ? resolvedTheme : null;\n\n    return (\n      <button\n        className={container}\n        aria-label={`Toggle Theme`}\n        onClick={() => setTheme(value === 'light' ? 'dark' : 'light')}\n        data-theme-toggle=\"\"\n        {...props}\n      >\n        {full.map(([key, Icon]) => {\n          if (key === 'system') return;\n\n          return (\n            <Icon\n              key={key}\n              fill=\"currentColor\"\n              className={cn(itemVariants({ active: value === key }))}\n            />\n          );\n        })}\n      </button>\n    );\n  }\n\n  const value = mounted ? theme : null;\n\n  return (\n    <div className={container} data-theme-toggle=\"\" {...props}>\n      {full.map(([key, Icon]) => (\n        <button\n          key={key}\n          aria-label={key}\n          className={cn(itemVariants({ active: value === key }))}\n          onClick={() => setTheme(key)}\n        >\n          <Icon className=\"size-full\" fill=\"currentColor\" />\n        </button>\n      ))}\n    </div>\n  );\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/accordion.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Accordion({\n  className,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Root>) {\n  return <AccordionPrimitive.Root data-slot=\"accordion\" className={cn(\"not-prose\", className)} {...props} />\n}\n\nfunction AccordionItem({\n  className,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Item>) {\n  return (\n    <AccordionPrimitive.Item\n      data-slot=\"accordion-item\"\n      className={cn(\"border-b last:border-b-0\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction AccordionTrigger({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {\n  return (\n    <AccordionPrimitive.Header className=\"flex\">\n      <AccordionPrimitive.Trigger\n        data-slot=\"accordion-trigger\"\n        className={cn(\n          \"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n        <ChevronDownIcon className=\"text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200\" />\n      </AccordionPrimitive.Trigger>\n    </AccordionPrimitive.Header>\n  )\n}\n\nfunction AccordionContent({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Content>) {\n  return (\n    <AccordionPrimitive.Content\n      data-slot=\"accordion-content\"\n      className=\"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm\"\n      {...props}\n    >\n      <div className={cn(\"pt-0 pb-4\", className)}>{children}</div>\n    </AccordionPrimitive.Content>\n  )\n}\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }\n"
  },
  {
    "path": "apps/docs/src/components/ui/animated-beam.tsx",
    "content": "\"use client\"\n\nimport { RefObject, useEffect, useId, useState } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface AnimatedBeamProps {\n  className?: string\n  containerRef: RefObject<HTMLElement | null> // Container ref\n  fromRef: RefObject<HTMLElement | null>\n  toRef: RefObject<HTMLElement | null>\n  curvature?: number\n  reverse?: boolean\n  pathColor?: string\n  pathWidth?: number\n  pathOpacity?: number\n  gradientStartColor?: string\n  gradientStopColor?: string\n  delay?: number\n  duration?: number\n  startXOffset?: number\n  startYOffset?: number\n  endXOffset?: number\n  endYOffset?: number\n}\n\nexport const AnimatedBeam: React.FC<AnimatedBeamProps> = ({\n  className,\n  containerRef,\n  fromRef,\n  toRef,\n  curvature = 0,\n  reverse = false, // Include the reverse prop\n  duration = Math.random() * 3 + 4,\n  delay = 0,\n  pathColor = \"gray\",\n  pathWidth = 2,\n  pathOpacity = 0.2,\n  gradientStartColor = \"#ffaa40\",\n  gradientStopColor = \"#9c40ff\",\n  startXOffset = 0,\n  startYOffset = 0,\n  endXOffset = 0,\n  endYOffset = 0,\n}) => {\n  const id = useId()\n  const [pathD, setPathD] = useState(\"\")\n  const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 })\n\n  // Calculate the gradient coordinates based on the reverse prop\n  const gradientCoordinates = reverse\n    ? {\n        x1: [\"90%\", \"-10%\"],\n        x2: [\"100%\", \"0%\"],\n        y1: [\"0%\", \"0%\"],\n        y2: [\"0%\", \"0%\"],\n      }\n    : {\n        x1: [\"10%\", \"110%\"],\n        x2: [\"0%\", \"100%\"],\n        y1: [\"0%\", \"0%\"],\n        y2: [\"0%\", \"0%\"],\n      }\n\n  useEffect(() => {\n    const updatePath = () => {\n      if (containerRef.current && fromRef.current && toRef.current) {\n        const containerRect = containerRef.current.getBoundingClientRect()\n        const rectA = fromRef.current.getBoundingClientRect()\n        const rectB = toRef.current.getBoundingClientRect()\n\n        const svgWidth = containerRect.width\n        const svgHeight = containerRect.height\n        setSvgDimensions({ width: svgWidth, height: svgHeight })\n\n        const startX =\n          rectA.left - containerRect.left + rectA.width / 2 + startXOffset\n        const startY =\n          rectA.top - containerRect.top + rectA.height / 2 + startYOffset\n        const endX =\n          rectB.left - containerRect.left + rectB.width / 2 + endXOffset\n        const endY =\n          rectB.top - containerRect.top + rectB.height / 2 + endYOffset\n\n        const controlY = startY - curvature\n        const d = `M ${startX},${startY} Q ${\n          (startX + endX) / 2\n        },${controlY} ${endX},${endY}`\n        setPathD(d)\n      }\n    }\n\n    // Initialize ResizeObserver\n    const resizeObserver = new ResizeObserver(() => {\n      updatePath()\n    })\n\n    // Observe the container element\n    if (containerRef.current) {\n      resizeObserver.observe(containerRef.current)\n    }\n\n    // Call the updatePath initially to set the initial path\n    updatePath()\n\n    // Clean up the observer on component unmount\n    return () => {\n      resizeObserver.disconnect()\n    }\n  }, [\n    containerRef,\n    fromRef,\n    toRef,\n    curvature,\n    startXOffset,\n    startYOffset,\n    endXOffset,\n    endYOffset,\n  ])\n\n  return (\n    <svg\n      fill=\"none\"\n      width={svgDimensions.width}\n      height={svgDimensions.height}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={cn(\n        \"pointer-events-none absolute top-0 left-0 transform-gpu stroke-2\",\n        className\n      )}\n      viewBox={`0 0 ${svgDimensions.width} ${svgDimensions.height}`}\n    >\n      <path\n        d={pathD}\n        stroke={pathColor}\n        strokeWidth={pathWidth}\n        strokeOpacity={pathOpacity}\n        strokeLinecap=\"round\"\n      />\n      <path\n        d={pathD}\n        strokeWidth={pathWidth}\n        stroke={`url(#${id})`}\n        strokeOpacity=\"1\"\n        strokeLinecap=\"round\"\n      />\n      <defs>\n        <motion.linearGradient\n          className=\"transform-gpu\"\n          id={id}\n          gradientUnits={\"userSpaceOnUse\"}\n          initial={{\n            x1: \"0%\",\n            x2: \"0%\",\n            y1: \"0%\",\n            y2: \"0%\",\n          }}\n          animate={{\n            x1: gradientCoordinates.x1,\n            x2: gradientCoordinates.x2,\n            y1: gradientCoordinates.y1,\n            y2: gradientCoordinates.y2,\n          }}\n          transition={{\n            delay,\n            duration,\n            ease: [0.16, 1, 0.3, 1], // https://easings.net/#easeOutExpo\n            repeat: Infinity,\n            repeatDelay: 0,\n          }}\n        >\n          <stop stopColor={gradientStartColor} stopOpacity=\"0\"></stop>\n          <stop stopColor={gradientStartColor}></stop>\n          <stop offset=\"32.5%\" stopColor={gradientStopColor}></stop>\n          <stop\n            offset=\"100%\"\n            stopColor={gradientStopColor}\n            stopOpacity=\"0\"\n          ></stop>\n        </motion.linearGradient>\n      </defs>\n    </svg>\n  )\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/badge.tsx",
    "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { BadgeCheckIcon, CodeIcon, Download, SettingsIcon } from \"lucide-react\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"./tooltip\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useQuery } from \"@tanstack/react-query\";\n\nconst badgeVariants = cva(\n\t\"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden\",\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n\t\t\t\tsecondary: \"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n\t\t\t\toutline: \"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t},\n\t}\n);\n\nfunction Badge({\n\tclassName,\n\tvariant,\n\tasChild = false,\n\t...props\n}: React.ComponentProps<\"span\"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n\tconst Comp = asChild ? Slot : \"span\";\n\n\treturn <Comp data-slot=\"badge\" className={cn(badgeVariants({ variant }), className)} {...props} />;\n}\n\nfunction SourceBadge({\n\tpath,\n\tclassName,\n\t...props\n}: Omit<React.ComponentProps<typeof Badge>, \"variant\"> & {\n\t/**\n\t * The path to the source file. Relative to the root of the project.\n\t */\n\tpath: string;\n}) {\n\treturn (\n\t\t<a\n\t\t\thref={new URL(path, \"https://github.com/jsrepojs/jsrepo/blob/main/\").toString()}\n\t\t\tclassName=\"flex place-items-center justify-center not-prose\"\n\t\t>\n\t\t\t<Badge variant=\"secondary\" className={cn(\"text-muted-foreground\", className)} {...props}>\n\t\t\t\t<CodeIcon className=\"size-3\" />\n\t\t\t\t<span>Source</span>\n\t\t\t</Badge>\n\t\t</a>\n\t);\n}\n\nfunction OfficialBadge({ className, ...props }: Omit<React.ComponentProps<typeof Badge>, \"variant\">) {\n\treturn (\n\t\t<Tooltip>\n\t\t\t<TooltipTrigger asChild>\n\t\t\t\t<Badge variant=\"secondary\" className={cn(\"text-muted-foreground cursor-default\", className)} {...props}>\n\t\t\t\t\t<BadgeCheckIcon className=\"size-3\" />\n\t\t\t\t\t<span>Official</span>\n\t\t\t\t</Badge>\n\t\t\t</TooltipTrigger>\n\t\t\t<TooltipContent>\n\t\t\t\t<p>Officially supported by the jsrepo team.</p>\n\t\t\t</TooltipContent>\n\t\t</Tooltip>\n\t);\n}\n\nfunction DefaultBadge({ className, ...props }: Omit<React.ComponentProps<typeof Badge>, \"variant\">) {\n\treturn (\n\t\t<Tooltip>\n\t\t\t<TooltipTrigger asChild>\n\t\t\t\t<Badge variant=\"secondary\" className={cn(\"text-muted-foreground cursor-default\", className)} {...props}>\n\t\t\t\t\t<SettingsIcon className=\"size-3\" />\n\t\t\t\t\t<span>Default</span>\n\t\t\t\t</Badge>\n\t\t\t</TooltipTrigger>\n\t\t\t<TooltipContent>\n\t\t\t\t<p>Works with zero config.</p>\n\t\t\t</TooltipContent>\n\t\t</Tooltip>\n\t);\n}\n\ntype DownloadsResponse = {\n\tdownloads: number;\n\tstart: string;\n\tend: string;\n\tpackage: string;\n};\n\nfunction NpmBadge({\n\tpackageName,\n\tclassName,\n\t...props\n}: Omit<React.ComponentProps<typeof Badge>, \"variant\"> & { packageName: string }) {\n\tconst query = useQuery({\n\t\tqueryKey: [\"npm\", packageName],\n\t\tqueryFn: async () => {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`https://api.npmjs.org/downloads/point/last-month/${packageName}`);\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tconst data = (await response.json()) as DownloadsResponse;\n\n\t\t\t\treturn data.downloads;\n\t\t\t} catch {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t},\n\t\tstaleTime: 1000 * 60 * 60 * 24, // 24 hours\n\t\trefetchOnMount: false,\n\t\trefetchOnWindowFocus: false,\n\t});\n\n\treturn (\n\t\t<Tooltip>\n\t\t\t<TooltipTrigger asChild>\n\t\t\t\t<a\n\t\t\t\t\thref={`https://npmjs.com/package/${packageName}`}\n\t\t\t\t\tclassName=\"flex place-items-center justify-center not-prose\"\n\t\t\t\t>\n\t\t\t\t\t<Badge variant=\"secondary\" className={cn(\"text-muted-foreground\", className)} {...props}>\n\t\t\t\t\t\t<Download className=\"size-3\" />\n\t\t\t\t\t\t<span>{query.data ?? 0}/month</span>\n\t\t\t\t\t</Badge>\n\t\t\t\t</a>\n\t\t\t</TooltipTrigger>\n\t\t\t<TooltipContent>\n\t\t\t\t<p>Available on npm.</p>\n\t\t\t</TooltipContent>\n\t\t</Tooltip>\n\t);\n}\n\nfunction BadgeGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n\treturn (\n\t\t<TooltipProvider>\n\t\t\t<div {...props} className={cn(\"flex place-items-center gap-1 mb-8\", className)} data-slot=\"badge-group\" />\n\t\t</TooltipProvider>\n\t);\n}\n\nexport { Badge, badgeVariants, SourceBadge, OfficialBadge, DefaultBadge, BadgeGroup, NpmBadge };\n"
  },
  {
    "path": "apps/docs/src/components/ui/button.tsx",
    "content": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n\t\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n\t\t\t\tsecondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n\t\t\t\tghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n\t\t\t\tlink: \"text-primary underline-offset-4 hover:underline\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n\t\t\t\tsm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n\t\t\t\tlg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n\t\t\t\ticon: \"size-9\",\n\t\t\t\t\"icon-sm\": \"size-8\",\n\t\t\t\t\"icon-lg\": \"size-10\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t\tsize: \"default\",\n\t\t},\n\t}\n);\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n\tVariantProps<typeof buttonVariants> & {\n\t\tasChild?: boolean;\n\t};\n\nfunction Button({ className, variant, size, asChild = false, ...props }: ButtonProps) {\n\tconst Comp = asChild ? Slot : \"button\";\n\n\treturn <Comp data-slot=\"button\" className={cn(buttonVariants({ variant, size, className }))} {...props} />;\n}\n\nexport { Button, buttonVariants };\n"
  },
  {
    "path": "apps/docs/src/components/ui/collapsible.tsx",
    "content": "'use client';\nimport * as CollapsiblePrimitive from '@radix-ui/react-collapsible';\nimport { forwardRef, useEffect, useState } from 'react';\nimport { cn } from '@/lib/utils';\n\nconst Collapsible = CollapsiblePrimitive.Root;\n\nconst CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;\n\nconst CollapsibleContent = forwardRef<\n  HTMLDivElement,\n  React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent>\n>(({ children, ...props }, ref) => {\n  const [mounted, setMounted] = useState(false);\n\n  useEffect(() => {\n    setMounted(true);\n  }, []);\n\n  return (\n    <CollapsiblePrimitive.CollapsibleContent\n      ref={ref}\n      {...props}\n      className={cn(\n        'overflow-hidden',\n        mounted &&\n          'data-[state=closed]:animate-fd-collapsible-up data-[state=open]:animate-fd-collapsible-down',\n        props.className,\n      )}\n    >\n      {children}\n    </CollapsiblePrimitive.CollapsibleContent>\n  );\n});\n\nCollapsibleContent.displayName =\n  CollapsiblePrimitive.CollapsibleContent.displayName;\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\n"
  },
  {
    "path": "apps/docs/src/components/ui/field.tsx",
    "content": "\"use client\"\n\nimport { useMemo } from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Label } from \"@/components/ui/label\"\nimport { Separator } from \"@/components/ui/separator\"\n\nfunction FieldSet({ className, ...props }: React.ComponentProps<\"fieldset\">) {\n  return (\n    <fieldset\n      data-slot=\"field-set\"\n      className={cn(\n        \"flex flex-col gap-6\",\n        \"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FieldLegend({\n  className,\n  variant = \"legend\",\n  ...props\n}: React.ComponentProps<\"legend\"> & { variant?: \"legend\" | \"label\" }) {\n  return (\n    <legend\n      data-slot=\"field-legend\"\n      data-variant={variant}\n      className={cn(\n        \"mb-3 font-medium\",\n        \"data-[variant=legend]:text-base\",\n        \"data-[variant=label]:text-sm\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FieldGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"field-group\"\n      className={cn(\n        \"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nconst fieldVariants = cva(\n  \"group/field flex w-full gap-3 data-[invalid=true]:text-destructive\",\n  {\n    variants: {\n      orientation: {\n        vertical: [\"flex-col [&>*]:w-full [&>.sr-only]:w-auto\"],\n        horizontal: [\n          \"flex-row items-center\",\n          \"[&>[data-slot=field-label]]:flex-auto\",\n          \"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n        ],\n        responsive: [\n          \"flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto\",\n          \"@md/field-group:[&>[data-slot=field-label]]:flex-auto\",\n          \"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n        ],\n      },\n    },\n    defaultVariants: {\n      orientation: \"vertical\",\n    },\n  }\n)\n\nfunction Field({\n  className,\n  orientation = \"vertical\",\n  ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof fieldVariants>) {\n  return (\n    <div\n      role=\"group\"\n      data-slot=\"field\"\n      data-orientation={orientation}\n      className={cn(fieldVariants({ orientation }), className)}\n      {...props}\n    />\n  )\n}\n\nfunction FieldContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"field-content\"\n      className={cn(\n        \"group/field-content flex flex-1 flex-col gap-1.5 leading-snug\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FieldLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof Label>) {\n  return (\n    <Label\n      data-slot=\"field-label\"\n      className={cn(\n        \"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50\",\n        \"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4\",\n        \"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FieldTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"field-label\"\n      className={cn(\n        \"flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FieldDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"field-description\"\n      className={cn(\n        \"text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance\",\n        \"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5\",\n        \"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FieldSeparator({\n  children,\n  className,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  children?: React.ReactNode\n}) {\n  return (\n    <div\n      data-slot=\"field-separator\"\n      data-content={!!children}\n      className={cn(\n        \"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2\",\n        className\n      )}\n      {...props}\n    >\n      <Separator className=\"absolute inset-0 top-1/2\" />\n      {children && (\n        <span\n          className=\"bg-background text-muted-foreground relative mx-auto block w-fit px-2\"\n          data-slot=\"field-separator-content\"\n        >\n          {children}\n        </span>\n      )}\n    </div>\n  )\n}\n\nfunction FieldError({\n  className,\n  children,\n  errors,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  errors?: Array<{ message?: string } | undefined>\n}) {\n  const content = useMemo(() => {\n    if (children) {\n      return children\n    }\n\n    if (!errors?.length) {\n      return null\n    }\n\n    const uniqueErrors = [\n      ...new Map(errors.map((error) => [error?.message, error])).values(),\n    ]\n\n    if (uniqueErrors?.length == 1) {\n      return uniqueErrors[0]?.message\n    }\n\n    return (\n      <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n        {uniqueErrors.map(\n          (error, index) =>\n            error?.message && <li key={index}>{error.message}</li>\n        )}\n      </ul>\n    )\n  }, [children, errors])\n\n  if (!content) {\n    return null\n  }\n\n  return (\n    <div\n      role=\"alert\"\n      data-slot=\"field-error\"\n      className={cn(\"text-destructive text-sm font-normal\", className)}\n      {...props}\n    >\n      {content}\n    </div>\n  )\n}\n\nexport {\n  Field,\n  FieldLabel,\n  FieldDescription,\n  FieldError,\n  FieldGroup,\n  FieldLegend,\n  FieldSeparator,\n  FieldSet,\n  FieldContent,\n  FieldTitle,\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/github-button.tsx",
    "content": "\"use client\";\n\nimport Link from \"next/link\";\nimport { Button } from \"./button\";\nimport { GitHub } from \"../logos/github\";\nimport { useQuery } from \"@tanstack/react-query\";\n\nexport type GithubButtonProps = {\n\trepo: { owner: string; name: string };\n\tfallback: number;\n};\n\ntype UnGhStarResponse = {\n\ttotalStars: number;\n};\n\nexport function GitHubButton({ repo, fallback }: GithubButtonProps) {\n\tconst { data } = useQuery({\n\t\tqueryKey: [\"github\", repo.owner, repo.name],\n\t\tqueryFn: () =>\n\t\t\tfetch(`https://ungh.cc/stars/${repo.owner}/${repo.name}`).then(\n\t\t\t\t(res) => res.json() as Promise<UnGhStarResponse>\n\t\t\t),\n\t});\n\n\treturn (\n\t\t<Button variant=\"ghost\" asChild className=\"bg-background!\">\n\t\t\t<Link href={`https://github.com/${repo.owner}/${repo.name}`} target=\"_blank\">\n\t\t\t\t<GitHub className=\"size-4\" />\n\t\t\t\t<span className=\"font-mono font-normal\">{data?.totalStars ?? fallback}</span>\n\t\t\t</Link>\n\t\t</Button>\n\t);\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/index.ts",
    "content": "export { Badge, SourceBadge, OfficialBadge, DefaultBadge, BadgeGroup, NpmBadge } from \"./badge\";\nexport { Button } from \"./button\";\nexport { Tabs, TabsList, TabsTrigger, TabsContent } from \"./tabs\";\nexport { UnderlineTabs, UnderlineTabsList, UnderlineTabsTrigger, UnderlineTabsContent } from \"./underline-tabs\";\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from \"./accordion\";"
  },
  {
    "path": "apps/docs/src/components/ui/input-group.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport { Textarea } from \"@/components/ui/textarea\"\n\nfunction InputGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"input-group\"\n      role=\"group\"\n      className={cn(\n        \"group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none\",\n        \"h-9 min-w-0 has-[>textarea]:h-auto\",\n\n        // Variants based on alignment.\n        \"has-[>[data-align=inline-start]]:[&>input]:pl-2\",\n        \"has-[>[data-align=inline-end]]:[&>input]:pr-2\",\n        \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3\",\n        \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3\",\n\n        // Focus state.\n        \"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]\",\n\n        // Error state.\n        \"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nconst inputGroupAddonVariants = cva(\n  \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n  {\n    variants: {\n      align: {\n        \"inline-start\":\n          \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n        \"inline-end\":\n          \"order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]\",\n        \"block-start\":\n          \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n        \"block-end\":\n          \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\",\n      },\n    },\n    defaultVariants: {\n      align: \"inline-start\",\n    },\n  }\n)\n\nfunction InputGroupAddon({\n  className,\n  align = \"inline-start\",\n  ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof inputGroupAddonVariants>) {\n  return (\n    <div\n      role=\"group\"\n      data-slot=\"input-group-addon\"\n      data-align={align}\n      className={cn(inputGroupAddonVariants({ align }), className)}\n      onClick={(e) => {\n        if ((e.target as HTMLElement).closest(\"button\")) {\n          return\n        }\n        e.currentTarget.parentElement?.querySelector(\"input\")?.focus()\n      }}\n      {...props}\n    />\n  )\n}\n\nconst inputGroupButtonVariants = cva(\n  \"text-sm shadow-none flex gap-2 items-center\",\n  {\n    variants: {\n      size: {\n        xs: \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n        sm: \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n        \"icon-xs\":\n          \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n        \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\",\n      },\n    },\n    defaultVariants: {\n      size: \"xs\",\n    },\n  }\n)\n\nfunction InputGroupButton({\n  className,\n  type = \"button\",\n  variant = \"ghost\",\n  size = \"xs\",\n  ...props\n}: Omit<React.ComponentProps<typeof Button>, \"size\"> &\n  VariantProps<typeof inputGroupButtonVariants>) {\n  return (\n    <Button\n      type={type}\n      data-size={size}\n      variant={variant}\n      className={cn(inputGroupButtonVariants({ size }), className)}\n      {...props}\n    />\n  )\n}\n\nfunction InputGroupText({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      className={cn(\n        \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction InputGroupInput({\n  className,\n  ...props\n}: React.ComponentProps<\"input\">) {\n  return (\n    <Input\n      data-slot=\"input-group-control\"\n      className={cn(\n        \"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction InputGroupTextarea({\n  className,\n  ...props\n}: React.ComponentProps<\"textarea\">) {\n  return (\n    <Textarea\n      data-slot=\"input-group-control\"\n      className={cn(\n        \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupText,\n  InputGroupInput,\n  InputGroupTextarea,\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/input.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n  return (\n    <input\n      type={type}\n      data-slot=\"input\"\n      className={cn(\n        \"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n        \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n        \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Input }\n"
  },
  {
    "path": "apps/docs/src/components/ui/item.tsx",
    "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Separator } from \"./separator\"\n\nfunction ItemGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      role=\"list\"\n      data-slot=\"item-group\"\n      className={cn(\"group/item-group flex flex-col\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ItemSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof Separator>) {\n  return (\n    <Separator\n      data-slot=\"item-separator\"\n      orientation=\"horizontal\"\n      className={cn(\"my-0\", className)}\n      {...props}\n    />\n  )\n}\n\nconst itemVariants = cva(\n  \"group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        outline: \"border-border\",\n        muted: \"bg-muted/50\",\n      },\n      size: {\n        default: \"p-4 gap-4 \",\n        sm: \"py-3 px-4 gap-2.5\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nfunction Item({\n  className,\n  variant = \"default\",\n  size = \"default\",\n  asChild = false,\n  ...props\n}: React.ComponentProps<\"div\"> &\n  VariantProps<typeof itemVariants> & { asChild?: boolean }) {\n  const Comp = asChild ? Slot : \"div\"\n  return (\n    <Comp\n      data-slot=\"item\"\n      data-variant={variant}\n      data-size={size}\n      className={cn(itemVariants({ variant, size, className }))}\n      {...props}\n    />\n  )\n}\n\nconst itemMediaVariants = cva(\n  \"flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none group-has-data-[slot=item-description]/item:translate-y-0.5\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-transparent\",\n        icon: \"size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4\",\n        image:\n          \"size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nfunction ItemMedia({\n  className,\n  variant = \"default\",\n  ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof itemMediaVariants>) {\n  return (\n    <div\n      data-slot=\"item-media\"\n      data-variant={variant}\n      className={cn(itemMediaVariants({ variant, className }))}\n      {...props}\n    />\n  )\n}\n\nfunction ItemContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"item-content\"\n      className={cn(\n        \"flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ItemTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"item-title\"\n      className={cn(\n        \"flex w-fit items-center gap-2 text-sm leading-snug font-medium\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ItemDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"item-description\"\n      className={cn(\n        \"text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance\",\n        \"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ItemActions({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"item-actions\"\n      className={cn(\"flex items-center gap-2\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ItemHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"item-header\"\n      className={cn(\n        \"flex basis-full items-center justify-between gap-2\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ItemFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"item-footer\"\n      className={cn(\n        \"flex basis-full items-center justify-between gap-2\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Item,\n  ItemMedia,\n  ItemContent,\n  ItemActions,\n  ItemGroup,\n  ItemSeparator,\n  ItemTitle,\n  ItemDescription,\n  ItemHeader,\n  ItemFooter,\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/label.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Label({\n  className,\n  ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n  return (\n    <LabelPrimitive.Root\n      data-slot=\"label\"\n      className={cn(\n        \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Label }\n"
  },
  {
    "path": "apps/docs/src/components/ui/popover.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Popover({\n  ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n  return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\n}\n\nfunction PopoverTrigger({\n  ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n  return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\nfunction PopoverContent({\n  className,\n  align = \"center\",\n  sideOffset = 4,\n  ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n  return (\n    <PopoverPrimitive.Portal>\n      <PopoverPrimitive.Content\n        data-slot=\"popover-content\"\n        align={align}\n        sideOffset={sideOffset}\n        className={cn(\n          \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden\",\n          className\n        )}\n        {...props}\n      />\n    </PopoverPrimitive.Portal>\n  )\n}\n\nfunction PopoverAnchor({\n  ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n  return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />\n}\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }\n"
  },
  {
    "path": "apps/docs/src/components/ui/scroll-area.tsx",
    "content": "import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';\nimport * as React from 'react';\nimport { cn } from '../../lib/cn';\n\nconst ScrollArea = React.forwardRef<\n  React.ComponentRef<typeof ScrollAreaPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n  <ScrollAreaPrimitive.Root\n    ref={ref}\n    type=\"scroll\"\n    className={cn('overflow-hidden', className)}\n    {...props}\n  >\n    {children}\n    <ScrollAreaPrimitive.Corner />\n    <ScrollBar orientation=\"vertical\" />\n  </ScrollAreaPrimitive.Root>\n));\n\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;\n\nconst ScrollViewport = React.forwardRef<\n  React.ComponentRef<typeof ScrollAreaPrimitive.Viewport>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Viewport>\n>(({ className, children, ...props }, ref) => (\n  <ScrollAreaPrimitive.Viewport\n    ref={ref}\n    className={cn('size-full rounded-[inherit]', className)}\n    {...props}\n  >\n    {children}\n  </ScrollAreaPrimitive.Viewport>\n));\n\nScrollViewport.displayName = ScrollAreaPrimitive.Viewport.displayName;\n\nconst ScrollBar = React.forwardRef<\n  React.ComponentRef<typeof ScrollAreaPrimitive.Scrollbar>,\n  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Scrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n  <ScrollAreaPrimitive.Scrollbar\n    ref={ref}\n    orientation={orientation}\n    className={cn(\n      'flex select-none data-[state=hidden]:animate-fd-fade-out',\n      orientation === 'vertical' && 'h-full w-1.5',\n      orientation === 'horizontal' && 'h-1.5 flex-col',\n      className,\n    )}\n    {...props}\n  >\n    <ScrollAreaPrimitive.ScrollAreaThumb className=\"relative flex-1 rounded-full bg-fd-border\" />\n  </ScrollAreaPrimitive.Scrollbar>\n));\nScrollBar.displayName = ScrollAreaPrimitive.Scrollbar.displayName;\n\nexport { ScrollArea, ScrollBar, ScrollViewport };\n"
  },
  {
    "path": "apps/docs/src/components/ui/separator.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Separator({\n  className,\n  orientation = \"horizontal\",\n  decorative = true,\n  ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n  return (\n    <SeparatorPrimitive.Root\n      data-slot=\"separator\"\n      decorative={decorative}\n      orientation={orientation}\n      className={cn(\n        \"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Separator }\n"
  },
  {
    "path": "apps/docs/src/components/ui/sonner.tsx",
    "content": "\"use client\"\n\nimport {\n  CircleCheckIcon,\n  InfoIcon,\n  Loader2Icon,\n  OctagonXIcon,\n  TriangleAlertIcon,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n  const { theme = \"system\" } = useTheme()\n\n  return (\n    <Sonner\n      theme={theme as ToasterProps[\"theme\"]}\n      className=\"toaster group\"\n      icons={{\n        success: <CircleCheckIcon className=\"size-4\" />,\n        info: <InfoIcon className=\"size-4\" />,\n        warning: <TriangleAlertIcon className=\"size-4\" />,\n        error: <OctagonXIcon className=\"size-4\" />,\n        loading: <Loader2Icon className=\"size-4 animate-spin\" />,\n      }}\n      style={\n        {\n          \"--normal-bg\": \"var(--popover)\",\n          \"--normal-text\": \"var(--popover-foreground)\",\n          \"--normal-border\": \"var(--border)\",\n          \"--border-radius\": \"var(--radius)\",\n        } as React.CSSProperties\n      }\n      {...props}\n    />\n  )\n}\n\nexport { Toaster }\n"
  },
  {
    "path": "apps/docs/src/components/ui/table.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Table({ className, ...props }: React.ComponentProps<\"table\">) {\n  return (\n    <div\n      data-slot=\"table-container\"\n      className=\"relative w-full overflow-x-auto\"\n    >\n      <table\n        data-slot=\"table\"\n        className={cn(\"w-full caption-bottom text-sm\", className)}\n        {...props}\n      />\n    </div>\n  )\n}\n\nfunction TableHeader({ className, ...props }: React.ComponentProps<\"thead\">) {\n  return (\n    <thead\n      data-slot=\"table-header\"\n      className={cn(\"[&_tr]:border-b\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TableBody({ className, ...props }: React.ComponentProps<\"tbody\">) {\n  return (\n    <tbody\n      data-slot=\"table-body\"\n      className={cn(\"[&_tr:last-child]:border-0\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TableFooter({ className, ...props }: React.ComponentProps<\"tfoot\">) {\n  return (\n    <tfoot\n      data-slot=\"table-footer\"\n      className={cn(\n        \"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TableRow({ className, ...props }: React.ComponentProps<\"tr\">) {\n  return (\n    <tr\n      data-slot=\"table-row\"\n      className={cn(\n        \"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TableHead({ className, ...props }: React.ComponentProps<\"th\">) {\n  return (\n    <th\n      data-slot=\"table-head\"\n      className={cn(\n        \"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TableCell({ className, ...props }: React.ComponentProps<\"td\">) {\n  return (\n    <td\n      data-slot=\"table-cell\"\n      className={cn(\n        \"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TableCaption({\n  className,\n  ...props\n}: React.ComponentProps<\"caption\">) {\n  return (\n    <caption\n      data-slot=\"table-caption\"\n      className={cn(\"text-muted-foreground mt-4 text-sm\", className)}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Table,\n  TableHeader,\n  TableBody,\n  TableFooter,\n  TableHead,\n  TableRow,\n  TableCell,\n  TableCaption,\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/tabs.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Tabs({\n  className,\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>) {\n  return (\n    <TabsPrimitive.Root\n      data-slot=\"tabs\"\n      className={cn(\"flex flex-col gap-2\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TabsList({\n  className,\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.List>) {\n  return (\n    <TabsPrimitive.List\n      data-slot=\"tabs-list\"\n      className={cn(\n        \"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TabsTrigger({\n  className,\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n  return (\n    <TabsPrimitive.Trigger\n      data-slot=\"tabs-trigger\"\n      className={cn(\n        \"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TabsContent({\n  className,\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.Content>) {\n  return (\n    <TabsPrimitive.Content\n      data-slot=\"tabs-content\"\n      className={cn(\"flex-1 outline-none\", className)}\n      {...props}\n    />\n  )\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent }\n"
  },
  {
    "path": "apps/docs/src/components/ui/terminal.tsx",
    "content": "\"use client\"\n\nimport {\n  Children,\n  createContext,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\"\nimport { motion, MotionProps, useInView } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface SequenceContextValue {\n  completeItem: (index: number) => void\n  activeIndex: number\n  sequenceStarted: boolean\n}\n\nconst SequenceContext = createContext<SequenceContextValue | null>(null)\n\nconst useSequence = () => useContext(SequenceContext)\n\nconst ItemIndexContext = createContext<number | null>(null)\nconst useItemIndex = () => useContext(ItemIndexContext)\n\ninterface AnimatedSpanProps extends MotionProps {\n  children: React.ReactNode\n  delay?: number\n  className?: string\n  startOnView?: boolean\n}\n\nexport const AnimatedSpan = ({\n  children,\n  delay = 0,\n  className,\n  startOnView = false,\n  ...props\n}: AnimatedSpanProps) => {\n  const elementRef = useRef<HTMLDivElement | null>(null)\n  const isInView = useInView(elementRef as React.RefObject<Element>, {\n    amount: 0.3,\n    once: true,\n  })\n\n  const sequence = useSequence()\n  const itemIndex = useItemIndex()\n  const [hasStarted, setHasStarted] = useState(false)\n  useEffect(() => {\n    if (!sequence || itemIndex === null) return\n    if (!sequence.sequenceStarted) return\n    if (hasStarted) return\n    if (sequence.activeIndex === itemIndex) {\n      setHasStarted(true)\n    }\n  }, [sequence?.activeIndex, sequence?.sequenceStarted, hasStarted, itemIndex])\n\n  const shouldAnimate = sequence ? hasStarted : startOnView ? isInView : true\n\n  return (\n    <motion.div\n      ref={elementRef}\n      initial={{ opacity: 0, y: -5 }}\n      animate={shouldAnimate ? { opacity: 1, y: 0 } : { opacity: 0, y: -5 }}\n      transition={{ duration: 0.3, delay: sequence ? 0 : delay / 1000 }}\n      className={cn(\"grid text-sm font-normal tracking-tight\", className)}\n      onAnimationComplete={() => {\n        if (!sequence) return\n        if (itemIndex === null) return\n        sequence.completeItem(itemIndex)\n      }}\n      {...props}\n    >\n      {children}\n    </motion.div>\n  )\n}\n\ninterface TypingAnimationProps extends MotionProps {\n  children: string\n  className?: string\n  duration?: number\n  delay?: number\n  as?: React.ElementType\n  startOnView?: boolean\n}\n\nexport const TypingAnimation = ({\n  children,\n  className,\n  duration = 60,\n  delay = 0,\n  as: Component = \"span\",\n  startOnView = true,\n  ...props\n}: TypingAnimationProps) => {\n  if (typeof children !== \"string\") {\n    throw new Error(\"TypingAnimation: children must be a string. Received:\")\n  }\n\n  const MotionComponent = useMemo(\n    () =>\n      motion.create(Component, {\n        forwardMotionProps: true,\n      }),\n    [Component]\n  )\n\n  const [displayedText, setDisplayedText] = useState<string>(\"\")\n  const [started, setStarted] = useState(false)\n  const elementRef = useRef<HTMLElement | null>(null)\n  const isInView = useInView(elementRef as React.RefObject<Element>, {\n    amount: 0.3,\n    once: true,\n  })\n\n  const sequence = useSequence()\n  const itemIndex = useItemIndex()\n\n  useEffect(() => {\n    if (sequence && itemIndex !== null) {\n      if (!sequence.sequenceStarted) return\n      if (started) return\n      if (sequence.activeIndex === itemIndex) {\n        setStarted(true)\n      }\n      return\n    }\n\n    if (!startOnView) {\n      const startTimeout = setTimeout(() => setStarted(true), delay)\n      return () => clearTimeout(startTimeout)\n    }\n\n    if (!isInView) return\n\n    const startTimeout = setTimeout(() => setStarted(true), delay)\n    return () => clearTimeout(startTimeout)\n  }, [\n    delay,\n    startOnView,\n    isInView,\n    started,\n    sequence?.activeIndex,\n    sequence?.sequenceStarted,\n    itemIndex,\n  ])\n\n  useEffect(() => {\n    if (!started) return\n\n    let i = 0\n    const typingEffect = setInterval(() => {\n      if (i < children.length) {\n        setDisplayedText(children.substring(0, i + 1))\n        i++\n      } else {\n        clearInterval(typingEffect)\n        if (sequence && itemIndex !== null) {\n          sequence.completeItem(itemIndex)\n        }\n      }\n    }, duration)\n\n    return () => {\n      clearInterval(typingEffect)\n    }\n  }, [children, duration, started])\n\n  return (\n    <MotionComponent\n      ref={elementRef}\n      className={cn(\"text-sm font-normal tracking-tight\", className)}\n      {...props}\n    >\n      {displayedText}\n    </MotionComponent>\n  )\n}\n\ninterface TerminalProps {\n  children: React.ReactNode\n  className?: string\n  sequence?: boolean\n  startOnView?: boolean\n}\n\nexport const Terminal = ({\n  children,\n  className,\n  sequence = true,\n  startOnView = true,\n}: TerminalProps) => {\n  const containerRef = useRef<HTMLDivElement | null>(null)\n  const isInView = useInView(containerRef as React.RefObject<Element>, {\n    amount: 0.3,\n    once: true,\n  })\n\n  const [activeIndex, setActiveIndex] = useState(0)\n  const sequenceHasStarted = sequence ? !startOnView || isInView : false\n\n  const contextValue = useMemo<SequenceContextValue | null>(() => {\n    if (!sequence) return null\n    return {\n      completeItem: (index: number) => {\n        setActiveIndex((current) => (index === current ? current + 1 : current))\n      },\n      activeIndex,\n      sequenceStarted: sequenceHasStarted,\n    }\n  }, [sequence, activeIndex, sequenceHasStarted])\n\n  const wrappedChildren = useMemo(() => {\n    if (!sequence) return children\n    const array = Children.toArray(children)\n    return array.map((child, index) => (\n      <ItemIndexContext.Provider key={index} value={index}>\n        {child as React.ReactNode}\n      </ItemIndexContext.Provider>\n    ))\n  }, [children, sequence])\n\n  const content = (\n    <div\n      data-slot=\"terminal\"\n      ref={containerRef}\n      className={cn(\n        \"border-border bg-card z-0 h-full max-h-[400px] w-full max-w-lg rounded-xl border\",\n        className\n      )}\n    >\n      <div data-slot=\"terminal-header\" className=\"border-border flex flex-col gap-y-2 border-b p-4\">\n        <div className=\"flex flex-row gap-x-2\">\n          <div className=\"h-2 w-2 rounded-full bg-red-500\"></div>\n          <div className=\"h-2 w-2 rounded-full bg-yellow-500\"></div>\n          <div className=\"h-2 w-2 rounded-full bg-green-500\"></div>\n        </div>\n      </div>\n      <pre className=\"p-4\" data-slot=\"terminal-content\">\n        <code className=\"grid gap-y-1 overflow-auto\">{wrappedChildren}</code>\n      </pre>\n    </div>\n  )\n\n  if (!sequence) return content\n\n  return (\n    <SequenceContext.Provider value={contextValue}>\n      {content}\n    </SequenceContext.Provider>\n  )\n}\n"
  },
  {
    "path": "apps/docs/src/components/ui/textarea.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n  return (\n    <textarea\n      data-slot=\"textarea\"\n      className={cn(\n        \"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Textarea }\n"
  },
  {
    "path": "apps/docs/src/components/ui/tooltip.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction TooltipProvider({\n  delayDuration = 0,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n  return (\n    <TooltipPrimitive.Provider\n      data-slot=\"tooltip-provider\"\n      delayDuration={delayDuration}\n      {...props}\n    />\n  )\n}\n\nfunction Tooltip({\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n  return (\n    <TooltipProvider>\n      <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n    </TooltipProvider>\n  )\n}\n\nfunction TooltipTrigger({\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n  return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />\n}\n\nfunction TooltipContent({\n  className,\n  sideOffset = 0,\n  children,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n  return (\n    <TooltipPrimitive.Portal>\n      <TooltipPrimitive.Content\n        data-slot=\"tooltip-content\"\n        sideOffset={sideOffset}\n        className={cn(\n          \"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n        <TooltipPrimitive.Arrow className=\"bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]\" />\n      </TooltipPrimitive.Content>\n    </TooltipPrimitive.Portal>\n  )\n}\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n"
  },
  {
    "path": "apps/docs/src/components/ui/underline-tabs.tsx",
    "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction UnderlineTabs({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {\n\treturn (\n\t\t<TabsPrimitive.Root data-slot=\"underline-tabs\" className={cn(\"flex flex-col gap-2\", className)} {...props} />\n\t);\n}\n\nfunction UnderlineTabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n\treturn (\n\t\t<TabsPrimitive.List\n\t\t\tdata-slot=\"underline-tabs-list\"\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-end relative border-b h-9 w-fit overflow-x-auto max-w-full scrollbar-hide\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nfunction UnderlineTabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n\treturn (\n\t\t<TabsPrimitive.Trigger\n\t\t\tdata-slot=\"underline-tabs-trigger\"\n\t\t\tclassName={cn(\n\t\t\t\t\"inline-flex relative top-px h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 px-3 py-1 text-sm font-medium whitespace-nowrap transition-[color]\",\n\t\t\t\t\"dark:text-muted-foreground border-b-2 border-b-transparent border-transparent\",\n\t\t\t\t\"data-[state=active]:border-foreground dark:data-[state=active]:text-foreground\",\n\t\t\t\t\"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:outline-1\",\n\t\t\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nfunction UnderlineTabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n\treturn (\n\t\t<TabsPrimitive.Content\n\t\t\tdata-slot=\"underline-tabs-content\"\n\t\t\tclassName={cn(\"flex-1 outline-none\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport { UnderlineTabs, UnderlineTabsList, UnderlineTabsTrigger, UnderlineTabsContent };\n"
  },
  {
    "path": "apps/docs/src/hooks/use-copy-to-clipboard.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\n\ntype CopyFn = (text: string) => Promise<boolean>\n\nexport function useCopyToClipboard(delay = 2000): [CopyFn, boolean] {\n  const [isCopied, setIsCopied] = React.useState(false)\n\n  React.useEffect(() => {\n    if (!isCopied) return\n\n    const timer = setTimeout(() => {\n      setIsCopied(false)\n    }, delay)\n\n    return () => clearTimeout(timer)\n  }, [isCopied, delay])\n\n  const copy: CopyFn = React.useCallback(async (text) => {\n    if (!navigator?.clipboard) {\n      console.warn(\"Clipboard not supported\")\n      return false\n    }\n\n    try {\n      await navigator.clipboard.writeText(text)\n      setIsCopied(true)\n      return true\n    } catch (error) {\n      console.warn(\"Copy failed\", error)\n      return false\n    }\n  }, [])\n\n  return [copy, isCopied]\n}\n"
  },
  {
    "path": "apps/docs/src/hooks/use-scroll-to-top.tsx",
    "content": "\"use client\";\n\nimport { useEffect } from \"react\";\nimport { usePathname } from \"next/navigation\";\n\nexport function useScrollToTop() {\n    const pathname = usePathname();\n\n    useEffect(() => {\n        window.scrollTo(0, 0);\n    }, [pathname]);\n}"
  },
  {
    "path": "apps/docs/src/lib/cn.ts",
    "content": "export { twMerge as cn } from 'tailwind-merge';\n"
  },
  {
    "path": "apps/docs/src/lib/is-active.ts",
    "content": "import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs';\n\nfunction normalize(url: string) {\n  if (url.length > 1 && url.endsWith('/')) return url.slice(0, -1);\n  return url;\n}\n\nexport function isActive(\n  url: string,\n  pathname: string,\n  nested = true,\n): boolean {\n  url = normalize(url);\n  pathname = normalize(pathname);\n\n  return url === pathname || (nested && pathname.startsWith(`${url}/`));\n}\n\nexport function isTabActive(tab: SidebarTab, pathname: string) {\n  if (tab.urls) return tab.urls.has(normalize(pathname));\n\n  return isActive(tab.url, pathname, true);\n}\n"
  },
  {
    "path": "apps/docs/src/lib/layout.shared.tsx",
    "content": "import { JsrepoWordmark } from \"@/components/logos/jsrepo-com\";\nimport type { BaseLayoutProps } from \"fumadocs-ui/layouts/shared\";\nimport Link from \"next/link\";\n\n/**\n * Shared layout configurations\n *\n * you can customize layouts individually from:\n * Home Layout: app/(home)/layout.tsx\n * Docs Layout: app/docs/layout.tsx\n */\nexport function baseOptions(): BaseLayoutProps {\n\treturn {\n\t\tnav: {\n\t\t\ttitle: (\n\t\t\t\t<>\n\t\t\t\t\t<JsrepoWordmark className=\"h-6\" aria-label=\"jsrepo\" />\n\t\t\t\t</>\n\t\t\t),\n\t\t},\n\t\t// see https://fumadocs.dev/docs/ui/navigation/links\n\t\tlinks: [],\n\t\tgithubUrl: \"https://github.com/jsrepojs/jsrepo\",\n\t};\n}\n"
  },
  {
    "path": "apps/docs/src/lib/og.ts",
    "content": "export async function loadGoogleFont(font: string, text: string) {\n\tconst url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent(text)}`;\n\tconst css = await (await fetch(url)).text();\n\tconst resource = css.match(/src: url\\((.+)\\) format\\('(opentype|truetype)'\\)/);\n\n\tif (resource) {\n\t\tconst response = await fetch(resource[1]);\n\t\tif (response.status == 200) {\n\t\t\treturn await response.arrayBuffer();\n\t\t}\n\t}\n\n\tthrow new Error(\"failed to load font data\");\n}"
  },
  {
    "path": "apps/docs/src/lib/source.ts",
    "content": "import { docs } from '@/.source';\nimport { type InferPageType, loader } from 'fumadocs-core/source';\nimport { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons';\n\n// See https://fumadocs.dev/docs/headless/source-api for more info\nexport const source = loader({\n  baseUrl: '/docs',\n  source: docs.toFumadocsSource(),\n  plugins: [lucideIconsPlugin()],\n});\n\nexport function getPageImage(page: InferPageType<typeof source>) {\n  const segments = [...page.slugs, 'image.png'];\n\n  return {\n    segments,\n    url: `/og/docs/${segments.join('/')}`,\n  };\n}\n\nexport async function getLLMText(page: InferPageType<typeof source>) {\n  const processed = await page.data.getText('processed');\n\n  return `# ${page.data.title} (${page.url})\n\n${processed}`;\n}\n"
  },
  {
    "path": "apps/docs/src/lib/utils.ts",
    "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"
  },
  {
    "path": "apps/docs/src/mdx-components.tsx",
    "content": "import { CodeBlock, Pre } from \"fumadocs-ui/components/codeblock\";\nimport { Step, Steps } from 'fumadocs-ui/components/steps';\nimport { TypeTable } from \"fumadocs-ui/components/type-table\";\nimport defaultMdxComponents from \"fumadocs-ui/mdx\";\nimport type { MDXComponents } from \"mdx/types\";\nimport * as UI from \"@/components/ui\";\nimport * as Logos from \"@/components/logos\";\nimport * as AllLucideIcons from \"lucide-react\";\n\n// Only import the <name>Icon icons to prevent conflicts\nconst Icons = Object.fromEntries(\n  Object.entries(AllLucideIcons).filter(\n    ([key]) => key.endsWith(\"Icon\")\n  )\n);\n\n// use this function to get MDX components, you will need it for rendering MDX\nexport function getMDXComponents(components?: MDXComponents): MDXComponents {\n\treturn {\n\t\t...defaultMdxComponents,\n\t\tpre: ({ ref: _ref, ...props }) => (\n\t\t\t<CodeBlock {...props}>\n\t\t\t\t<Pre>{props.children}</Pre>\n\t\t\t</CodeBlock>\n\t\t),\n\t\tSteps,\n\t\tStep,\n\t\tTypeTable,\n\t\t...Logos,\n\t\t...UI,\n\t\t...(Icons as unknown as MDXComponents),\n\t\t...components,\n\t};\n}\n"
  },
  {
    "path": "apps/docs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"target\": \"ESNext\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"react-jsx\",\n    \"incremental\": true,\n    \"paths\": {\n      \"@/.source\": [\n        \"./.source/index.ts\"\n      ],\n      \"@/*\": [\n        \"./src/*\"\n      ]\n    },\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ]\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \".next/types/**/*.ts\",\n    \"next.config.mjs\",\n    \".next/dev/types/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "biome.json",
    "content": "{\n\t\"$schema\": \"https://biomejs.dev/schemas/2.3.11/schema.json\",\n\t\"formatter\": {\n\t\t\"enabled\": true,\n\t\t\"formatWithErrors\": false,\n\t\t\"indentStyle\": \"tab\",\n\t\t\"indentWidth\": 4,\n\t\t\"lineEnding\": \"lf\",\n\t\t\"lineWidth\": 100,\n\t\t\"attributePosition\": \"auto\"\n\t},\n\t\"files\": {\n\t\t\"includes\": [\n\t\t\t\"**\",\n\t\t\t\"!**/dist\",\n\t\t\t\"!**/node_modules\",\n\t\t\t\"!**/docs\",\n\t\t\t\"!**/temp-test\",\n\t\t\t\"!**/tests/fixtures/**/*\",\n\t\t\t\"!*.svelte\"\n\t\t]\n\t},\n\t\"assist\": { \"actions\": { \"source\": { \"organizeImports\": \"on\" } } },\n\t\"linter\": {\n\t\t\"enabled\": true,\n\t\t\"rules\": {\n\t\t\t\"recommended\": true,\n\t\t\t\"complexity\": {\n\t\t\t\t\"noExtraBooleanCast\": \"error\",\n\t\t\t\t\"noUselessCatch\": \"error\",\n\t\t\t\t\"noUselessTypeConstraint\": \"error\",\n\t\t\t\t\"noAdjacentSpacesInRegex\": \"error\"\n\t\t\t},\n\t\t\t\"correctness\": {\n\t\t\t\t\"noConstAssign\": \"error\",\n\t\t\t\t\"noConstantCondition\": \"error\",\n\t\t\t\t\"noEmptyCharacterClassInRegex\": \"error\",\n\t\t\t\t\"noEmptyPattern\": \"error\",\n\t\t\t\t\"noGlobalObjectCalls\": \"error\",\n\t\t\t\t\"noInvalidConstructorSuper\": \"error\",\n\t\t\t\t\"noNonoctalDecimalEscape\": \"error\",\n\t\t\t\t\"noPrecisionLoss\": \"error\",\n\t\t\t\t\"noSelfAssign\": \"error\",\n\t\t\t\t\"noSetterReturn\": \"error\",\n\t\t\t\t\"noSwitchDeclarations\": \"error\",\n\t\t\t\t\"noUndeclaredVariables\": \"error\",\n\t\t\t\t\"noUnreachable\": \"error\",\n\t\t\t\t\"noUnreachableSuper\": \"error\",\n\t\t\t\t\"noUnsafeFinally\": \"error\",\n\t\t\t\t\"noUnsafeOptionalChaining\": \"error\",\n\t\t\t\t\"noUnusedLabels\": \"error\",\n\t\t\t\t\"noUnusedPrivateClassMembers\": \"error\",\n\t\t\t\t\"noUnusedVariables\": \"error\",\n\t\t\t\t\"useIsNan\": \"error\",\n\t\t\t\t\"useValidForDirection\": \"error\",\n\t\t\t\t\"useYield\": \"error\",\n\t\t\t\t\"noInvalidBuiltinInstantiation\": \"error\",\n\t\t\t\t\"useValidTypeof\": \"error\"\n\t\t\t},\n\t\t\t\"style\": {\n\t\t\t\t\"noNamespace\": \"error\",\n\t\t\t\t\"useAsConstAssertion\": \"error\",\n\t\t\t\t\"noNonNullAssertion\": \"off\",\n\t\t\t\t\"useArrayLiterals\": \"off\"\n\t\t\t},\n\t\t\t\"suspicious\": {\n\t\t\t\t\"noAsyncPromiseExecutor\": \"error\",\n\t\t\t\t\"noCatchAssign\": \"error\",\n\t\t\t\t\"noClassAssign\": \"error\",\n\t\t\t\t\"noCompareNegZero\": \"error\",\n\t\t\t\t\"noControlCharactersInRegex\": \"error\",\n\t\t\t\t\"noDebugger\": \"error\",\n\t\t\t\t\"noDuplicateCase\": \"error\",\n\t\t\t\t\"noDuplicateClassMembers\": \"error\",\n\t\t\t\t\"noDuplicateObjectKeys\": \"error\",\n\t\t\t\t\"noDuplicateParameters\": \"error\",\n\t\t\t\t\"noEmptyBlockStatements\": \"error\",\n\t\t\t\t\"noExplicitAny\": \"error\",\n\t\t\t\t\"noExtraNonNullAssertion\": \"error\",\n\t\t\t\t\"noFallthroughSwitchClause\": \"error\",\n\t\t\t\t\"noFunctionAssign\": \"error\",\n\t\t\t\t\"noGlobalAssign\": \"error\",\n\t\t\t\t\"noImportAssign\": \"error\",\n\t\t\t\t\"noMisleadingCharacterClass\": \"error\",\n\t\t\t\t\"noMisleadingInstantiator\": \"error\",\n\t\t\t\t\"noPrototypeBuiltins\": \"error\",\n\t\t\t\t\"noRedeclare\": \"error\",\n\t\t\t\t\"noShadowRestrictedNames\": \"error\",\n\t\t\t\t\"noUnsafeDeclarationMerging\": \"error\",\n\t\t\t\t\"noUnsafeNegation\": \"error\",\n\t\t\t\t\"useGetterReturn\": \"error\",\n\t\t\t\t\"useNamespaceKeyword\": \"error\",\n\t\t\t\t\"noWith\": \"error\"\n\t\t\t}\n\t\t},\n\t\t\"includes\": [\"**\", \"!**/dist/**/*\", \"!**/node_modules/**/*\"]\n\t},\n\t\"javascript\": {\n\t\t\"formatter\": {\n\t\t\t\"jsxQuoteStyle\": \"double\",\n\t\t\t\"quoteProperties\": \"asNeeded\",\n\t\t\t\"trailingCommas\": \"es5\",\n\t\t\t\"semicolons\": \"always\",\n\t\t\t\"arrowParentheses\": \"always\",\n\t\t\t\"bracketSpacing\": true,\n\t\t\t\"bracketSameLine\": false,\n\t\t\t\"quoteStyle\": \"single\",\n\t\t\t\"attributePosition\": \"auto\"\n\t\t},\n\t\t\"globals\": []\n\t},\n\t\"overrides\": [\n\t\t{\n\t\t\t\"includes\": [\"**/*.md\"],\n\t\t\t\"formatter\": {\n\t\t\t\t\"indentStyle\": \"space\",\n\t\t\t\t\"indentWidth\": 2,\n\t\t\t\t\"lineWidth\": 100\n\t\t\t}\n\t\t}\n\t],\n\t\"css\": {\n\t\t\"parser\": {\n\t\t\t\"tailwindDirectives\": true\n\t\t}\n\t},\n\t\"html\": {\n\t\t\"experimentalFullSupportEnabled\": true,\n\t\t\"formatter\": {\n\t\t\t\"indentScriptAndStyle\": true\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/react/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# env files (can opt-in for committing if needed)\n.env*\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "examples/react/README.md",
    "content": "# @example/react\n\nAn example of using jsrepo to build and distribute a react registry.\n\n## Getting Started\n\n```sh\npnpm create next-app\n```\n\n```sh\npnpm dlx jsrepo init\n```"
  },
  {
    "path": "examples/react/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!.next/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "examples/react/jsrepo.config.mts",
    "content": "import { output as shadcn } from '@jsrepo/shadcn/output';\nimport { defineConfig } from 'jsrepo';\nimport { distributed } from 'jsrepo/outputs';\n\nexport default defineConfig({\n\tregistry: {\n\t\tname: '@example/react',\n\t\tversion: 'package',\n\t\thomepage: 'https://www.jsrepo.com/@example/react',\n\t\toutputs: [\n\t\t\tdistributed({ dir: './public/r', format: true }),\n\t\t\tshadcn({ dir: './public/r/shadcn', format: true }),\n\t\t],\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: 'button',\n\t\t\t\ttype: 'ui',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/registry/ui/button.tsx',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/app/demos/button-demo/page.tsx',\n\t\t\t\t\t\ttype: 'page',\n\t\t\t\t\t\trole: 'example',\n\t\t\t\t\t\ttarget: 'src/app/demos/button-demo/page.tsx',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'utils',\n\t\t\t\ttype: 'lib',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/registry/lib/utils.ts',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t},\n});\n"
  },
  {
    "path": "examples/react/next.config.ts",
    "content": "import type { NextConfig } from 'next';\n\nconst nextConfig: NextConfig = {\n\t/* config options here */\n\treactCompiler: true,\n};\n\nexport default nextConfig;\n"
  },
  {
    "path": "examples/react/package.json",
    "content": "{\n\t\"name\": \"@example/react\",\n\t\"version\": \"0.1.0\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"concurrently -n registry,app \\\"pnpm registry:dev\\\" \\\"next dev\\\"\",\n\t\t\"build\": \"pnpm build:registry && next build\",\n\t\t\"start\": \"pnpm build:registry && next start\",\n\t\t\"registry:build\": \"jsrepo build\",\n\t\t\"registry:dev\": \"jsrepo build --watch\"\n\t},\n\t\"dependencies\": {\n\t\t\"next\": \"16.1.1\",\n\t\t\"react\": \"19.2.3\",\n\t\t\"react-dom\": \"19.2.3\"\n\t},\n\t\"devDependencies\": {\n\t\t\"concurrently\": \"catalog:\",\n\t\t\"@jsrepo/shadcn\": \"workspace:*\",\n\t\t\"@tailwindcss/postcss\": \"^4\",\n\t\t\"@types/node\": \"^20\",\n\t\t\"@types/react\": \"^19\",\n\t\t\"@types/react-dom\": \"^19\",\n\t\t\"babel-plugin-react-compiler\": \"1.0.0\",\n\t\t\"clsx\": \"^2.1.1\",\n\t\t\"jsrepo\": \"workspace:*\",\n\t\t\"tailwind-merge\": \"^3.3.1\",\n\t\t\"tailwind-variants\": \"^3.1.1\",\n\t\t\"tailwindcss\": \"^4\",\n\t\t\"typescript\": \"^5\"\n\t}\n}\n"
  },
  {
    "path": "examples/react/postcss.config.mjs",
    "content": "const config = {\n\tplugins: {\n\t\t'@tailwindcss/postcss': {},\n\t},\n};\n\nexport default config;\n"
  },
  {
    "path": "examples/react/public/r/button.json",
    "content": "{\n\t\"name\": \"button\",\n\t\"type\": \"ui\",\n\t\"add\": \"when-added\",\n\t\"files\": [\n\t\t{\n\t\t\t\"type\": \"ui\",\n\t\t\t\"role\": \"file\",\n\t\t\t\"content\": \"import type React from 'react';\\nimport { tv, type VariantProps } from 'tailwind-variants';\\nimport { cn } from '@/registry/lib/utils';\\n\\nexport const buttonVariants = tv({\\n\\tbase: 'flex items-center rounded-md active:scale-98 transition-all',\\n\\tvariants: {\\n\\t\\tvariant: {\\n\\t\\t\\tdefault: 'bg-primary text-primary-foreground',\\n\\t\\t\\tdestructive: 'bg-destructive text-destructive-foreground',\\n\\t\\t\\toutline: 'border border-border hover:bg-accent',\\n\\t\\t},\\n\\t\\tsize: {\\n\\t\\t\\tdefault: 'h-8 px-2.5',\\n\\t\\t\\tsm: 'h-7 px-2',\\n\\t\\t},\\n\\t},\\n});\\n\\nexport type Variant = VariantProps<typeof buttonVariants>['variant'];\\nexport type Size = VariantProps<typeof buttonVariants>['size'];\\n\\nexport function Button({\\n\\tvariant = 'default',\\n\\tsize = 'default',\\n\\tclassName,\\n\\t...props\\n}: React.ComponentProps<'button'> & { variant?: Variant; size?: Size }) {\\n\\treturn <button {...props} className={cn(buttonVariants({ variant, size }), className)} />;\\n}\\n\",\n\t\t\t\"path\": \"button.tsx\",\n\t\t\t\"_imports_\": [\n\t\t\t\t{\n\t\t\t\t\t\"import\": \"@/registry/lib/utils\",\n\t\t\t\t\t\"item\": \"utils\",\n\t\t\t\t\t\"file\": {\n\t\t\t\t\t\t\"type\": \"lib\",\n\t\t\t\t\t\t\"path\": \"utils.ts\"\n\t\t\t\t\t},\n\t\t\t\t\t\"meta\": {\n\t\t\t\t\t\t\"filePathRelativeToItem\": \"utils.ts\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t},\n\t\t{\n\t\t\t\"type\": \"page\",\n\t\t\t\"role\": \"example\",\n\t\t\t\"content\": \"import { Button } from '@/registry/ui/button';\\n\\nexport function ButtonDemoPage() {\\n\\treturn (\\n\\t\\t<>\\n\\t\\t\\t<div className=\\\"flex place-items-center gap-2\\\">\\n\\t\\t\\t\\t<Button variant=\\\"default\\\">Click me</Button>\\n\\t\\t\\t\\t<Button variant=\\\"destructive\\\">Click me</Button>\\n\\t\\t\\t\\t<Button variant=\\\"outline\\\">Click me</Button>\\n\\t\\t\\t</div>\\n\\t\\t\\t<div className=\\\"flex place-items-end gap-2\\\">\\n\\t\\t\\t\\t<Button variant=\\\"default\\\" size=\\\"sm\\\">\\n\\t\\t\\t\\t\\tSm\\n\\t\\t\\t\\t</Button>\\n\\t\\t\\t\\t<Button variant=\\\"default\\\" size=\\\"default\\\">\\n\\t\\t\\t\\t\\tMd\\n\\t\\t\\t\\t</Button>\\n\\t\\t\\t</div>\\n\\t\\t</>\\n\\t);\\n}\\n\",\n\t\t\t\"path\": \"page.tsx\",\n\t\t\t\"_imports_\": [\n\t\t\t\t{\n\t\t\t\t\t\"import\": \"@/registry/ui/button\",\n\t\t\t\t\t\"item\": \"button\",\n\t\t\t\t\t\"file\": {\n\t\t\t\t\t\t\"type\": \"ui\",\n\t\t\t\t\t\t\"path\": \"button.tsx\"\n\t\t\t\t\t},\n\t\t\t\t\t\"meta\": {\n\t\t\t\t\t\t\"filePathRelativeToItem\": \"button.tsx\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"target\": \"src/app/demos/button-demo/page.tsx\",\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t}\n\t],\n\t\"registryDependencies\": [\"utils\"],\n\t\"dependencies\": [\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"react\",\n\t\t\t\"version\": \"19.2.0\"\n\t\t}\n\t],\n\t\"devDependencies\": [\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"tailwind-variants\",\n\t\t\t\"version\": \"^3.1.1\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/react/public/r/registry.json",
    "content": "{\n\t\"name\": \"@example/react\",\n\t\"homepage\": \"https://www.jsrepo.com/@example/react\",\n\t\"version\": \"package\",\n\t\"type\": \"distributed\",\n\t\"items\": [\n\t\t{\n\t\t\t\"name\": \"button\",\n\t\t\t\"type\": \"ui\",\n\t\t\t\"add\": \"when-added\",\n\t\t\t\"registryDependencies\": [\"utils\"],\n\t\t\t\"dependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"react\",\n\t\t\t\t\t\"version\": \"19.2.0\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"devDependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"tailwind-variants\",\n\t\t\t\t\t\"version\": \"^3.1.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"files\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"ui\",\n\t\t\t\t\t\"role\": \"file\",\n\t\t\t\t\t\"path\": \"button.tsx\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"page\",\n\t\t\t\t\t\"role\": \"example\",\n\t\t\t\t\t\"path\": \"page.tsx\",\n\t\t\t\t\t\"target\": \"src/app/demos/button-demo/page.tsx\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"name\": \"utils\",\n\t\t\t\"type\": \"lib\",\n\t\t\t\"add\": \"when-added\",\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"clsx\",\n\t\t\t\t\t\"version\": \"^2.1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"tailwind-merge\",\n\t\t\t\t\t\"version\": \"^3.3.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"files\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"lib\",\n\t\t\t\t\t\"role\": \"file\",\n\t\t\t\t\t\"path\": \"utils.ts\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/react/public/r/shadcn/button.json",
    "content": "{\n\t\"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n\t\"name\": \"button\",\n\t\"type\": \"registry:ui\",\n\t\"files\": [\n\t\t{\n\t\t\t\"type\": \"registry:ui\",\n\t\t\t\"path\": \"button.tsx\",\n\t\t\t\"content\": \"import type React from 'react';\\nimport { tv, type VariantProps } from 'tailwind-variants';\\nimport { cn } from '@/registry/lib/utils';\\n\\nexport const buttonVariants = tv({\\n\\tbase: 'flex items-center rounded-md active:scale-98 transition-all',\\n\\tvariants: {\\n\\t\\tvariant: {\\n\\t\\t\\tdefault: 'bg-primary text-primary-foreground',\\n\\t\\t\\tdestructive: 'bg-destructive text-destructive-foreground',\\n\\t\\t\\toutline: 'border border-border hover:bg-accent',\\n\\t\\t},\\n\\t\\tsize: {\\n\\t\\t\\tdefault: 'h-8 px-2.5',\\n\\t\\t\\tsm: 'h-7 px-2',\\n\\t\\t},\\n\\t},\\n});\\n\\nexport type Variant = VariantProps<typeof buttonVariants>['variant'];\\nexport type Size = VariantProps<typeof buttonVariants>['size'];\\n\\nexport function Button({\\n\\tvariant = 'default',\\n\\tsize = 'default',\\n\\tclassName,\\n\\t...props\\n}: React.ComponentProps<'button'> & { variant?: Variant; size?: Size }) {\\n\\treturn <button {...props} className={cn(buttonVariants({ variant, size }), className)} />;\\n}\\n\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"registry:page\",\n\t\t\t\"path\": \"page.tsx\",\n\t\t\t\"target\": \"src/app/demos/button-demo/page.tsx\",\n\t\t\t\"content\": \"import { Button } from '@/registry/ui/button';\\n\\nexport function ButtonDemoPage() {\\n\\treturn (\\n\\t\\t<>\\n\\t\\t\\t<div className=\\\"flex place-items-center gap-2\\\">\\n\\t\\t\\t\\t<Button variant=\\\"default\\\">Click me</Button>\\n\\t\\t\\t\\t<Button variant=\\\"destructive\\\">Click me</Button>\\n\\t\\t\\t\\t<Button variant=\\\"outline\\\">Click me</Button>\\n\\t\\t\\t</div>\\n\\t\\t\\t<div className=\\\"flex place-items-end gap-2\\\">\\n\\t\\t\\t\\t<Button variant=\\\"default\\\" size=\\\"sm\\\">\\n\\t\\t\\t\\t\\tSm\\n\\t\\t\\t\\t</Button>\\n\\t\\t\\t\\t<Button variant=\\\"default\\\" size=\\\"default\\\">\\n\\t\\t\\t\\t\\tMd\\n\\t\\t\\t\\t</Button>\\n\\t\\t\\t</div>\\n\\t\\t</>\\n\\t);\\n}\\n\"\n\t\t}\n\t],\n\t\"registryDependencies\": [\"utils\"],\n\t\"dependencies\": [\"react@19.2.0\"]\n}\n"
  },
  {
    "path": "examples/react/public/r/shadcn/registry.json",
    "content": "{\n\t\"$schema\": \"https://ui.shadcn.com/schema/registry.json\",\n\t\"name\": \"@example/react\",\n\t\"homepage\": \"https://www.jsrepo.com/@example/react\",\n\t\"items\": [\n\t\t{\n\t\t\t\"name\": \"button\",\n\t\t\t\"type\": \"registry:ui\",\n\t\t\t\"dependencies\": [\"react@19.2.0\"],\n\t\t\t\"registryDependencies\": [\"utils\"],\n\t\t\t\"files\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"registry:ui\",\n\t\t\t\t\t\"path\": \"button.tsx\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"registry:page\",\n\t\t\t\t\t\"path\": \"page.tsx\",\n\t\t\t\t\t\"target\": \"src/app/demos/button-demo/page.tsx\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"name\": \"utils\",\n\t\t\t\"type\": \"registry:lib\",\n\t\t\t\"dependencies\": [],\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"files\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"registry:lib\",\n\t\t\t\t\t\"path\": \"utils.ts\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/react/public/r/shadcn/utils.json",
    "content": "{\n\t\"$schema\": \"https://ui.shadcn.com/schema/registry-item.json\",\n\t\"name\": \"utils\",\n\t\"type\": \"registry:lib\",\n\t\"files\": [\n\t\t{\n\t\t\t\"type\": \"registry:lib\",\n\t\t\t\"path\": \"utils.ts\",\n\t\t\t\"content\": \"import { type ClassValue, clsx } from 'clsx';\\nimport { twMerge } from 'tailwind-merge';\\n\\nexport function cn(...inputs: ClassValue[]) {\\n\\treturn twMerge(clsx(inputs));\\n}\\n\"\n\t\t}\n\t],\n\t\"registryDependencies\": [],\n\t\"dependencies\": []\n}\n"
  },
  {
    "path": "examples/react/public/r/utils.json",
    "content": "{\n\t\"name\": \"utils\",\n\t\"type\": \"lib\",\n\t\"add\": \"when-added\",\n\t\"files\": [\n\t\t{\n\t\t\t\"type\": \"lib\",\n\t\t\t\"role\": \"file\",\n\t\t\t\"content\": \"import { type ClassValue, clsx } from 'clsx';\\nimport { twMerge } from 'tailwind-merge';\\n\\nexport function cn(...inputs: ClassValue[]) {\\n\\treturn twMerge(clsx(inputs));\\n}\\n\",\n\t\t\t\"path\": \"utils.ts\",\n\t\t\t\"_imports_\": [],\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t}\n\t],\n\t\"registryDependencies\": [],\n\t\"dependencies\": [],\n\t\"devDependencies\": [\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"clsx\",\n\t\t\t\"version\": \"^2.1.1\"\n\t\t},\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"tailwind-merge\",\n\t\t\t\"version\": \"^3.3.1\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/react/src/app/demos/button-demo/page.tsx",
    "content": "import { Button } from '@/registry/ui/button';\n\nexport function ButtonDemoPage() {\n\treturn (\n\t\t<>\n\t\t\t<div className=\"flex place-items-center gap-2\">\n\t\t\t\t<Button variant=\"default\">Click me</Button>\n\t\t\t\t<Button variant=\"destructive\">Click me</Button>\n\t\t\t\t<Button variant=\"outline\">Click me</Button>\n\t\t\t</div>\n\t\t\t<div className=\"flex place-items-end gap-2\">\n\t\t\t\t<Button variant=\"default\" size=\"sm\">\n\t\t\t\t\tSm\n\t\t\t\t</Button>\n\t\t\t\t<Button variant=\"default\" size=\"default\">\n\t\t\t\t\tMd\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</>\n\t);\n}\n"
  },
  {
    "path": "examples/react/src/app/globals.css",
    "content": "@import \"tailwindcss\";\n\n:root {\n\t--background: oklch(1 0 0);\n\t--foreground: oklch(0.145 0 0);\n\t--card: oklch(1 0 0);\n\t--card-foreground: oklch(0.145 0 0);\n\t--popover: oklch(1 0 0);\n\t--popover-foreground: oklch(0.145 0 0);\n\t--primary: oklch(0.205 0 0);\n\t--primary-foreground: oklch(0.985 0 0);\n\t--secondary: oklch(0.97 0 0);\n\t--secondary-foreground: oklch(0.205 0 0);\n\t--muted: oklch(0.97 0 0);\n\t--muted-foreground: oklch(0.556 0 0);\n\t--accent: oklch(0.97 0 0);\n\t--accent-foreground: oklch(0.205 0 0);\n\t--destructive: oklch(0.577 0.245 27.325);\n\t--destructive-foreground: oklch(1 0 0);\n\t--border: oklch(0.922 0 0);\n\t--input: oklch(0.922 0 0);\n\t--ring: oklch(0.708 0 0);\n\t--chart-1: oklch(0.81 0.1 252);\n\t--chart-2: oklch(0.62 0.19 260);\n\t--chart-3: oklch(0.55 0.22 263);\n\t--chart-4: oklch(0.49 0.22 264);\n\t--chart-5: oklch(0.42 0.18 266);\n\t--sidebar: oklch(0.985 0 0);\n\t--sidebar-foreground: oklch(0.145 0 0);\n\t--sidebar-primary: oklch(0.205 0 0);\n\t--sidebar-primary-foreground: oklch(0.985 0 0);\n\t--sidebar-accent: oklch(0.97 0 0);\n\t--sidebar-accent-foreground: oklch(0.205 0 0);\n\t--sidebar-border: oklch(0.922 0 0);\n\t--sidebar-ring: oklch(0.708 0 0);\n\t--font-sans:\n\t\tui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\",\n\t\tArial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n\t--font-serif: ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif;\n\t--font-mono:\n\t\tui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\",\n\t\tmonospace;\n\t--radius: 0.625rem;\n\t--shadow-x: 0;\n\t--shadow-y: 1px;\n\t--shadow-blur: 3px;\n\t--shadow-spread: 0px;\n\t--shadow-opacity: 0.1;\n\t--shadow-color: oklch(0 0 0);\n\t--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);\n\t--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);\n\t--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);\n\t--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);\n\t--tracking-normal: 0em;\n\t--spacing: 0.25rem;\n}\n\n.dark {\n\t--background: oklch(0.145 0 0);\n\t--foreground: oklch(0.985 0 0);\n\t--card: oklch(0.205 0 0);\n\t--card-foreground: oklch(0.985 0 0);\n\t--popover: oklch(0.269 0 0);\n\t--popover-foreground: oklch(0.985 0 0);\n\t--primary: oklch(0.922 0 0);\n\t--primary-foreground: oklch(0.205 0 0);\n\t--secondary: oklch(0.269 0 0);\n\t--secondary-foreground: oklch(0.985 0 0);\n\t--muted: oklch(0.269 0 0);\n\t--muted-foreground: oklch(0.708 0 0);\n\t--accent: oklch(0.371 0 0);\n\t--accent-foreground: oklch(0.985 0 0);\n\t--destructive: oklch(0.704 0.191 22.216);\n\t--destructive-foreground: oklch(0.985 0 0);\n\t--border: oklch(0.275 0 0);\n\t--input: oklch(0.325 0 0);\n\t--ring: oklch(0.556 0 0);\n\t--chart-1: oklch(0.81 0.1 252);\n\t--chart-2: oklch(0.62 0.19 260);\n\t--chart-3: oklch(0.55 0.22 263);\n\t--chart-4: oklch(0.49 0.22 264);\n\t--chart-5: oklch(0.42 0.18 266);\n\t--sidebar: oklch(0.205 0 0);\n\t--sidebar-foreground: oklch(0.985 0 0);\n\t--sidebar-primary: oklch(0.488 0.243 264.376);\n\t--sidebar-primary-foreground: oklch(0.985 0 0);\n\t--sidebar-accent: oklch(0.269 0 0);\n\t--sidebar-accent-foreground: oklch(0.985 0 0);\n\t--sidebar-border: oklch(0.275 0 0);\n\t--sidebar-ring: oklch(0.439 0 0);\n\t--font-sans:\n\t\tui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\",\n\t\tArial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n\t--font-serif: ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif;\n\t--font-mono:\n\t\tui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\",\n\t\tmonospace;\n\t--radius: 0.625rem;\n\t--shadow-x: 0;\n\t--shadow-y: 1px;\n\t--shadow-blur: 3px;\n\t--shadow-spread: 0px;\n\t--shadow-opacity: 0.1;\n\t--shadow-color: oklch(0 0 0);\n\t--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);\n\t--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);\n\t--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);\n\t--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);\n\t--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);\n}\n\n@theme inline {\n\t--color-background: var(--background);\n\t--color-foreground: var(--foreground);\n\t--color-card: var(--card);\n\t--color-card-foreground: var(--card-foreground);\n\t--color-popover: var(--popover);\n\t--color-popover-foreground: var(--popover-foreground);\n\t--color-primary: var(--primary);\n\t--color-primary-foreground: var(--primary-foreground);\n\t--color-secondary: var(--secondary);\n\t--color-secondary-foreground: var(--secondary-foreground);\n\t--color-muted: var(--muted);\n\t--color-muted-foreground: var(--muted-foreground);\n\t--color-accent: var(--accent);\n\t--color-accent-foreground: var(--accent-foreground);\n\t--color-destructive: var(--destructive);\n\t--color-destructive-foreground: var(--destructive-foreground);\n\t--color-border: var(--border);\n\t--color-input: var(--input);\n\t--color-ring: var(--ring);\n\t--color-chart-1: var(--chart-1);\n\t--color-chart-2: var(--chart-2);\n\t--color-chart-3: var(--chart-3);\n\t--color-chart-4: var(--chart-4);\n\t--color-chart-5: var(--chart-5);\n\t--color-sidebar: var(--sidebar);\n\t--color-sidebar-foreground: var(--sidebar-foreground);\n\t--color-sidebar-primary: var(--sidebar-primary);\n\t--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n\t--color-sidebar-accent: var(--sidebar-accent);\n\t--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n\t--color-sidebar-border: var(--sidebar-border);\n\t--color-sidebar-ring: var(--sidebar-ring);\n\n\t--font-sans: var(--font-sans);\n\t--font-mono: var(--font-mono);\n\t--font-serif: var(--font-serif);\n\n\t--radius-sm: calc(var(--radius) - 4px);\n\t--radius-md: calc(var(--radius) - 2px);\n\t--radius-lg: var(--radius);\n\t--radius-xl: calc(var(--radius) + 4px);\n\n\t--shadow-2xs: var(--shadow-2xs);\n\t--shadow-xs: var(--shadow-xs);\n\t--shadow-sm: var(--shadow-sm);\n\t--shadow: var(--shadow);\n\t--shadow-md: var(--shadow-md);\n\t--shadow-lg: var(--shadow-lg);\n\t--shadow-xl: var(--shadow-xl);\n\t--shadow-2xl: var(--shadow-2xl);\n}\n\nbody {\n\t@apply bg-background text-foreground;\n}\n"
  },
  {
    "path": "examples/react/src/app/layout.tsx",
    "content": "import type { Metadata } from 'next';\nimport { Geist, Geist_Mono } from 'next/font/google';\nimport './globals.css';\n\nconst geistSans = Geist({\n\tvariable: '--font-geist-sans',\n\tsubsets: ['latin'],\n});\n\nconst geistMono = Geist_Mono({\n\tvariable: '--font-geist-mono',\n\tsubsets: ['latin'],\n});\n\nexport const metadata: Metadata = {\n\ttitle: '@example/react',\n\tdescription: 'A example of using jsrepo to build a react registry',\n};\n\nexport default function RootLayout({\n\tchildren,\n}: Readonly<{\n\tchildren: React.ReactNode;\n}>) {\n\treturn (\n\t\t<html lang=\"en\" className=\"dark\">\n\t\t\t<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>\n\t\t\t\t{children}\n\t\t\t</body>\n\t\t</html>\n\t);\n}\n"
  },
  {
    "path": "examples/react/src/app/page.tsx",
    "content": "import { Button } from '@/registry/ui/button';\n\nexport default function Home() {\n\treturn (\n\t\t<main className=\"min-h-screen flex flex-col gap-4 items-center justify-center bg-background\">\n\t\t\t<h1 className=\"text-4xl font-bold\">@example/react</h1>\n\t\t\t<Button>Click me</Button>\n\t\t</main>\n\t);\n}\n"
  },
  {
    "path": "examples/react/src/registry/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n"
  },
  {
    "path": "examples/react/src/registry/ui/button.tsx",
    "content": "import type React from 'react';\nimport { tv, type VariantProps } from 'tailwind-variants';\nimport { cn } from '@/registry/lib/utils';\n\nexport const buttonVariants = tv({\n\tbase: 'flex items-center rounded-md active:scale-98 transition-all',\n\tvariants: {\n\t\tvariant: {\n\t\t\tdefault: 'bg-primary text-primary-foreground',\n\t\t\tdestructive: 'bg-destructive text-destructive-foreground',\n\t\t\toutline: 'border border-border hover:bg-accent',\n\t\t},\n\t\tsize: {\n\t\t\tdefault: 'h-8 px-2.5',\n\t\t\tsm: 'h-7 px-2',\n\t\t},\n\t},\n});\n\nexport type Variant = VariantProps<typeof buttonVariants>['variant'];\nexport type Size = VariantProps<typeof buttonVariants>['size'];\n\nexport function Button({\n\tvariant = 'default',\n\tsize = 'default',\n\tclassName,\n\t...props\n}: React.ComponentProps<'button'> & { variant?: Variant; size?: Size }) {\n\treturn <button {...props} className={cn(buttonVariants({ variant, size }), className)} />;\n}\n"
  },
  {
    "path": "examples/react/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2017\",\n\t\t\"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n\t\t\"allowJs\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"module\": \"esnext\",\n\t\t\"moduleResolution\": \"bundler\",\n\t\t\"resolveJsonModule\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"incremental\": true,\n\t\t\"plugins\": [\n\t\t\t{\n\t\t\t\t\"name\": \"next\"\n\t\t\t}\n\t\t],\n\t\t\"paths\": {\n\t\t\t\"@/*\": [\"./src/*\"]\n\t\t}\n\t},\n\t\"include\": [\n\t\t\"next-env.d.ts\",\n\t\t\"**/*.ts\",\n\t\t\"**/*.tsx\",\n\t\t\".next/types/**/*.ts\",\n\t\t\".next/dev/types/**/*.ts\",\n\t\t\"**/*.mts\"\n\t],\n\t\"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/svelte/.gitignore",
    "content": "node_modules\n\n# Output\n.output\n.vercel\n.netlify\n.wrangler\n/.svelte-kit\n/build\n\n# OS\n.DS_Store\nThumbs.db\n\n# Env\n.env\n.env.*\n!.env.example\n!.env.test\n\n# Vite\nvite.config.js.timestamp-*\nvite.config.ts.timestamp-*\n"
  },
  {
    "path": "examples/svelte/.npmrc",
    "content": "engine-strict=true\n"
  },
  {
    "path": "examples/svelte/README.md",
    "content": "# @example/svelte\n\nAn example of using jsrepo to build and distribute a svelte registry.\n\n## Getting Started\n\n```sh\npnpm dlx sv create\n```\n\n```sh\npnpm dlx jsrepo init\n```"
  },
  {
    "path": "examples/svelte/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!.svelte-kit/**/*\", \"!**/*.svelte\"]\n\t}\n}\n"
  },
  {
    "path": "examples/svelte/jsrepo.config.ts",
    "content": "import { defineConfig } from 'jsrepo';\nimport { distributed } from 'jsrepo/outputs';\n\nexport default defineConfig({\n\tregistry: {\n\t\tname: '@example/svelte',\n\t\thomepage: 'https://www.jsrepo.com/@example/svelte',\n\t\toutputs: [distributed({ dir: './static/r', format: true })],\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: 'button',\n\t\t\t\ttype: 'ui',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/lib/registry/ui/button',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/routes/demos/button-demo/+page.svelte',\n\t\t\t\t\t\trole: 'example',\n\t\t\t\t\t\ttype: 'page',\n\t\t\t\t\t\ttarget: 'src/routes/demos/button-demo/+page.svelte',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'utils',\n\t\t\t\ttype: 'lib',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/lib/registry/lib/utils.ts',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t},\n});\n"
  },
  {
    "path": "examples/svelte/package.json",
    "content": "{\n\t\"name\": \"@example/svelte\",\n\t\"private\": true,\n\t\"version\": \"0.0.1\",\n\t\"type\": \"module\",\n\t\"scripts\": {\n\t\t\"dev\": \"concurrently -n registry,app -c \\\"blue,green\\\" \\\"pnpm registry:dev\\\" \\\"vite dev\\\"\",\n\t\t\"build\": \"pnpm build:registry && vite build\",\n\t\t\"preview\": \"pnpm build:registry && vite preview\",\n\t\t\"prepare\": \"svelte-kit sync || echo ''\",\n\t\t\"check\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json\",\n\t\t\"check:watch\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch\",\n\t\t\"registry:build\": \"jsrepo build\",\n\t\t\"registry:dev\": \"jsrepo build --watch\"\n\t},\n\t\"devDependencies\": {\n\t\t\"concurrently\": \"catalog:\",\n\t\t\"@sveltejs/adapter-auto\": \"^7.0.0\",\n\t\t\"@sveltejs/kit\": \"^2.47.1\",\n\t\t\"@sveltejs/vite-plugin-svelte\": \"^6.2.1\",\n\t\t\"@tailwindcss/vite\": \"^4.1.14\",\n\t\t\"clsx\": \"^2.1.1\",\n\t\t\"jsrepo\": \"workspace:*\",\n\t\t\"svelte\": \"^5.41.0\",\n\t\t\"svelte-check\": \"^4.3.3\",\n\t\t\"tailwind-merge\": \"^3.3.1\",\n\t\t\"tailwind-variants\": \"^3.1.1\",\n\t\t\"tailwindcss\": \"^4.1.14\",\n\t\t\"typescript\": \"^5.9.3\",\n\t\t\"vite\": \"^7.1.10\"\n\t}\n}\n"
  },
  {
    "path": "examples/svelte/src/app.css",
    "content": "@import \"tailwindcss\";\n"
  },
  {
    "path": "examples/svelte/src/app.d.ts",
    "content": "// See https://svelte.dev/docs/kit/types#app.d.ts\n// for information about these interfaces\ndeclare global {\n\t// biome-ignore lint/style/noNamespace: cmon man\n\tnamespace App {\n\t\t// interface Error {}\n\t\t// interface Locals {}\n\t\t// interface PageData {}\n\t\t// interface PageState {}\n\t\t// interface Platform {}\n\t}\n}\n\nexport {};\n"
  },
  {
    "path": "examples/svelte/src/app.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t%sveltekit.head%\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">%sveltekit.body%</div>\n\t</body>\n</html>\n"
  },
  {
    "path": "examples/svelte/src/lib/registry/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n"
  },
  {
    "path": "examples/svelte/src/lib/registry/ui/button/button.svelte",
    "content": "<script lang=\"ts\" module>\n    import { tv, type VariantProps } from \"tailwind-variants\";\n\n    export const buttonVariants = tv({\n        base: \"flex items-center rounded-md active:scale-98 transition-all\",\n        variants: {\n            variant: {\n                default: \"bg-primary text-primary-foreground\",\n                destructive: \"bg-destructive text-destructive-foreground\",\n                outline: \"border border-border hover:bg-accent\",\n            },\n            size: {\n                default: \"h-8 px-2.5\",\n                sm: \"h-7 px-2\",\n            },\n        },\n    });\n\n    export type Variant = VariantProps<typeof buttonVariants>[\"variant\"];\n    export type Size = VariantProps<typeof buttonVariants>[\"size\"];\n</script>\n\n<script lang=\"ts\">\n    import { cn } from \"$lib/registry/lib/utils\";\n    import type { HTMLButtonAttributes } from \"svelte/elements\";\n\n    let { class: className, children, size = 'default', variant = 'default', ...restProps }: HTMLButtonAttributes & { variant?: Variant; size?: Size } = $props();\n</script>\n\n<button class={cn(buttonVariants({ variant, size }), className)} {...restProps}>\n    {@render children?.()}\n</button>"
  },
  {
    "path": "examples/svelte/src/lib/registry/ui/button/index.ts",
    "content": "import Button, { buttonVariants, type Size, type Variant } from './button.svelte';\n\nexport { Button, buttonVariants, type Size, type Variant };\n"
  },
  {
    "path": "examples/svelte/src/routes/+layout.svelte",
    "content": "<script lang=\"ts\">\n\timport '../app.css';\n\timport favicon from '$lib/assets/favicon.svg';\n\n\t\n\tconst { children } = $props();\n</script>\n\n<svelte:head>\n\t<link rel=\"icon\" href={favicon} />\n</svelte:head>\n\n{@render children()}\n"
  },
  {
    "path": "examples/svelte/src/routes/+page.svelte",
    "content": "<h1>Welcome to SvelteKit</h1>\n<p>Visit <a href=\"https://svelte.dev/docs/kit\">svelte.dev/docs/kit</a> to read the documentation</p>\n"
  },
  {
    "path": "examples/svelte/src/routes/demos/button-demo/+page.svelte",
    "content": "<script lang=\"ts\">\n    import { Button } from '$lib/registry/ui/button';\n</script>\n\n<div class=\"flex place-items-center gap-2\">\n    <Button variant=\"default\">Click me</Button>\n    <Button variant=\"destructive\">Click me</Button>\n    <Button variant=\"outline\">Click me</Button>\n</div>\n<div class=\"flex place-items-end gap-2\">\n    <Button variant=\"default\" size=\"sm\">Sm</Button>\n    <Button variant=\"default\" size=\"default\">Md</Button>\n</div>\n"
  },
  {
    "path": "examples/svelte/static/r/button.json",
    "content": "{\n\t\"name\": \"button\",\n\t\"type\": \"ui\",\n\t\"add\": \"when-added\",\n\t\"files\": [\n\t\t{\n\t\t\t\"type\": \"ui\",\n\t\t\t\"role\": \"file\",\n\t\t\t\"content\": \"<script lang=\\\"ts\\\" module>\\n    import { tv, type VariantProps } from \\\"tailwind-variants\\\";\\n\\n    export const buttonVariants = tv({\\n        base: \\\"flex items-center rounded-md active:scale-98 transition-all\\\",\\n        variants: {\\n            variant: {\\n                default: \\\"bg-primary text-primary-foreground\\\",\\n                destructive: \\\"bg-destructive text-destructive-foreground\\\",\\n                outline: \\\"border border-border hover:bg-accent\\\",\\n            },\\n            size: {\\n                default: \\\"h-8 px-2.5\\\",\\n                sm: \\\"h-7 px-2\\\",\\n            },\\n        },\\n    });\\n\\n    export type Variant = VariantProps<typeof buttonVariants>[\\\"variant\\\"];\\n    export type Size = VariantProps<typeof buttonVariants>[\\\"size\\\"];\\n</script>\\n\\n<script lang=\\\"ts\\\">\\n    import { cn } from \\\"$lib/registry/lib/utils\\\";\\n    import type { HTMLButtonAttributes } from \\\"svelte/elements\\\";\\n\\n    let { class: className, children, size = 'default', variant = 'default', ...restProps }: HTMLButtonAttributes & { variant?: Variant; size?: Size } = $props();\\n</script>\\n\\n<button class={cn(buttonVariants({ variant, size }), className)} {...restProps}>\\n    {@render children?.()}\\n</button>\",\n\t\t\t\"path\": \"button/button.svelte\",\n\t\t\t\"_imports_\": [\n\t\t\t\t{\n\t\t\t\t\t\"import\": \"$lib/registry/lib/utils\",\n\t\t\t\t\t\"item\": \"utils\",\n\t\t\t\t\t\"file\": {\n\t\t\t\t\t\t\"type\": \"lib\",\n\t\t\t\t\t\t\"path\": \"utils.ts\"\n\t\t\t\t\t},\n\t\t\t\t\t\"meta\": {\n\t\t\t\t\t\t\"filePathRelativeToItem\": \"utils.ts\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t},\n\t\t{\n\t\t\t\"type\": \"ui\",\n\t\t\t\"role\": \"file\",\n\t\t\t\"content\": \"import Button, { buttonVariants, type Size, type Variant } from './button.svelte';\\n\\nexport { Button, buttonVariants, type Size, type Variant };\\n\",\n\t\t\t\"path\": \"button/index.ts\",\n\t\t\t\"_imports_\": [],\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t},\n\t\t{\n\t\t\t\"type\": \"page\",\n\t\t\t\"role\": \"example\",\n\t\t\t\"content\": \"<script lang=\\\"ts\\\">\\n    import { Button } from '$lib/registry/ui/button';\\n</script>\\n\\n<div class=\\\"flex place-items-center gap-2\\\">\\n    <Button variant=\\\"default\\\">Click me</Button>\\n    <Button variant=\\\"destructive\\\">Click me</Button>\\n    <Button variant=\\\"outline\\\">Click me</Button>\\n</div>\\n<div class=\\\"flex place-items-end gap-2\\\">\\n    <Button variant=\\\"default\\\" size=\\\"sm\\\">Sm</Button>\\n    <Button variant=\\\"default\\\" size=\\\"default\\\">Md</Button>\\n</div>\\n\",\n\t\t\t\"path\": \"+page.svelte\",\n\t\t\t\"_imports_\": [\n\t\t\t\t{\n\t\t\t\t\t\"import\": \"$lib/registry/ui/button\",\n\t\t\t\t\t\"item\": \"button\",\n\t\t\t\t\t\"file\": {\n\t\t\t\t\t\t\"type\": \"ui\",\n\t\t\t\t\t\t\"path\": \"button/index.ts\"\n\t\t\t\t\t},\n\t\t\t\t\t\"meta\": {\n\t\t\t\t\t\t\"filePathRelativeToItem\": \"button/index.ts\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"target\": \"src/routes/demos/button-demo/+page.svelte\",\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t}\n\t],\n\t\"registryDependencies\": [\"utils\"],\n\t\"dependencies\": [],\n\t\"devDependencies\": [\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"tailwind-variants\",\n\t\t\t\"version\": \"^3.1.1\"\n\t\t},\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"svelte\",\n\t\t\t\"version\": \"^5.41.0\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/svelte/static/r/registry.json",
    "content": "{\n\t\"name\": \"@example/svelte\",\n\t\"homepage\": \"https://www.jsrepo.com/@example/svelte\",\n\t\"type\": \"distributed\",\n\t\"items\": [\n\t\t{\n\t\t\t\"name\": \"button\",\n\t\t\t\"type\": \"ui\",\n\t\t\t\"add\": \"when-added\",\n\t\t\t\"registryDependencies\": [\"utils\"],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"tailwind-variants\",\n\t\t\t\t\t\"version\": \"^3.1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"svelte\",\n\t\t\t\t\t\"version\": \"^5.41.0\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"files\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"ui\",\n\t\t\t\t\t\"role\": \"file\",\n\t\t\t\t\t\"path\": \"button/button.svelte\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"ui\",\n\t\t\t\t\t\"role\": \"file\",\n\t\t\t\t\t\"path\": \"button/index.ts\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"page\",\n\t\t\t\t\t\"role\": \"example\",\n\t\t\t\t\t\"path\": \"+page.svelte\",\n\t\t\t\t\t\"target\": \"src/routes/demos/button-demo/+page.svelte\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"name\": \"utils\",\n\t\t\t\"type\": \"lib\",\n\t\t\t\"add\": \"when-added\",\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": [\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"clsx\",\n\t\t\t\t\t\"version\": \"^2.1.1\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ecosystem\": \"js\",\n\t\t\t\t\t\"name\": \"tailwind-merge\",\n\t\t\t\t\t\"version\": \"^3.3.1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"files\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"lib\",\n\t\t\t\t\t\"role\": \"file\",\n\t\t\t\t\t\"path\": \"utils.ts\",\n\t\t\t\t\t\"registryDependencies\": [],\n\t\t\t\t\t\"dependencies\": [],\n\t\t\t\t\t\"devDependencies\": []\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/svelte/static/r/utils.json",
    "content": "{\n\t\"name\": \"utils\",\n\t\"type\": \"lib\",\n\t\"add\": \"when-added\",\n\t\"files\": [\n\t\t{\n\t\t\t\"type\": \"lib\",\n\t\t\t\"role\": \"file\",\n\t\t\t\"content\": \"import { type ClassValue, clsx } from 'clsx';\\nimport { twMerge } from 'tailwind-merge';\\n\\nexport function cn(...inputs: ClassValue[]) {\\n\\treturn twMerge(clsx(inputs));\\n}\\n\",\n\t\t\t\"path\": \"utils.ts\",\n\t\t\t\"_imports_\": [],\n\t\t\t\"registryDependencies\": [],\n\t\t\t\"dependencies\": [],\n\t\t\t\"devDependencies\": []\n\t\t}\n\t],\n\t\"registryDependencies\": [],\n\t\"dependencies\": [],\n\t\"devDependencies\": [\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"clsx\",\n\t\t\t\"version\": \"^2.1.1\"\n\t\t},\n\t\t{\n\t\t\t\"ecosystem\": \"js\",\n\t\t\t\"name\": \"tailwind-merge\",\n\t\t\t\"version\": \"^3.3.1\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "examples/svelte/static/robots.txt",
    "content": "# allow crawling everything by default\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "examples/svelte/svelte.config.js",
    "content": "import adapter from '@sveltejs/adapter-auto';\nimport { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n\t// Consult https://svelte.dev/docs/kit/integrations\n\t// for more information about preprocessors\n\tpreprocess: vitePreprocess(),\n\n\tkit: {\n\t\t// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.\n\t\t// If your environment is not supported, or you settled on a specific environment, switch out the adapter.\n\t\t// See https://svelte.dev/docs/kit/adapters for more information about adapters.\n\t\tadapter: adapter(),\n\t},\n};\n\nexport default config;\n"
  },
  {
    "path": "examples/svelte/tsconfig.json",
    "content": "{\n\t\"extends\": \"./.svelte-kit/tsconfig.json\",\n\t\"compilerOptions\": {\n\t\t\"allowImportingTsExtensions\": true,\n\t\t\"allowJs\": true,\n\t\t\"checkJs\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"sourceMap\": true,\n\t\t\"strict\": true,\n\t\t\"moduleResolution\": \"bundler\"\n\t}\n\t// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias\n\t// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files\n\t//\n\t// To make changes to top-level options such as include and exclude, we recommend extending\n\t// the generated config; see https://svelte.dev/docs/kit/configuration#typescript\n}\n"
  },
  {
    "path": "examples/svelte/vite.config.ts",
    "content": "import { sveltekit } from '@sveltejs/kit/vite';\nimport tailwindcss from '@tailwindcss/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n\tplugins: [tailwindcss(), sveltekit()],\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"@jsrepo/monorepo\",\n\t\"version\": \"0.0.0\",\n\t\"description\": \"The monorepo for jsrepo\",\n\t\"license\": \"MIT\",\n\t\"packageManager\": \"pnpm@10.30.1\",\n\t\"type\": \"module\",\n\t\"scripts\": {\n\t\t\"build\": \"pnpm --r --filter '!./examples/**' build\",\n\t\t\"dev\": \"concurrently -n packages,apps --kill-others=false -c \\\"blue,green\\\" \\\"pnpm --r --parallel --filter '!./examples/**' --filter '!./apps/**' dev\\\" \\\"wait-on packages/jsrepo/dist/api/index.mjs && pnpm -r --parallel --filter './apps/**' dev\\\"\",\n\t\t\"dev:packages\": \"pnpm --r --parallel --filter './packages/*' dev\",\n\t\t\"dev:apps\": \"pnpm -r --parallel --filter './apps/**' dev\",\n\t\t\"lint\": \"biome lint --write\",\n\t\t\"format\": \"biome format --write\",\n\t\t\"check\": \"biome check --fix && pnpm type:check\",\n\t\t\"type:check\": \"pnpm -r check\",\n\t\t\"test\": \"pnpm -r test\",\n\t\t\"changeset:version\": \"changeset version && pnpm format\",\n\t\t\"ci:release\": \"pnpm build:packages && changeset publish\",\n\t\t\"postinstall\": \"pnpm build:packages\",\n\t\t\"build:packages\": \"pnpm -parallel --filter './packages/*' build\",\n\t\t\"bundle-analyze\": \"pnpm -r --filter './packages/*' --color=always bundle-analyze\",\n\t\t\"build:examples\": \"pnpm -r --filter './examples/**' build\",\n\t\t\"build:example-registries\": \"pnpm -r --filter './examples/**' registry:build\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@biomejs/biome\": \"2.3.11\",\n\t\t\"@changesets/cli\": \"^2.29.8\",\n\t\t\"@svitejs/changesets-changelog-github-compact\": \"^1.2.0\",\n\t\t\"concurrently\": \"catalog:\",\n\t\t\"pkg-pr-new\": \"^0.0.63\",\n\t\t\"wait-on\": \"^9.0.4\"\n\t},\n\t\"pnpm\": {\n\t\t\"onlyBuiltDependencies\": [\n\t\t\t\"@tailwindcss/oxide\",\n\t\t\t\"core-js\",\n\t\t\t\"esbuild\",\n\t\t\t\"msw\",\n\t\t\t\"sharp\"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "packages/bun/CHANGELOG.md",
    "content": "# @jsrepo/bun\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 1.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 1.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 1.0.0\n### Patch Changes\n\n\n- chore: setup trusted publish ([#775](https://github.com/jsrepojs/jsrepo/pull/775))\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n"
  },
  {
    "path": "packages/bun/README.md",
    "content": "# @jsrepo/bun\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/bun)](https://npmjs.com/package/@jsrepo/bun)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/bun)](https://npmjs.com/package/@jsrepo/bun)\n\nA remote dependency resolver for **jsrepo** that resolves Bun `workspace:` protocol versions to concrete semver strings during registry builds.\n\n## Usage\n\nInstall the package:\n\n```sh\npnpm install @jsrepo/bun -D\n```\n\nAdd the resolver to your jsrepo config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport { bun } from \"@jsrepo/bun\";\n\nexport default defineConfig({\n  build: {\n    remoteDependencyResolver: bun(),\n  },\n});\n```\n\nNow your `workspace:` versions will be resolved to concrete versions when building your registry."
  },
  {
    "path": "packages/bun/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/bun\",\n\t\"description\": \"Remote dependency resolver for bun workspace protocol.\",\n\t\"version\": \"2.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"bun\",\n\t\t\"workspace\",\n\t\t\"resolver\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"test\": \"vitest\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"dependencies\": {\n\t\t\"fast-glob\": \"^3.3.3\",\n\t\t\"pathe\": \"^2.0.3\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"vitest\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/bun/src/index.ts",
    "content": "import type {\n\tRemoteDependency,\n\tRemoteDependencyResolver,\n\tRemoteDependencyResolverOptions,\n} from 'jsrepo/config';\nimport {\n\tbuildWorkspacePackageMap,\n\tfindBunWorkspaceRoot,\n\tgetWorkspacePatterns,\n} from './workspace.js';\n\n/**\n * Resolve `workspace:` and `catalog:` versions to concrete semver strings.\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import { bun } from \"@jsrepo/bun\";\n *\n * export default defineConfig({\n *   // ...\n *   build: {\n *     remoteDependencyResolver: bun(),\n *   },\n * });\n * ```\n */\nexport function bun(): RemoteDependencyResolver {\n\treturn async (\n\t\tdep: RemoteDependency,\n\t\toptions: RemoteDependencyResolverOptions\n\t): Promise<RemoteDependency> => {\n\t\tconst version = dep.version;\n\t\tif (!version) return dep;\n\n\t\tconst versionSpec = parseWorkspaceVersion(version);\n\t\tif (versionSpec === null) return dep;\n\n\t\tconst state = getOrParseState(options.cwd);\n\t\tconst workspaceVersion = state.workspacePackages.get(dep.name);\n\n\t\tif (!workspaceVersion) {\n\t\t\tthrow new Error(\n\t\t\t\t`Workspace package \"${dep.name}\" not found. Available: ${[\n\t\t\t\t\t...state.workspacePackages.keys(),\n\t\t\t\t].join(', ')}`\n\t\t\t);\n\t\t}\n\n\t\tconst resolvedVersion = resolveWorkspaceVersion(versionSpec, workspaceVersion);\n\t\treturn { ...dep, version: resolvedVersion };\n\t};\n}\n\ntype BunWorkspaceState = {\n\tworkspacePackages: Map<string, string>;\n};\n\nconst stateCache = new Map<string, BunWorkspaceState>();\n\nfunction getOrParseState(cwd: string): BunWorkspaceState {\n\tlet state = stateCache.get(cwd);\n\tif (!state) {\n\t\tconst workspaceRoot = findBunWorkspaceRoot(cwd);\n\t\tif (!workspaceRoot) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find package.json with workspaces by walking up from ${cwd}`\n\t\t\t);\n\t\t}\n\t\tconst patterns = getWorkspacePatterns(workspaceRoot);\n\t\tconst workspacePackages = buildWorkspacePackageMap(workspaceRoot, patterns);\n\t\tstate = { workspacePackages };\n\t\tstateCache.set(cwd, state);\n\t}\n\treturn state;\n}\n\nfunction resolveWorkspaceVersion(versionSpec: string, workspaceVersion: string): string {\n\tif (versionSpec === '*' || versionSpec === '' || !versionSpec) {\n\t\treturn workspaceVersion;\n\t}\n\tif (versionSpec === '^') {\n\t\treturn `^${workspaceVersion}`;\n\t}\n\tif (versionSpec === '~') {\n\t\treturn `~${workspaceVersion}`;\n\t}\n\t// workspace:1.0.0 - use literal\n\treturn versionSpec;\n}\n\n/**\n * Parse workspace: protocol value. Returns versionSpec or null if not workspace.\n */\nfunction parseWorkspaceVersion(version: string): string | null {\n\tif (!version.startsWith('workspace:')) return null;\n\treturn version.slice('workspace:'.length) || '*';\n}\n"
  },
  {
    "path": "packages/bun/src/workspace.ts",
    "content": "import fs, { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport fg from 'fast-glob';\nimport { dirname, join, resolve } from 'pathe';\n\nexport type WorkspacePackage = {\n\tname: string;\n\tversion: string;\n};\n\ntype PackageJsonWithWorkspaces =\n\t| { workspaces?: string[] }\n\t| { workspaces?: { packages?: string[] } };\n\n/**\n * Walk up from cwd to find a package.json with a workspaces field.\n */\nexport function findBunWorkspaceRoot(cwd: string): string | null {\n\tlet dir = resolve(cwd);\n\tconst pathRoot = path.parse(dir).root;\n\n\twhile (dir !== pathRoot) {\n\t\tconst pkgPath = join(dir, 'package.json');\n\t\tif (existsSync(pkgPath)) {\n\t\t\ttry {\n\t\t\t\tconst content = fs.readFileSync(pkgPath, 'utf-8');\n\t\t\t\tconst pkg = JSON.parse(content) as PackageJsonWithWorkspaces;\n\t\t\t\tif (pkg.workspaces) {\n\t\t\t\t\treturn dir;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore parse errors\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\n\treturn null;\n}\n\n/**\n * Extract workspace package patterns from package.json.\n * Supports workspaces: [\"packages/*\"] and workspaces: { packages: [\"packages/*\"] }\n */\nexport function getWorkspacePatterns(workspaceRoot: string): string[] {\n\tconst pkgPath = join(workspaceRoot, 'package.json');\n\tif (!existsSync(pkgPath)) return [];\n\ttry {\n\t\tconst content = fs.readFileSync(pkgPath, 'utf-8');\n\t\tconst pkg = JSON.parse(content) as PackageJsonWithWorkspaces;\n\t\tconst workspaces = pkg.workspaces;\n\t\tif (Array.isArray(workspaces)) {\n\t\t\treturn workspaces;\n\t\t}\n\t\tif (workspaces && typeof workspaces === 'object' && Array.isArray(workspaces.packages)) {\n\t\t\treturn workspaces.packages;\n\t\t}\n\t\treturn [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Expand workspace package globs to absolute directory paths.\n */\nexport function expandWorkspacePackages(workspaceRoot: string, packages: string[]): string[] {\n\tconst dirs: string[] = [];\n\tfor (const pattern of packages) {\n\t\tconst matches = fg.sync(pattern, {\n\t\t\tcwd: workspaceRoot,\n\t\t\tonlyDirectories: true,\n\t\t\tabsolute: true,\n\t\t});\n\t\tdirs.push(...matches);\n\t}\n\treturn [...new Set(dirs)];\n}\n\n/**\n * Read package.json and return name and version. Returns null if invalid.\n */\nfunction readPackageInfo(dir: string): WorkspacePackage | null {\n\tconst pkgPath = join(dir, 'package.json');\n\tif (!existsSync(pkgPath)) return null;\n\ttry {\n\t\tconst content = fs.readFileSync(pkgPath, 'utf-8');\n\t\tconst pkg = JSON.parse(content) as { name?: string; version?: string };\n\t\tif (typeof pkg.name === 'string' && typeof pkg.version === 'string') {\n\t\t\treturn { name: pkg.name, version: pkg.version };\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Build a map of package name -> version from workspace packages.\n */\nexport function buildWorkspacePackageMap(\n\tworkspaceRoot: string,\n\tpackages: string[]\n): Map<string, string> {\n\tconst map = new Map<string, string>();\n\tconst dirs = expandWorkspacePackages(workspaceRoot, packages);\n\n\tfor (const dir of dirs) {\n\t\tconst info = readPackageInfo(dir);\n\t\tif (info) {\n\t\t\tmap.set(info.name, info.version);\n\t\t}\n\t}\n\n\treturn map;\n}\n"
  },
  {
    "path": "packages/bun/tests/bun.test.ts",
    "content": "import type { AbsolutePath } from 'jsrepo/utils';\nimport path from 'pathe';\nimport { describe, expect, it } from 'vitest';\nimport { bun } from '../src/index.js';\n\nconst fixturesDir = path.join(\n\tpath.dirname(new URL(import.meta.url).pathname),\n\t'fixtures',\n\t'bun-workspace'\n) as AbsolutePath;\n\nconst fixturesDirObject = path.join(\n\tpath.dirname(new URL(import.meta.url).pathname),\n\t'fixtures',\n\t'bun-workspace-object'\n) as AbsolutePath;\n\ndescribe('bun resolver', () => {\n\tconst cwd = fixturesDir;\n\tconst cwdObject = fixturesDirObject;\n\tconst resolver = bun();\n\n\tit('should resolve workspace:* to exact version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo', version: 'workspace:*' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'foo', version: '3.0.1' });\n\t});\n\n\tit('should resolve workspace:^ to ^version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo', version: 'workspace:^' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'foo', version: '^3.0.1' });\n\t});\n\n\tit('should resolve workspace:~ to ~version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'bar', version: 'workspace:~' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'bar', version: '~0.1.0' });\n\t});\n\n\tit('should resolve workspace:1.0.0 to literal version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo', version: 'workspace:1.0.0' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'foo', version: '1.0.0' });\n\t});\n\n\tit('should return dep unchanged when version is not workspace', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo', version: '^1.0.0' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual(dep);\n\t});\n\n\tit('should return dep unchanged when version is undefined', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual(dep);\n\t});\n\n\tit('should throw when workspace package not found', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'nonexistent', version: 'workspace:*' };\n\t\tawait expect(resolver(dep, { cwd })).rejects.toThrow(\n\t\t\t/Workspace package \"nonexistent\" not found/\n\t\t);\n\t});\n\n\tit('should support workspaces: [\"packages/*\"] (array)', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo', version: 'workspace:*' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved.version).toBe('3.0.1');\n\t});\n\n\tit('should support workspaces: { packages: [\"packages/*\"] } (object)', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'baz', version: 'workspace:*' };\n\t\tconst resolved = await resolver(dep, { cwd: cwdObject });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'baz', version: '2.0.0' });\n\t});\n\n\tit('should use options.cwd for discovery', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'foo', version: 'workspace:*' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved.version).toBe('3.0.1');\n\t});\n\n\tit('should cache: multiple resolutions with same cwd do not re-parse', async () => {\n\t\tconst dep1 = { ecosystem: 'js' as const, name: 'foo', version: 'workspace:*' };\n\t\tconst dep2 = { ecosystem: 'js' as const, name: 'bar', version: 'workspace:*' };\n\t\tconst r1 = await resolver(dep1, { cwd });\n\t\tconst r2 = await resolver(dep2, { cwd });\n\t\texpect(r1.version).toBe('3.0.1');\n\t\texpect(r2.version).toBe('0.1.0');\n\t});\n});\n"
  },
  {
    "path": "packages/bun/tests/fixtures/bun-workspace/package.json",
    "content": "{\n\t\"name\": \"bun-workspace-root\",\n\t\"version\": \"1.0.0\",\n\t\"workspaces\": [\"packages/*\"]\n}\n"
  },
  {
    "path": "packages/bun/tests/fixtures/bun-workspace/packages/bar/package.json",
    "content": "{\n\t\"name\": \"bar\",\n\t\"version\": \"0.1.0\"\n}\n"
  },
  {
    "path": "packages/bun/tests/fixtures/bun-workspace/packages/foo/package.json",
    "content": "{\n\t\"name\": \"foo\",\n\t\"version\": \"3.0.1\"\n}\n"
  },
  {
    "path": "packages/bun/tests/fixtures/bun-workspace-object/package.json",
    "content": "{\n\t\"name\": \"bun-workspace-root\",\n\t\"version\": \"1.0.0\",\n\t\"workspaces\": {\n\t\t\"packages\": [\"packages/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/bun/tests/fixtures/bun-workspace-object/packages/baz/package.json",
    "content": "{\n\t\"name\": \"baz\",\n\t\"version\": \"2.0.0\"\n}\n"
  },
  {
    "path": "packages/bun/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\", \"tests/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/bun/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/jsrepo/CHANGELOG.md",
    "content": "# jsrepo\n\n## 3.7.0\n### Minor Changes\n\n\n- feat: Add `build.transforms` to config to allow users to transform file content before build ([#786](https://github.com/jsrepojs/jsrepo/pull/786))\n\n## 3.6.2\n### Patch Changes\n\n\n- fix: Loosely validate mcp servers during mcp init to prevent validation errors ([#784](https://github.com/jsrepojs/jsrepo/pull/784))\n\n## 3.6.1\n### Patch Changes\n\n\n- fix: Ensure dependencies can be installed on `init` ([#781](https://github.com/jsrepojs/jsrepo/pull/781))\n\n## 3.6.0\n### Minor Changes\n\n\n- feat: Add `build.remoteDependencyResolver` to allow for resolving `workspace:` and `catalog:` dependencies ([#775](https://github.com/jsrepojs/jsrepo/pull/775))\n\n## 3.5.2\n### Patch Changes\n\n\n- chore: bump deps ([#776](https://github.com/jsrepojs/jsrepo/pull/776))\n\n## 3.5.1\n### Patch Changes\n\n\n- feat: Add `--registry` option to `auth` command ([#772](https://github.com/jsrepojs/jsrepo/pull/772))\n\n## 3.5.0\n### Minor Changes\n\n\n- feat: Support NodeJs subpath imports ([#764](https://github.com/jsrepojs/jsrepo/pull/764))\n\n\n### Patch Changes\n\n\n- fix: Fix an issue where jsrepo would resolve bun imports to the bun.lock file ([`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359))\n\n## 3.4.0\n### Minor Changes\n\n\n- feat: Lifecycle hooks ([#762](https://github.com/jsrepojs/jsrepo/pull/762))\n\n## 3.3.0\n### Minor Changes\n\n\n- feat: Add --with <role> support for optional file roles and allow arbitrary role strings; deprecate legacy --with-* flags. MCP now accepts role filters too. ([#759](https://github.com/jsrepojs/jsrepo/pull/759))\n\n## 3.2.1\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n## 3.2.0\n### Minor Changes\n\n\n- feat: Correctly handle files renamed by transforms ([#747](https://github.com/jsrepojs/jsrepo/pull/747))\n\n\n### Patch Changes\n\n\n- fix: Allow users to not select `optionally-on-init` items ([#747](https://github.com/jsrepojs/jsrepo/pull/747))\n\n## 3.1.1\n### Patch Changes\n\n\n- fix: Fixed a bug with adding plugins ([#743](https://github.com/jsrepojs/jsrepo/pull/743))\n\n## 3.1.0\n### Minor Changes\n\n\n- feat: Support glob syntax for registry item files ([#738](https://github.com/jsrepojs/jsrepo/pull/738))\n\n\n### Patch Changes\n\n\n- feat: improve warning system with typed warnings and `onwarn` callback in config ([#739](https://github.com/jsrepojs/jsrepo/pull/739))\n\n## 3.0.11\n### Patch Changes\n\n\n- fix: environment variable parsing ([#733](https://github.com/jsrepojs/jsrepo/pull/733))\n\n\n- feat: add `headers` to `http` provider to allow for providing custom headers with every request ([#736](https://github.com/jsrepojs/jsrepo/pull/736))\n\n## 3.0.10\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n\n- fix: Do not warn that depenedencies cannot be resolved for binary files ([#729](https://github.com/jsrepojs/jsrepo/pull/729))\n\n## 3.0.9\n### Patch Changes\n\n\n- fix: Improve package manager detection to fallback on the detected user agent ([#725](https://github.com/jsrepojs/jsrepo/pull/725))\n\n## 3.0.8\n### Patch Changes\n\n\n- fix: Fix an issue where dev dependencies weren't installed ([#717](https://github.com/jsrepojs/jsrepo/pull/717))\n\n## 3.0.7\n### Patch Changes\n\n\n- feat: Present the user with the defaultPath provided by the registry when prompting for a path ([#713](https://github.com/jsrepojs/jsrepo/pull/713))\n\n## 3.0.6\n### Patch Changes\n\n\n- fix: Ensure circular dependencies are handled correctly when adding ([#708](https://github.com/jsrepojs/jsrepo/pull/708))\n\n## 3.0.5\n### Patch Changes\n\n\n- fix: only show updates when there are updates ([#703](https://github.com/jsrepojs/jsrepo/pull/703))\n\n\n- fix: normalize file type when getting the path ([#703](https://github.com/jsrepojs/jsrepo/pull/703))\n\n\n- feat: show the names of skipped dependencies ([#703](https://github.com/jsrepojs/jsrepo/pull/703))\n\n## 3.0.4\n### Patch Changes\n\n\n- fix: Ensure items in repository mode are fetched with the `relativePath` ([#701](https://github.com/jsrepojs/jsrepo/pull/701))\n\n## 3.0.3\n### Patch Changes\n\n\n- fix: Ensure that `config mcp` writes the correct file ([`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0))\n\n## 3.0.2\n### Patch Changes\n\n\n- fix: ensure paths are resolved correctly ([#697](https://github.com/jsrepojs/jsrepo/pull/697))\n\n\n- fix: Ensure the user selects at least one option in the add command multiselect ([#697](https://github.com/jsrepojs/jsrepo/pull/697))\n\n## 3.0.1\n### Patch Changes\n\n\n- fix: Ensure that the correct package is installed on config commands ([`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad))\n\n## 3.0.0\n### Patch Changes\n\n\n- v3 initial beta release ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: remove `registry:` prefix from item types ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add `meta` prop to registry items ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: ensure token is provided to fetch methods ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Remove zod reliance for exported types ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: allow for searching components in list when running `add` command without a specific item ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Improve robustness of builds with value optional instead of key optional and better tests ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: peer deps ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Ensure that paths are updated when running add/update commands ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Fix add type ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: make `svelte` and `vue` optional peer dependencies ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Skip and warn the user for dynamic imports with unresolvable syntax ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: `publish` command ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Add `docsLink` to `NoOutputsError` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: ensure dependencies are properly installed ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Fixed an issue where when initializing a registry paths would be unnecessarily added to the config ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: improve error formatting ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- Fix error message stacking in manifest fetch errors. Removes redundant \"Error fetching\" prefixes to improve readability of error messages. ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- chore: bump deps ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add `categories` prop to registry items ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: ensure vscode configuration is correct ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Create config at `.mts` only if `type: \"module\"` is not set in package.json ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add `optionally-on-init` add option ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: ensure dependencies are still installed even if file content is the same ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- update `transform` api to allow for renaming files ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- breaking: rename `contents` -> `content` for shadcn compatibility ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: transform imports from shadcn registries ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: make peer deps less agressive ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Fixed an issue where files with multiple dots i.e. foo.bar.ts were not resolved correctly ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- breaking: Rename `remoteDependencies` -> `dependencies` and `devDependencies` for improved shadcn compatibility ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: ensure dev dependencies are added ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: `config mcp` support for google antigravity ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- Fix bundling issues ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Improve error message when registry item cannot be found ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- shadcn-compat: add `title` to registry items config ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Improve errors for invalid imports. ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Ensure items are added to the correct paths in the users project ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Exit with the correct code for `publish` and `build` commands ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: `publish` command ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Ensure registry dependencies exist ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: false positive for unresolvable syntax when dynamic imports are backquoted but the template is unused ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: Prevent duplicate dependencies in build result ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add support for `index` items ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- refactor the way that files are added to users projects ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- breaking: Rename manifest file from `jsrepo.json` -> `registry.json` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.29\n### Patch Changes\n\n\n- fix: Ensure that paths are updated when running add/update commands ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.28\n### Patch Changes\n\n\n- fix: Fix add type ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.27\n### Patch Changes\n\n\n- fix: ensure vscode configuration is correct ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.26\n### Patch Changes\n\n\n- Fix error message stacking in manifest fetch errors. Removes redundant \"Error fetching\" prefixes to improve readability of error messages. ([#686](https://github.com/jsrepojs/jsrepo/pull/686))\n\n## 3.0.0-beta.25\n### Patch Changes\n\n\n- feat: transform imports from shadcn registries ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: `config mcp` support for google antigravity ([#682](https://github.com/jsrepojs/jsrepo/pull/682))\n\n## 3.0.0-beta.24\n### Patch Changes\n\n\n- fix: Fixed an issue where when initializing a registry paths would be unnecessarily added to the config ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: improve error formatting ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add support for `index` items ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.23\n### Patch Changes\n\n\n- feat: add `meta` prop to registry items ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add `categories` prop to registry items ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.22\n### Patch Changes\n\n\n- fix: Ensure items are added to the correct paths in the users project ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.21\n### Patch Changes\n\n\n- feat: allow for searching components in list when running `add` command without a specific item ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.20\n### Patch Changes\n\n\n- fix: false positive for unresolvable syntax when dynamic imports are backquoted but the template is unused ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.19\n### Patch Changes\n\n\n- fix: Prevent duplicate dependencies in build result ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.18\n### Patch Changes\n\n\n- fix: peer deps ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- refactor the way that files are added to users projects ([#668](https://github.com/jsrepojs/jsrepo/pull/668))\n\n## 3.0.0-beta.17\n### Patch Changes\n\n\n- fix: Skip and warn the user for dynamic imports with unresolvable syntax ([#666](https://github.com/jsrepojs/jsrepo/pull/666))\n\n\n- fix: Improve errors for invalid imports. ([#666](https://github.com/jsrepojs/jsrepo/pull/666))\n\n## 3.0.0-beta.16\n### Patch Changes\n\n\n- fix: Improve robustness of builds with value optional instead of key optional and better tests ([#664](https://github.com/jsrepojs/jsrepo/pull/664))\n\n## 3.0.0-beta.15\n### Patch Changes\n\n\n- fix: ensure dependencies are properly installed ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.14\n### Patch Changes\n\n\n- fix: ensure dependencies are still installed even if file content is the same ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.13\n### Patch Changes\n\n\n- fix: make peer deps less agressive ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: ensure dev dependencies are added ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.12\n### Patch Changes\n\n\n- fix: Fixed an issue where files with multiple dots i.e. foo.bar.ts were not resolved correctly ([#659](https://github.com/jsrepojs/jsrepo/pull/659))\n\n## 3.0.0-beta.11\n### Patch Changes\n\n\n- fix: Ensure registry dependencies exist ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.10\n### Patch Changes\n\n\n- fix: Create config at `.mts` only if `type: \"module\"` is not set in package.json ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.9\n### Patch Changes\n\n\n- fix: Add `docsLink` to `NoOutputsError` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.8\n### Patch Changes\n\n\n- shadcn-compat: add `title` to registry items config ([#653](https://github.com/jsrepojs/jsrepo/pull/653))\n\n## 3.0.0-beta.7\n### Patch Changes\n\n\n- update `transform` api to allow for renaming files ([#651](https://github.com/jsrepojs/jsrepo/pull/651))\n\n## 3.0.0-beta.6\n### Patch Changes\n\n\n- fix: Exit with the correct code for `publish` and `build` commands ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.5\n### Patch Changes\n\n\n- feat: add `optionally-on-init` add option ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 3.0.0-beta.4\n### Patch Changes\n\n\n- feat: remove `registry:` prefix from item types ([#645](https://github.com/jsrepojs/jsrepo/pull/645))\n\n\n- feat: `publish` command ([#645](https://github.com/jsrepojs/jsrepo/pull/645))\n\n\n- breaking: rename `contents` -> `content` for shadcn compatibility ([#645](https://github.com/jsrepojs/jsrepo/pull/645))\n\n\n- breaking: Rename `remoteDependencies` -> `dependencies` and `devDependencies` for improved shadcn compatibility ([#645](https://github.com/jsrepojs/jsrepo/pull/645))\n\n\n- feat: `publish` command ([#645](https://github.com/jsrepojs/jsrepo/pull/645))\n\n## 3.0.0-beta.3\n### Patch Changes\n\n\n- fix: ensure token is provided to fetch methods ([#643](https://github.com/jsrepojs/jsrepo/pull/643))\n\n\n- fix: Improve error message when registry item cannot be found ([#643](https://github.com/jsrepojs/jsrepo/pull/643))\n\n## 3.0.0-beta.2\n### Patch Changes\n\n\n- fix: Remove zod reliance for exported types ([#641](https://github.com/jsrepojs/jsrepo/pull/641))\n\n\n- chore: bump deps ([#641](https://github.com/jsrepojs/jsrepo/pull/641))\n\n## 3.0.0-beta.1\n### Patch Changes\n\n\n- fix: make `svelte` and `vue` optional peer dependencies ([#640](https://github.com/jsrepojs/jsrepo/pull/640))\n\n\n- Fix bundling issues ([#640](https://github.com/jsrepojs/jsrepo/pull/640))\n\n\n- breaking: Rename manifest file from `jsrepo.json` -> `registry.json` ([#637](https://github.com/jsrepojs/jsrepo/pull/637))\n\n## 3.0.0-beta.0\n### Patch Changes\n\n\n- v3 initial beta release ([#635](https://github.com/jsrepojs/jsrepo/pull/635))\n"
  },
  {
    "path": "packages/jsrepo/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 jsrepo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/jsrepo/README.md",
    "content": "<img width=\"100%\" alt=\"CleanShot 2025-11-24 at 09 35 57\" src=\"https://github.com/user-attachments/assets/61899098-fbc9-4263-972f-aacdfe0976de\" />\n\n[![npm version](https://flat.badgen.net/npm/v/jsrepo?color=yellow)](https://npmjs.com/package/jsrepo)\n[![npm downloads](https://flat.badgen.net/npm/dm/jsrepo?color=yellow)](https://npmjs.com/package/jsrepo)\n\n# jsrepo \n\nThe modern registry toolchain. Distribute your code in any language anywhere with one CLI. \n\n## Get Started\n\n```sh\nnpx jsrepo init\n```"
  },
  {
    "path": "packages/jsrepo/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/package.json",
    "content": "{\n\t\"name\": \"jsrepo\",\n\t\"description\": \"A CLI to add shared code from remote repositories.\",\n\t\"version\": \"3.7.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"repo\",\n\t\t\"cli\",\n\t\t\"svelte\",\n\t\t\"vue\",\n\t\t\"typescript\",\n\t\t\"javascript\",\n\t\t\"shadcn\",\n\t\t\"registry\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/api/index.d.mts\",\n\t\t\t\"default\": \"./dist/api/index.mjs\"\n\t\t},\n\t\t\"./providers\": {\n\t\t\t\"types\": \"./dist/api/providers.d.mts\",\n\t\t\t\"default\": \"./dist/api/providers.mjs\"\n\t\t},\n\t\t\"./langs\": {\n\t\t\t\"types\": \"./dist/api/langs/index.d.mts\",\n\t\t\t\"default\": \"./dist/api/langs/index.mjs\"\n\t\t},\n\t\t\"./langs/*\": {\n\t\t\t\"types\": \"./dist/api/langs/*.d.mts\",\n\t\t\t\"default\": \"./dist/api/langs/*.mjs\"\n\t\t},\n\t\t\"./outputs\": {\n\t\t\t\"types\": \"./dist/api/outputs.d.mts\",\n\t\t\t\"default\": \"./dist/api/outputs.mjs\"\n\t\t},\n\t\t\"./config\": {\n\t\t\t\"types\": \"./dist/api/config.d.mts\",\n\t\t\t\"default\": \"./dist/api/config.mjs\"\n\t\t},\n\t\t\"./errors\": {\n\t\t\t\"types\": \"./dist/api/errors.d.mts\",\n\t\t\t\"default\": \"./dist/api/errors.mjs\"\n\t\t},\n\t\t\"./utils\": {\n\t\t\t\"types\": \"./dist/api/utils.d.mts\",\n\t\t\t\"default\": \"./dist/api/utils.mjs\"\n\t\t},\n\t\t\"./warnings\": {\n\t\t\t\"types\": \"./dist/api/warnings.d.mts\",\n\t\t\t\"default\": \"./dist/api/warnings.mjs\"\n\t\t}\n\t},\n\t\"bin\": \"./dist/bin.mjs\",\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"start\": \"node ./dist/bin.mjs\",\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"test\": \"vitest\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 1250000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"dependencies\": {\n\t\t\"commander\": \"catalog:\",\n\t\t\"oxc-parser\": \"^0.114.0\",\n\t\t\"unconfig\": \"^7.5.0\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"svelte\": \"^5.0.0\",\n\t\t\"vue\": \"^3.0.0\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"vue\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"svelte\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"@clack/prompts\": \"1.0.1\",\n\t\t\"@types/semver\": \"^7.7.1\",\n\t\t\"@types/tar-stream\": \"^3.1.4\",\n\t\t\"conf\": \"^15.1.0\",\n\t\t\"css-dependency\": \"^0.0.3\",\n\t\t\"dedent\": \"catalog:\",\n\t\t\"diff\": \"^8.0.3\",\n\t\t\"escape-string-regexp\": \"^5.0.0\",\n\t\t\"fast-glob\": \"^3.3.3\",\n\t\t\"get-tsconfig\": \"^4.13.6\",\n\t\t\"is-unicode-supported\": \"^2.1.0\",\n\t\t\"magic-string\": \"^0.30.21\",\n\t\t\"nevereverthrow\": \"^0.3.0\",\n\t\t\"node-machine-id\": \"^1.1.12\",\n\t\t\"package-manager-detector\": \"^1.6.0\",\n\t\t\"parse5\": \"^8.0.0\",\n\t\t\"pathe\": \"^2.0.3\",\n\t\t\"picocolors\": \"catalog:\",\n\t\t\"semver\": \"^7.7.4\",\n\t\t\"tar-stream\": \"^3.1.7\",\n\t\t\"tinyexec\": \"^1.0.2\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"vite\": \"^7.3.1\",\n\t\t\"vitest\": \"catalog:\",\n\t\t\"zod\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/api/config.ts",
    "content": "export type { RemoteDependency } from '@/utils/build';\nexport {\n\ttype Config,\n\tdefineConfig,\n\ttype RegistryConfig,\n\tRegistryFileRoles as OptionallyInstalledRegistryTypes,\n\ttype RegistryItem,\n\ttype RegistryItemAdd,\n\ttype RegistryItemFile,\n\ttype RegistryItemType,\n\ttype RemoteDependencyResolver,\n\ttype RemoteDependencyResolverOptions,\n\ttype Transform,\n\ttype TransformOptions,\n} from '@/utils/config';\nexport { loadConfigSearch } from '@/utils/config/utils';\nexport type {\n\tAfterArgs,\n\tAfterHook,\n\tBeforeArgs,\n\tBeforeHook,\n\tHook,\n\tHookFn,\n\tInferHookArgs,\n} from '@/utils/hooks';\n"
  },
  {
    "path": "packages/jsrepo/src/api/errors.ts",
    "content": "export * from '@/utils/errors';\n"
  },
  {
    "path": "packages/jsrepo/src/api/index.ts",
    "content": "export * from '@/api/config';\nexport * from '@/api/errors';\nexport * from '@/api/utils';\nexport * from '@/api/warnings';\nexport * from '@/langs';\nexport * from '@/outputs';\nexport * from '@/providers';\n"
  },
  {
    "path": "packages/jsrepo/src/api/langs/index.ts",
    "content": "export * from '@/langs';\n"
  },
  {
    "path": "packages/jsrepo/src/api/langs/js.ts",
    "content": "export { getImports, installDependencies, js, resolveImports, transformImports } from '@/langs/js';\n"
  },
  {
    "path": "packages/jsrepo/src/api/outputs.ts",
    "content": "export * from '@/outputs';\n"
  },
  {
    "path": "packages/jsrepo/src/api/providers.ts",
    "content": "export * from '@/providers';\n"
  },
  {
    "path": "packages/jsrepo/src/api/utils.ts",
    "content": "export {\n\tfetchAllResolvedItems,\n\tgetPathsForItems,\n\tnormalizeItemTypeForPath,\n\tparseWantedItems,\n\tprepareUpdates,\n\ttype RegistryItemWithContent,\n\tresolveAndFetchAllItems,\n\tresolveRegistries,\n\tresolveTree,\n\tresolveWantedItems,\n\tupdateFiles,\n} from '@/utils/add';\nexport { joinAbsolute } from '@/utils/path';\nexport {\n\tdetectPackageManager,\n\tpromptAddEnvVars,\n\tpromptInstallDependenciesByEcosystem,\n} from '@/utils/prompts';\nexport { resolveWithRoles } from '@/utils/roles';\nexport type { AbsolutePath, ItemRelativePath } from '@/utils/types';\n"
  },
  {
    "path": "packages/jsrepo/src/api/warnings.ts",
    "content": "export * from '@/utils/warnings';\n"
  },
  {
    "path": "packages/jsrepo/src/bin.ts",
    "content": "#!/usr/bin/env node\n\nimport { cli } from '@/cli';\n\ncli.parse();\n"
  },
  {
    "path": "packages/jsrepo/src/cli.ts",
    "content": "import { program } from 'commander';\nimport pkg from '@/../package.json';\nimport * as commands from '@/commands';\n\nconst cli = program\n\t.name(pkg.name)\n\t.description(pkg.description)\n\t.version(pkg.version)\n\t.addCommand(commands.add)\n\t.addCommand(commands.auth)\n\t.addCommand(commands.build)\n\t.addCommand(commands.config)\n\t.addCommand(commands.init)\n\t.addCommand(commands.publish)\n\t.addCommand(commands.update);\n\nexport { cli };\n"
  },
  {
    "path": "packages/jsrepo/src/commands/add.ts",
    "content": "import {\n\ttype AutocompleteMultiSelectOptions,\n\tautocompleteMultiselect,\n\tcancel,\n\tisCancel,\n} from '@clack/prompts';\nimport { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport { DEFAULT_PROVIDERS } from '@/providers';\nimport {\n\tgetPathsForItems,\n\ttype ItemDistributed,\n\ttype ItemRepository,\n\tparseWantedItems,\n\tprepareUpdates,\n\ttype ResolvedWantedItem,\n\tresolveAndFetchAllItems,\n\tresolveRegistries,\n\tresolveWantedItems,\n\tupdateFiles,\n} from '@/utils/add';\nimport type { RemoteDependency } from '@/utils/build';\nimport type { Config } from '@/utils/config';\nimport { updateConfigPaths } from '@/utils/config/mods/update-paths';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, InvalidRegistryError, RegistryNotProvidedError } from '@/utils/errors';\nimport { readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport {\n\tinitLogging,\n\tintro,\n\toutro,\n\tpromptAddEnvVars,\n\tpromptInstallDependenciesByEcosystem,\n} from '@/utils/prompts';\nimport { resolveWithRoles } from '@/utils/roles';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n\tall: z.boolean(),\n\toverwrite: z.boolean(),\n\tverbose: z.boolean(),\n\tregistry: z.string().optional(),\n\twith: z.array(z.string()).default([]),\n\twithExamples: z.boolean(),\n\twithDocs: z.boolean(),\n\twithTests: z.boolean(),\n\texpand: z.boolean(),\n\tmaxUnchanged: z.number(),\n});\n\nexport type AddOptions = z.infer<typeof schema>;\n\nexport const add = new Command('add')\n\t.description('Add items to your project.')\n\t.argument(\n\t\t'[items...]',\n\t\t'Names of the items you want to add to your project. ex: (utils/math, github/ieedan/std/utils/math)'\n\t)\n\t.option('--registry <registry>', 'The registry to add items from.', undefined)\n\t.option('--all', 'Add all items from every registry.', false)\n\t.option('--with <roles...>', 'Include files with the given roles.')\n\t.option('--with-examples', 'Deprecated. Use `--with example`.', false)\n\t.option('--with-docs', 'Deprecated. Use `--with doc`.', false)\n\t.option('--with-tests', 'Deprecated. Use `--with test`.', false)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.addOption(commonOptions.verbose)\n\t.addOption(commonOptions.overwrite)\n\t.addOption(commonOptions.expand)\n\t.addOption(commonOptions.maxUnchanged)\n\t.action(async (blockNames, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: !options.yes,\n\t\t});\n\n\t\tconst config = configResult?.config ?? {};\n\t\tconst cwd = configResult ? (path.dirname(configResult.path) as AbsolutePath) : options.cwd;\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig as Config,\n\t\t\t{ command: 'add', options: { ...options, cwd } },\n\t\t\t{ cwd, yes: options.yes }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(\n\t\t\trunAdd(\n\t\t\t\tblockNames,\n\t\t\t\t// this way if the config is found in a higher directory we base everything off of that directory\n\t\t\t\t{\n\t\t\t\t\t...options,\n\t\t\t\t\tcwd,\n\t\t\t\t},\n\t\t\t\tconfigResult\n\t\t\t)\n\t\t);\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig as Config,\n\t\t\t{ command: 'add', options: { ...options, cwd }, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport type AddCommandResult = {\n\titems: (ItemRepository | ItemDistributed)[];\n\tupdatedFiles: string[];\n\tupdatedDependencies: {\n\t\tinstalled: boolean;\n\t\tdependencies: RemoteDependency[];\n\t};\n\tupdatedEnvVars: Record<string, string> | undefined;\n\tupdatedPaths: Config['paths'] | undefined;\n};\n\nexport async function runAdd(\n\titemsArg: string[],\n\toptions: AddOptions,\n\tconfigResult: { path: AbsolutePath; config: Config } | null\n): Promise<Result<AddCommandResult, CLIError>> {\n\tconst { verbose: _, spinner } = initLogging({ options });\n\n\tconst config = configResult?.config;\n\n\t// this is to support zero config adds\n\tconst providers = config?.providers ?? DEFAULT_PROVIDERS;\n\tconst registries = options.registry ? [options.registry] : (config?.registries ?? []);\n\n\tlet resolvedWantedItems: ResolvedWantedItem[];\n\n\tif (itemsArg.length === 0 || options.all) {\n\t\t// in the case that --all was provided we treat items as registry urls\n\t\tif (options.all && itemsArg.length > 0) {\n\t\t\t// we ensure that all registries are valid\n\t\t\tfor (const item of itemsArg) {\n\t\t\t\tconst foundProvider = providers.some((p) => p.matches(item));\n\t\t\t\tif (!foundProvider) {\n\t\t\t\t\terror(new InvalidRegistryError(item));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst availableRegistries = itemsArg.length > 0 ? itemsArg : registries;\n\t\t// we can't add from 0 registries\n\t\tif (availableRegistries.length === 0) return err(new RegistryNotProvidedError());\n\n\t\tspinner.start(\n\t\t\t`Retrieving manifest${registries.length > 1 ? 's' : ''} from ${pc.cyan(registries.join(', '))}`\n\t\t);\n\n\t\tconst resolvedRegistriesResult = await resolveRegistries(availableRegistries, {\n\t\t\tcwd: options.cwd,\n\t\t\tproviders,\n\t\t});\n\n\t\tif (resolvedRegistriesResult.isErr()) {\n\t\t\tspinner.stop('Failed to retrieve manifests');\n\t\t\treturn err(resolvedRegistriesResult.error);\n\t\t}\n\t\tspinner.stop(\n\t\t\t`Retrieved manifest${registries.length > 1 ? 's' : ''} from ${pc.cyan(registries.join(', '))}`\n\t\t);\n\t\tconst resolvedRegistries = resolvedRegistriesResult.value;\n\n\t\tconst possibleItems = Array.from(resolvedRegistries.entries()).flatMap(([_, registry]) => {\n\t\t\treturn registry.manifest.items\n\t\t\t\t.filter((item) => item)\n\t\t\t\t.map((item) => ({\n\t\t\t\t\titem,\n\t\t\t\t\tregistry,\n\t\t\t\t}));\n\t\t});\n\n\t\tif (!options.all) {\n\t\t\tconst multiSelectOptions: AutocompleteMultiSelectOptions<`${string}/${string}`>['options'] =\n\t\t\t\tpossibleItems\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(item) =>\n\t\t\t\t\t\t\t(item.item.add ?? 'when-added') === 'when-added' &&\n\t\t\t\t\t\t\titem.item.name !== 'index'\n\t\t\t\t\t)\n\t\t\t\t\t.map((item) => {\n\t\t\t\t\t\tconst value = `${item.registry.url}/${item.item.name}` as const;\n\t\t\t\t\t\tconst label =\n\t\t\t\t\t\t\tresolvedRegistries.size > 1\n\t\t\t\t\t\t\t\t? `${pc.cyan(item.registry.url)}/${item.item.name}`\n\t\t\t\t\t\t\t\t: item.item.name;\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tlabel: label,\n\t\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\t\thint:\n\t\t\t\t\t\t\t\t(item.item.description?.length ??\n\t\t\t\t\t\t\t\t0 >= process.stdout.columns - label.length - 12)\n\t\t\t\t\t\t\t\t\t? `${item.item.description\n\t\t\t\t\t\t\t\t\t\t\t?.slice(0, process.stdout.columns - label.length - 12)\n\t\t\t\t\t\t\t\t\t\t\t.trim()}...`\n\t\t\t\t\t\t\t\t\t: item.item.description,\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\n\t\t\tconst userSelections = await autocompleteMultiselect({\n\t\t\t\tmessage: 'Which items would you like to add?',\n\t\t\t\toptions: multiSelectOptions,\n\t\t\t\tmaxItems: process.stdout.rows - 10,\n\t\t\t\trequired: true,\n\t\t\t});\n\n\t\t\tif (isCancel(userSelections)) {\n\t\t\t\tcancel('Canceled!');\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\n\t\t\tresolvedWantedItems = possibleItems\n\t\t\t\t.filter((item) => userSelections.includes(`${item.registry.url}/${item.item.name}`))\n\t\t\t\t.map((item) => ({\n\t\t\t\t\tregistry: item.registry,\n\t\t\t\t\titem: item.item,\n\t\t\t\t}));\n\t\t} else {\n\t\t\t// select all items\n\t\t\tresolvedWantedItems = possibleItems;\n\t\t}\n\t} else {\n\t\tconst parsedWantedItemsResult = parseWantedItems(itemsArg, {\n\t\t\tproviders,\n\t\t\tregistries: registries,\n\t\t});\n\t\tif (parsedWantedItemsResult.isErr()) return err(parsedWantedItemsResult.error);\n\t\tconst { wantedItems, neededRegistries } = parsedWantedItemsResult.value;\n\n\t\tspinner.start(\n\t\t\t`Retrieving manifest${neededRegistries.length > 1 ? 's' : ''} from ${pc.cyan(neededRegistries.join(', '))}`\n\t\t);\n\n\t\tconst resolvedRegistriesResult = await resolveRegistries(neededRegistries, {\n\t\t\tcwd: options.cwd,\n\t\t\tproviders,\n\t\t});\n\n\t\tif (resolvedRegistriesResult.isErr()) {\n\t\t\tspinner.stop('Failed to retrieve manifests');\n\t\t\treturn err(resolvedRegistriesResult.error);\n\t\t}\n\t\tspinner.stop(\n\t\t\t`Retrieved manifest${neededRegistries.length > 1 ? 's' : ''} from ${pc.cyan(neededRegistries.join(', '))}`\n\t\t);\n\t\tconst resolvedRegistries = resolvedRegistriesResult.value;\n\n\t\tconst resolvedWantedItemsResult = await resolveWantedItems(wantedItems, {\n\t\t\tresolvedRegistries,\n\t\t\tnonInteractive: options.yes,\n\t\t});\n\t\tif (resolvedWantedItemsResult.isErr()) return err(resolvedWantedItemsResult.error);\n\t\tresolvedWantedItems = resolvedWantedItemsResult.value;\n\t}\n\n\tspinner.start(\n\t\t`Fetching ${pc.cyan(resolvedWantedItems.map((item) => item.item.name).join(', '))}...`\n\t);\n\n\tconst withRoles = resolveWithRoles(options);\n\n\tconst itemsResult = await resolveAndFetchAllItems(resolvedWantedItems, {\n\t\twithRoles,\n\t});\n\tif (itemsResult.isErr()) {\n\t\tspinner.stop('Failed to fetch items');\n\t\treturn err(itemsResult.error);\n\t}\n\tspinner.stop(\n\t\t`Fetched ${pc.cyan(resolvedWantedItems.map((item) => item.item.name).join(', '))}`\n\t);\n\tconst items = itemsResult.value;\n\n\tconst itemPathsResult = await getPathsForItems({ items, config, options });\n\tif (itemPathsResult.isErr()) return err(itemPathsResult.error);\n\tconst { itemPaths, updatedPaths } = itemPathsResult.value;\n\n\tconst prepareUpdatesResult = await prepareUpdates({\n\t\tconfigResult,\n\t\toptions: { cwd: options.cwd, yes: options.yes, withRoles },\n\t\titemPaths,\n\t\titems,\n\t});\n\tif (prepareUpdatesResult.isErr()) return err(prepareUpdatesResult.error);\n\tconst { neededDependencies, neededEnvVars, neededFiles } = prepareUpdatesResult.value;\n\n\tconst updatedFilesResult = await updateFiles({ files: neededFiles, options });\n\tif (updatedFilesResult.isErr()) return err(updatedFilesResult.error);\n\tconst updatedFiles = updatedFilesResult.value;\n\n\tif (configResult && updatedPaths) {\n\t\tconst configCodeResult = readFileSync(configResult.path);\n\t\tif (configCodeResult.isErr()) return err(configCodeResult.error);\n\t\tconst configCode = configCodeResult.value;\n\t\tconst updatedConfigCode = await updateConfigPaths(config?.paths ?? updatedPaths, {\n\t\t\tconfig: { path: configResult.path, code: configCode },\n\t\t});\n\t\tif (updatedConfigCode.isErr()) return err(updatedConfigCode.error);\n\t\tconst writeResult = writeFileSync(configResult.path, updatedConfigCode.value);\n\t\tif (writeResult.isErr()) return err(writeResult.error);\n\t}\n\n\tlet updatedEnvVars: Record<string, string> | undefined;\n\tif (neededEnvVars) {\n\t\tconst promptAddEnvVarsResult = await promptAddEnvVars(neededEnvVars, { options });\n\t\tif (promptAddEnvVarsResult.isErr()) return err(promptAddEnvVarsResult.error);\n\t\tupdatedEnvVars = promptAddEnvVarsResult.value;\n\t}\n\n\tconst updatedDependencies = await promptInstallDependenciesByEcosystem(neededDependencies, {\n\t\toptions,\n\t\tconfig,\n\t});\n\n\treturn ok({\n\t\titems,\n\t\tupdatedDependencies,\n\t\tupdatedEnvVars,\n\t\tupdatedFiles,\n\t\tupdatedPaths,\n\t});\n}\n\nfunction formatResult(result: AddCommandResult): string {\n\tconst parts: string[] = [\n\t\t`Added ${pc.cyan(result.items.map((item) => item.name).join(', '))} to your project.`,\n\t];\n\n\tif (result.updatedFiles.length > 0) {\n\t\tparts.push(\n\t\t\t`    Updated ${pc.green(result.updatedFiles.length)} ${\n\t\t\t\tresult.updatedFiles.length === 1 ? 'file' : 'files'\n\t\t\t}.`\n\t\t);\n\t}\n\n\tif (result.updatedPaths && Object.keys(result.updatedPaths).length > 0) {\n\t\tparts.push(\n\t\t\t`    Updated ${pc.green(Object.keys(result.updatedPaths).length)} ${\n\t\t\t\tObject.keys(result.updatedPaths).length === 1 ? 'path' : 'paths'\n\t\t\t}.`\n\t\t);\n\t}\n\n\tif (result.updatedDependencies.dependencies.length > 0) {\n\t\tif (result.updatedDependencies.installed) {\n\t\t\tparts.push(\n\t\t\t\t`    Installed ${pc.green(result.updatedDependencies.dependencies.length)} ${\n\t\t\t\t\tresult.updatedDependencies.dependencies.length === 1\n\t\t\t\t\t\t? 'dependency'\n\t\t\t\t\t\t: 'dependencies'\n\t\t\t\t}.`\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(\n\t\t\t\t`    Skipped installation of ${pc.cyan(\n\t\t\t\t\tresult.updatedDependencies.dependencies\n\t\t\t\t\t\t.map((dep) => `${dep.name}${dep.version ? `@${dep.version}` : ''}`)\n\t\t\t\t\t\t.join(', ')\n\t\t\t\t)}.`\n\t\t\t);\n\t\t}\n\t}\n\n\tif (result.updatedEnvVars && Object.keys(result.updatedEnvVars).length > 0) {\n\t\tparts.push(\n\t\t\t`    Updated ${pc.green(Object.keys(result.updatedEnvVars).length)} ${\n\t\t\t\tObject.keys(result.updatedEnvVars).length === 1\n\t\t\t\t\t? 'environment variable'\n\t\t\t\t\t: 'environment variables'\n\t\t\t}.`\n\t\t);\n\t}\n\n\treturn parts.join('\\n');\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/auth.ts",
    "content": "import {\n\tcancel,\n\tconfirm,\n\tgroupMultiselect,\n\tisCancel,\n\tlog,\n\tmultiselect,\n\tpassword,\n\tselect,\n\tspinner,\n\ttext,\n} from '@clack/prompts';\nimport { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport { DEFAULT_PROVIDERS, type ProviderFactory } from '@/providers';\nimport type { Config } from '@/utils/config';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, JsrepoError, NoProviderFoundError } from '@/utils/errors';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { initLogging, intro, outro } from '@/utils/prompts';\nimport { TokenManager } from '@/utils/token-manager';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tregistry: z.string().optional(),\n\ttoken: z.string().optional(),\n\tlogout: z.boolean(),\n\tverbose: z.boolean(),\n});\n\nexport type AuthOptions = z.infer<typeof schema>;\n\nexport const auth = new Command('auth')\n\t.description('Authenticate to a provider or registry.')\n\t.argument('[provider]', 'The provider to authenticate to.')\n\t.option('--logout', 'Execute the logout flow.', false)\n\t.option('--registry <registry>', 'The registry to authenticate to.')\n\t.option('--token <token>', 'The token to use for authenticating to this provider.')\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.verbose)\n\t.action(async (provider, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\tconst config = configResult?.config ?? ({} as Config);\n\t\tconst cwd = configResult\n\t\t\t? (path.dirname(configResult.path) as import('@/utils/types').AbsolutePath)\n\t\t\t: options.cwd;\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'auth', options: { ...options, cwd } },\n\t\t\t{ cwd, yes: false }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(runAuth(provider, options, configResult?.config));\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'auth', options: { ...options, cwd }, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport type AuthCommandResult = {\n\ttype: 'logout' | 'login';\n\tprovider: string;\n\tregistry?: string;\n};\n\nexport async function runAuth(\n\tproviderArg: string,\n\toptions: AuthOptions,\n\tconfig: Config | undefined\n): Promise<Result<AuthCommandResult, CLIError>> {\n\tconst { verbose: _verbose, spinner: _spinner } = initLogging({ options });\n\n\tconst providers = (config?.providers ?? DEFAULT_PROVIDERS).filter((p) => p.auth !== undefined);\n\n\tlet provider: ProviderFactory | undefined;\n\tif (!providerArg) {\n\t\tif (options.registry) {\n\t\t\tprovider = providers.find((p) => p.matches(options.registry!));\n\t\t\tif (!provider) return err(new NoProviderFoundError(options.registry));\n\t\t} else {\n\t\t\tconst providerSelection = await select({\n\t\t\t\tmessage: 'Select a provider to authenticate to.',\n\t\t\t\toptions: providers.map((p) => ({\n\t\t\t\t\tlabel: p.name,\n\t\t\t\t\tvalue: p.name,\n\t\t\t\t})),\n\t\t\t});\n\n\t\t\tif (isCancel(providerSelection)) {\n\t\t\t\tcancel('Canceled!');\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\n\t\t\t// we know it's valid because we checked for cancel\n\t\t\tprovider = providers.find((p) => p.name === providerSelection)!;\n\t\t}\n\t} else {\n\t\tprovider = providers.find((p) => p.name === providerArg);\n\t\tif (!provider) return err(new NoProviderFoundError(providerArg));\n\t}\n\n\tif (options.registry && !provider.matches(options.registry)) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Registry ${pc.bold(options.registry)} cannot be parsed by provider ${pc.bold(provider.name)}.`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: `Pass a registry that matches ${pc.bold(provider.name)} or omit the provider argument.`,\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n\n\tif (options.logout) return await logout(provider, options);\n\n\treturn await login(provider, { config, options });\n}\n\nasync function logout(\n\tprovider: ProviderFactory,\n\toptions: AuthOptions\n): Promise<Result<AuthCommandResult, CLIError>> {\n\tconst tokenManager = new TokenManager();\n\tif (provider.auth!.tokenStoredFor === 'registry') {\n\t\tif (options.registry) {\n\t\t\ttokenManager.delete(provider, options.registry);\n\t\t\treturn ok({ type: 'logout', provider: provider.name, registry: options.registry });\n\t\t}\n\n\t\tconst registryTokens = tokenManager.getProviderRegistryTokens(provider);\n\t\tif (Object.keys(registryTokens).length === 0) {\n\t\t\treturn ok({ type: 'logout', provider: provider.name, registry: undefined });\n\t\t}\n\n\t\tconst registrySelection = await select({\n\t\t\tmessage: 'Select a registry to logout of.',\n\t\t\toptions: Object.keys(registryTokens).map((registry) => ({\n\t\t\t\tlabel: registry,\n\t\t\t\tvalue: registry,\n\t\t\t})),\n\t\t});\n\n\t\tif (isCancel(registrySelection)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\ttokenManager.delete(provider, registrySelection);\n\t\treturn ok({ type: 'logout', provider: provider.name, registry: registrySelection });\n\t} else {\n\t\ttokenManager.delete(provider, undefined);\n\t\treturn ok({ type: 'logout', provider: provider.name, registry: undefined });\n\t}\n}\n\nasync function login(\n\tprovider: ProviderFactory,\n\t{ config, options }: { config: Config | undefined; options: AuthOptions }\n): Promise<Result<AuthCommandResult, CLIError>> {\n\tconst tokenManager = new TokenManager();\n\tif (provider.auth!.tokenStoredFor === 'registry') {\n\t\tconst registries = config?.registries ?? [];\n\t\tlet registry = options.registry;\n\t\tif (!registry) {\n\t\t\tif (registries.length === 0) {\n\t\t\t\tconst registrySelection = await text({\n\t\t\t\t\tmessage: 'Enter the name of the registry to authenticate to.',\n\t\t\t\t\tvalidate(value) {\n\t\t\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t\t\t\tif (!provider.matches(value))\n\t\t\t\t\t\t\treturn 'Registry cannot be parsed by this provider';\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(registrySelection)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\tregistry = registrySelection;\n\t\t\t} else {\n\t\t\t\tconst registrySelection = await select({\n\t\t\t\t\tmessage: 'Select a registry to authenticate to.',\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t...registries.map((registry) => ({\n\t\t\t\t\t\t\tlabel: registry,\n\t\t\t\t\t\t\tvalue: registry,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: 'Other',\n\t\t\t\t\t\t\tvalue: 'other',\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(registrySelection)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\tif (registrySelection === 'other') {\n\t\t\t\t\tconst registrySelection = await text({\n\t\t\t\t\t\tmessage: 'Enter the name of the registry to authenticate to.',\n\t\t\t\t\t\tvalidate(value) {\n\t\t\t\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t\t\t\t\tif (!provider.matches(value))\n\t\t\t\t\t\t\t\treturn 'Registry cannot be parsed by this provider';\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tif (isCancel(registrySelection)) {\n\t\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\t\tprocess.exit(0);\n\t\t\t\t\t}\n\n\t\t\t\t\tregistry = registrySelection;\n\t\t\t\t} else {\n\t\t\t\t\tregistry = registrySelection;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet token = options.token;\n\t\tif (!token) {\n\t\t\tif (provider.auth!.getToken) {\n\t\t\t\ttoken = await provider.auth!.getToken({\n\t\t\t\t\tregistry,\n\t\t\t\t\tp: {\n\t\t\t\t\t\tconfirm,\n\t\t\t\t\t\tmultiselect,\n\t\t\t\t\t\tselect,\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t\tspinner,\n\t\t\t\t\t\tgroupMultiselect,\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tisCancel,\n\t\t\t\t\t\tcancel,\n\t\t\t\t\t\tcolor: pc,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst tokenResponse = await password({\n\t\t\t\t\tmessage: `Enter your token for ${pc.cyan(registry)}`,\n\t\t\t\t\tvalidate(value) {\n\t\t\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(tokenResponse)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\ttoken = tokenResponse;\n\t\t\t}\n\t\t}\n\n\t\ttokenManager.set(provider, registry, token);\n\t\treturn ok({ type: 'login', provider: provider.name, registry });\n\t} else {\n\t\tlet token = options.token;\n\t\tif (!token) {\n\t\t\tif (provider.auth!.getToken) {\n\t\t\t\ttoken = await provider.auth!.getToken({\n\t\t\t\t\tp: {\n\t\t\t\t\t\tconfirm,\n\t\t\t\t\t\tmultiselect,\n\t\t\t\t\t\tselect,\n\t\t\t\t\t\ttext,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t\tspinner,\n\t\t\t\t\t\tgroupMultiselect,\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tisCancel,\n\t\t\t\t\t\tcancel,\n\t\t\t\t\t\tcolor: pc,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst tokenResponse = await password({\n\t\t\t\t\tmessage: `Enter your token for ${pc.cyan(provider.name)}`,\n\t\t\t\t\tvalidate(value) {\n\t\t\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(tokenResponse)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\ttoken = tokenResponse;\n\t\t\t}\n\t\t}\n\n\t\ttokenManager.set(provider, undefined, token);\n\t\treturn ok({ type: 'login', provider: provider.name });\n\t}\n}\n\nexport function formatResult({ type, provider, registry }: AuthCommandResult): string {\n\tconst registryText = registry ? `${pc.cyan(registry)}` : `${pc.cyan(provider)}`;\n\n\tif (type === 'login') {\n\t\treturn `Logged in to ${registryText}.`;\n\t} else {\n\t\treturn `Logged out of ${registryText}.`;\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/build.ts",
    "content": "import { type FSWatcher, watch } from 'node:fs';\nimport { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tforEachRegistry,\n\thasRegistries,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport { buildRegistry, validateRegistryConfig } from '@/utils/build';\nimport type { Config } from '@/utils/config';\nimport { loadConfig, loadConfigSearch } from '@/utils/config/utils';\nimport {\n\ttype BuildError,\n\ttype CLIError,\n\tConfigNotFoundError,\n\tNoOutputsError,\n\tNoRegistriesError,\n} from '@/utils/errors';\nimport { existsSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { joinAbsolute } from '@/utils/path';\nimport { intro, isTTY, outro } from '@/utils/prompts';\nimport type { AbsolutePath } from '@/utils/types';\nimport { debounced } from '@/utils/utils';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\twatch: z.boolean(),\n\tdebounce: z.number(),\n});\n\nexport type BuildOptions = z.infer<typeof schema>;\n\nexport const build = new Command('build')\n\t.description('Build your registry.')\n\t.addOption(commonOptions.cwd)\n\t.option('-w, --watch', 'Watch for changes and rebuild automatically', false)\n\t.option(\n\t\t'-d, --debounce <ms>',\n\t\t'How long to wait before building again after a change is detected (watch mode only)',\n\t\t(val) => Number.parseInt(val, 10),\n\t\t100\n\t)\n\t.action(async (rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\t\tif (!configResult) error(new ConfigNotFoundError(options.cwd));\n\n\t\tif (options.watch) {\n\t\t\tawait runWatch(options, configResult);\n\t\t} else {\n\t\t\tconst config = configResult.config;\n\t\t\tconst cwd = path.dirname(configResult.path) as AbsolutePath;\n\t\t\tconst buildOptions = { ...options, cwd };\n\n\t\t\tawait runBeforeHooks(\n\t\t\t\tconfig,\n\t\t\t\t{ command: 'build', options: buildOptions },\n\t\t\t\t{ cwd, yes: false }\n\t\t\t);\n\n\t\t\tintro();\n\n\t\t\tconst result = await tryCommand(runBuild(buildOptions, config));\n\n\t\t\toutro(formatResult(result, { type: 'build' }));\n\n\t\t\tawait runAfterHooks(\n\t\t\t\tconfig,\n\t\t\t\t{ command: 'build', options: buildOptions, result },\n\t\t\t\t{ cwd }\n\t\t\t);\n\n\t\t\t// if any of the registries failed to build, exit with an error\n\t\t\tif (result.results.some((r) => r.isErr())) {\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t}\n\t});\n\nexport type RegistryBuildResult = {\n\ttime: number;\n\tname: string;\n\titems: number;\n\tfiles: number;\n\toutputs: number;\n};\n\nexport type BuildCommandResult = {\n\tresults: Result<RegistryBuildResult, BuildError>[];\n\tduration: number;\n};\n\nexport async function runBuild(\n\toptions: BuildOptions,\n\tconfig: Config\n): Promise<Result<BuildCommandResult, CLIError>> {\n\tif (!hasRegistries(config)) return err(new NoRegistriesError());\n\n\tconst start = performance.now();\n\tconst results = await forEachRegistry<Result<RegistryBuildResult, BuildError>>(\n\t\tconfig,\n\t\tasync (registry) => {\n\t\t\tregistry.outputs = registry.outputs ?? [];\n\n\t\t\tconst start = performance.now();\n\n\t\t\tconst result = await validateRegistryConfig(registry);\n\t\t\tif (result.isErr()) return err(result.error);\n\n\t\t\tif (registry.outputs.length > 0) {\n\t\t\t\tawait Promise.all(\n\t\t\t\t\tregistry.outputs.map((output) => output.clean({ cwd: options.cwd }))\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst buildResultResult = await buildRegistry(registry, { options, config });\n\t\t\tif (buildResultResult.isErr()) return err(buildResultResult.error);\n\t\t\tconst buildResult = buildResultResult.value;\n\n\t\t\tif (registry.outputs.length === 0)\n\t\t\t\treturn err(new NoOutputsError({ registryName: registry.name }));\n\n\t\t\tawait Promise.all(\n\t\t\t\tregistry.outputs.map((output) => output.output(buildResult, { cwd: options.cwd }))\n\t\t\t);\n\n\t\t\tconst end = performance.now();\n\t\t\tconst duration = end - start;\n\n\t\t\treturn ok({\n\t\t\t\tname: registry.name,\n\t\t\t\ttime: duration,\n\t\t\t\titems: buildResult.items.length,\n\t\t\t\tfiles: buildResult.items.flatMap((item) => item.files.length).length,\n\t\t\t\toutputs: registry.outputs.length,\n\t\t\t});\n\t\t},\n\t\t{ cwd: options.cwd }\n\t);\n\n\tconst end = performance.now();\n\tconst duration = end - start;\n\n\treturn ok({ results, duration });\n}\n\nasync function runWatch(\n\toptions: BuildOptions,\n\tconfigResult: { config: Config; path: string }\n): Promise<void> {\n\tlet currentConfig: Config = configResult.config;\n\n\tconst ac = new AbortController();\n\n\tconst watchers = new Map<string, FSWatcher>();\n\n\tlet buildActive = true;\n\n\tconst clearLine = () => {\n\t\tif (isTTY) {\n\t\t\tprocess.stdout.clearLine(0);\n\t\t\tprocess.stdout.cursorTo(0);\n\t\t}\n\t};\n\n\tconst writeStatus = (message: string, newLine = false) => {\n\t\tif (isTTY) {\n\t\t\tclearLine();\n\t\t\tprocess.stdout.write(message);\n\t\t} else if (newLine) {\n\t\t\tprocess.stdout.write(`${message}\\n`);\n\t\t}\n\t};\n\n\tconst debouncedBuild = debounced(\n\t\tasync ({ options, config }: { options: BuildOptions; config: Config }) => {\n\t\t\tif (buildActive) return;\n\t\t\tbuildActive = true;\n\n\t\t\twriteStatus(pc.dim(`Building...`));\n\n\t\t\tconst buildResult = await runBuild(options, config);\n\n\t\t\tclearLine();\n\n\t\t\tif (buildResult.isErr()) {\n\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t`Error building registry: ${pc.red(buildResult.error.message)}\\n`\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tprocess.stdout.write(`${formatResult(buildResult.value, { type: 'build' })}\\n`);\n\t\t\t}\n\n\t\t\twriteStatus(pc.dim(`${isTTY ? '\\n' : ''}Watching for changes...`));\n\n\t\t\tbuildActive = false;\n\t\t},\n\t\toptions.debounce\n\t);\n\n\twatchers.set(\n\t\tconfigResult.path,\n\t\tcreateWatcher(configResult.path, {\n\t\t\tsignal: ac.signal,\n\t\t\tonChange: async () => {\n\t\t\t\tconst result = await loadConfig({\n\t\t\t\t\tcwd: path.dirname(configResult.path),\n\t\t\t\t});\n\t\t\t\tif (result.isErr()) return;\n\t\t\t\tcurrentConfig = result.value;\n\t\t\t\tawait setupWatchers();\n\t\t\t\tdebouncedBuild({ options, config: currentConfig });\n\t\t\t},\n\t\t})\n\t);\n\n\tasync function setupWatchers() {\n\t\tawait forEachRegistry(\n\t\t\tconfigResult.config,\n\t\t\tasync (registry) => {\n\t\t\t\tconst files = registry.items.flatMap((item) =>\n\t\t\t\t\titem.files.map((file) => joinAbsolute(options.cwd, file.path))\n\t\t\t\t);\n\t\t\t\tfor (const file of files) {\n\t\t\t\t\tif (watchers.has(file)) continue;\n\t\t\t\t\tif (!existsSync(file)) continue;\n\t\t\t\t\twatchers.set(\n\t\t\t\t\t\tfile,\n\t\t\t\t\t\tcreateWatcher(file, {\n\t\t\t\t\t\t\tsignal: ac.signal,\n\t\t\t\t\t\t\tonChange: () => debouncedBuild({ options, config: currentConfig }),\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ cwd: options.cwd }\n\t\t);\n\t}\n\n\tsetupWatchers();\n\n\twriteStatus(`Building...`, true);\n\n\tconst buildResult = await runBuild(options, configResult.config);\n\n\tclearLine();\n\n\tif (buildResult.isErr()) {\n\t\tprocess.stdout.write(`Error building registry: ${pc.red(buildResult.error.message)}\\n`);\n\t} else {\n\t\tprocess.stdout.write(`${formatResult(buildResult.value, { type: 'build' })}\\n`);\n\t}\n\n\twriteStatus(pc.dim(`${isTTY ? '\\n' : ''}Watching for changes...`));\n\n\tbuildActive = false;\n\n\tprocess.on('SIGINT', () => {\n\t\tac.abort();\n\t});\n\tprocess.on('SIGTERM', () => {\n\t\tac.abort();\n\t});\n}\n\nfunction createWatcher(\n\tpath: string,\n\t{ onChange, signal }: { onChange?: () => void; signal: AbortSignal }\n) {\n\treturn watch(path, { recursive: true, signal }, (eventType) => {\n\t\tif (eventType === 'change') {\n\t\t\tonChange?.();\n\t\t}\n\t});\n}\n\nfunction formatResult(\n\t{ results, duration }: BuildCommandResult,\n\t{ type }: { type: 'build' | 'watch' }\n): string {\n\treturn `${type === 'build' ? 'Finished' : 'Rebuilt'} in ${pc.green(`${duration.toFixed(2)}ms`)}\n${results.map((result) => `   ${formatRegistryResult(result)}`).join('\\n')}`;\n}\n\nfunction formatRegistryResult(result: Result<RegistryBuildResult, BuildError>): string {\n\tif (result.isErr())\n\t\treturn `${pc.cyan(result.error.registryName)}: ${pc.red(result.error.toString())}`;\n\tconst { name, outputs, time, items, files } = result.value;\n\n\treturn `${pc.cyan(name)}: Created ${pc.green(outputs.toString())} ${\n\t\toutputs > 1 ? 'outputs' : 'output'\n\t} in ${pc.green(`${time.toFixed(2)}ms`)} with ${pc.green(items.toString())} ${\n\t\titems > 1 ? 'items' : 'item'\n\t} and ${pc.green(files.toString())} ${files > 1 ? 'files' : 'file'}.`;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/config/index.ts",
    "content": "import { Command } from 'commander';\nimport { language } from '@/commands/config/language';\nimport { mcp } from '@/commands/config/mcp';\nimport { provider } from '@/commands/config/provider';\nimport { transform } from '@/commands/config/transform';\n\nexport const config = new Command('config')\n\t.description('Modify your jsrepo config.')\n\t.addCommand(transform)\n\t.addCommand(provider)\n\t.addCommand(language)\n\t.addCommand(mcp);\n"
  },
  {
    "path": "packages/jsrepo/src/commands/config/language.ts",
    "content": "import { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport type { Config } from '@/utils/config';\nimport { addPluginsToConfig, parsePlugins } from '@/utils/config/mods/add-plugins';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, ConfigNotFoundError } from '@/utils/errors';\nimport { readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { intro, outro, promptInstallDependencies } from '@/utils/prompts';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n});\n\nexport type ConfigAddLanguageOptions = z.infer<typeof schema>;\n\nexport const language = new Command('language')\n\t.description('Add a language to your config.')\n\t.argument(\n\t\t'[languages...]',\n\t\t'Names of the languages you want to add to your config. ex: (jsrepo-language-go, jsrepo-language-rust)'\n\t)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.action(async (languages, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: !options.yes,\n\t\t});\n\t\tif (!configResult) error(new ConfigNotFoundError(options.cwd));\n\n\t\tconst config = configResult.config;\n\t\tconst cwd = path.dirname(configResult.path) as AbsolutePath;\n\t\tconst languageOptions = { ...options, cwd };\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'config.language', options: languageOptions },\n\t\t\t{ cwd, yes: options.yes }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(runLanguage(languages, languageOptions, configResult));\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'config.language', options: languageOptions, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport type ConfigAddLanguageCommandResult = {\n\tduration: number;\n\tlanguages: number;\n};\n\nexport async function runLanguage(\n\tlanguagesArg: string[],\n\toptions: ConfigAddLanguageOptions,\n\tconfig: { config: Config; path: AbsolutePath }\n): Promise<Result<ConfigAddLanguageCommandResult, CLIError>> {\n\tconst start = performance.now();\n\n\tconst languagesResult = parsePlugins(languagesArg, 'language');\n\tif (languagesResult.isErr()) return err(languagesResult.error);\n\tconst languages = languagesResult.value;\n\tconst codeResult = readFileSync(config.path);\n\tif (codeResult.isErr()) return err(codeResult.error);\n\tconst code = codeResult.value;\n\n\tconst newCodeResult = await addPluginsToConfig({\n\t\tplugins: languages,\n\t\tkey: 'languages',\n\t\tconfig: { path: config.path, code },\n\t});\n\tif (newCodeResult.isErr()) return err(newCodeResult.error);\n\tconst newCode = newCodeResult.value;\n\n\tconst writeResult = writeFileSync(config.path, newCode);\n\tif (writeResult.isErr()) return err(writeResult.error);\n\n\tawait promptInstallDependencies(\n\t\t{\n\t\t\tdevDependencies: languages.map((language) => ({\n\t\t\t\tname: language.packageName,\n\t\t\t\tversion: language.version,\n\t\t\t})),\n\t\t\tdependencies: [],\n\t\t},\n\t\t{ options, configPath: config.path }\n\t);\n\n\tconst end = performance.now();\n\tconst duration = end - start;\n\n\treturn ok({ duration, languages: languagesArg.length });\n}\n\nexport function formatResult({ duration, languages }: ConfigAddLanguageCommandResult): string {\n\treturn `Added ${pc.green(languages.toString())} ${languages > 1 ? 'languages' : 'language'} in ${pc.green(\n\t\t`${duration.toFixed(2)}ms`\n\t)}.`;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/config/mcp.ts",
    "content": "import os from 'node:os';\nimport { cancel, isCancel, multiselect } from '@clack/prompts';\nimport { Command, Option } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport type { AbsolutePath, Config } from '@/api';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, JsrepoError } from '@/utils/errors';\nimport { existsSync, readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { stringify } from '@/utils/json';\nimport { joinAbsolute } from '@/utils/path';\nimport { intro, outro } from '@/utils/prompts';\nimport { safeParseFromJSON } from '@/utils/zod';\n\nconst supportedClients = ['cursor', 'claude', 'vscode', 'codex', 'antigravity'] as const;\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tclient: z.array(z.enum(supportedClients)).optional(),\n\tall: z.boolean().optional(),\n});\n\nexport type ConfigMcpOptions = z.infer<typeof schema>;\n\ntype McpClient = (typeof supportedClients)[number];\n\nexport const mcp = new Command('mcp')\n\t.description('Configure the jsrepo MCP server for your environment.')\n\t.addOption(\n\t\tnew Option('--client <clients...>', 'The MCP client(s) to configure').choices(\n\t\t\tsupportedClients\n\t\t)\n\t)\n\t.option('--all', `Configure all supported MCP clients`, false)\n\t.addOption(commonOptions.cwd)\n\t.action(async (rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\t\tconst config = (configResult?.config ?? {}) as Config;\n\t\tconst cwd = options.cwd;\n\n\t\tawait runBeforeHooks(config, { command: 'config.mcp', options }, { cwd, yes: false });\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(runMcp(options));\n\n\t\toutro(formatResult({ ...result, cwd: options.cwd }));\n\n\t\tawait runAfterHooks(config, { command: 'config.mcp', options, result }, { cwd });\n\t});\n\nexport type ClientConfigResult = {\n\tname: string;\n\tfilePath: string;\n};\n\nexport type ClientResult = {\n\tname: string;\n\tresult: Result<{ filePath: string }, CLIError>;\n};\n\nexport type ConfigMcpCommandResult = {\n\tduration: number;\n\tresults: ClientResult[];\n};\n\nexport async function runMcp(\n\toptions: ConfigMcpOptions\n): Promise<Result<ConfigMcpCommandResult, CLIError>> {\n\tlet clientsToConfigure: McpClient[];\n\n\tif (options.all) {\n\t\tclientsToConfigure = Object.keys(CLIENTS) as McpClient[];\n\t} else if (options.client && options.client.length > 0) {\n\t\tclientsToConfigure = options.client;\n\t} else {\n\t\tconst clientOptions = Object.entries(CLIENTS).map(([value, config]) => ({\n\t\t\tvalue: value as McpClient,\n\t\t\tlabel: config.name,\n\t\t}));\n\n\t\tconst selected = await multiselect({\n\t\t\tmessage: 'Which clients would you like to configure?',\n\t\t\toptions: clientOptions,\n\t\t});\n\n\t\tif (isCancel(selected)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tclientsToConfigure = selected;\n\t}\n\n\tconst start = performance.now();\n\n\tconst results: ClientResult[] = [];\n\n\tfor (const wantedClient of clientsToConfigure) {\n\t\tconst client = CLIENTS[wantedClient];\n\t\tconst filePath = client.filePath(options.cwd);\n\n\t\tconst result = client.writeConfig(filePath);\n\t\tresults.push({\n\t\t\tname: client.name,\n\t\t\tresult: result.isErr() ? err(result.error) : ok({ filePath }),\n\t\t});\n\t}\n\n\tconst end = performance.now();\n\tconst duration = end - start;\n\n\treturn ok({ duration, results });\n}\n\nexport function formatResult({\n\tduration,\n\tresults,\n\tcwd,\n}: ConfigMcpCommandResult & { cwd: string }): string {\n\treturn `Configured ${results.length} ${results.length > 1 ? 'clients' : 'client'} in ${pc.green(\n\t\t`${duration.toFixed(2)}ms`\n\t)}:\\n${results.map((result) => formatClientResult(result, cwd)).join('\\n')}`;\n}\n\nfunction formatClientResult({ name, result }: ClientResult, cwd: string): string {\n\tif (result.isErr()) {\n\t\treturn `   ${pc.cyan(name)}: ${pc.red(result.error.message)}`;\n\t}\n\tconst { filePath } = result.value;\n\treturn `   ${pc.cyan(name)} → ${pc.dim(path.relative(cwd, filePath))}`;\n}\n\ninterface ClientConfig {\n\tname: string;\n\tfilePath: (cwd: AbsolutePath) => AbsolutePath;\n\twriteConfig: (filePath: AbsolutePath) => Result<void, CLIError>;\n}\n\nfunction writeConfig(filePath: AbsolutePath, content: string): Result<void, CLIError> {\n\ttry {\n\t\twriteFileSync(filePath, content);\n\t\treturn ok(undefined);\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to write config to ${filePath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nconst MCP_SERVER_CONFIG_JSON: ServerConfig = {\n\tcommand: 'npx',\n\targs: ['@jsrepo/mcp'],\n} as const;\n\nconst MCP_SERVER_CONFIG_TOML = `[mcp_servers.jsrepo]\ncommand = \"npx\"\nargs = [\"@jsrepo/mcp\"]\n`;\n\nexport function updateTomlConfig(existingContent: string): string {\n\tif (!existingContent) return MCP_SERVER_CONFIG_TOML;\n\tif (existingContent.includes('[mcp_servers.jsrepo]')) return existingContent;\n\n\t// Ensure there is space between the last section and the new config\n\tif (!existingContent.endsWith('\\n')) existingContent += '\\n';\n\n\treturn `${existingContent}${MCP_SERVER_CONFIG_TOML}`;\n}\n\nconst ServerConfigSchema = z\n\t.object({\n\t\tcommand: z.string().optional(),\n\t\targs: z.array(z.string()).optional(),\n\t\tenv: z.record(z.string(), z.string()).optional(),\n\t\ttype: z.string().optional(),\n\t\turl: z.string().optional(),\n\t\theaders: z.record(z.string(), z.string()).optional(),\n\t})\n\t.catchall(z.unknown());\n\nexport type ServerConfig = z.infer<typeof ServerConfigSchema>;\n\nexport const McpServerConfigSchema = z.object({\n\tmcpServers: z.record(z.string(), ServerConfigSchema),\n});\n\nexport const VSCodeServerConfigSchema = z.object({\n\tservers: z.record(z.string(), ServerConfigSchema),\n});\n\nexport type McpServerConfig = z.infer<typeof McpServerConfigSchema>;\nexport type VSCodeServerConfig = z.infer<typeof VSCodeServerConfigSchema>;\n\nexport function updateJsonConfig({\n\texistingContent,\n\tserverName,\n\tserverConfig,\n}: {\n\texistingContent: string;\n\tserverName: string;\n\tserverConfig: ServerConfig;\n}): Result<string, CLIError> {\n\tif (existingContent.trim().length === 0) {\n\t\treturn ok(\n\t\t\tstringify({ mcpServers: { [serverName]: serverConfig } } satisfies McpServerConfig, {\n\t\t\t\tformat: true,\n\t\t\t})\n\t\t);\n\t}\n\n\tconst parsedContentResult = safeParseFromJSON(McpServerConfigSchema, existingContent);\n\tif (parsedContentResult.isErr()) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to parse MCP server config: ${parsedContentResult.error.message}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n\tconst parsedContent = parsedContentResult.value;\n\n\tconst newContent = {\n\t\tmcpServers: {\n\t\t\t...parsedContent.mcpServers,\n\t\t\t[serverName]: serverConfig,\n\t\t},\n\t} satisfies McpServerConfig;\n\n\treturn ok(stringify(newContent, { format: true }));\n}\n\nexport function updateVSCodeJsonConfig({\n\texistingContent,\n\tserverName,\n\tserverConfig,\n}: {\n\texistingContent: string;\n\tserverName: string;\n\tserverConfig: ServerConfig;\n}): Result<string, CLIError> {\n\tif (existingContent.trim().length === 0) {\n\t\treturn ok(\n\t\t\tstringify({ servers: { [serverName]: serverConfig } } satisfies VSCodeServerConfig, {\n\t\t\t\tformat: true,\n\t\t\t})\n\t\t);\n\t}\n\n\tconst parsedContentResult = safeParseFromJSON(VSCodeServerConfigSchema, existingContent);\n\tif (parsedContentResult.isErr()) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to parse MCP server config: ${parsedContentResult.error.message}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n\tconst parsedContent = parsedContentResult.value;\n\n\tconst newContent = {\n\t\tservers: {\n\t\t\t...parsedContent.servers,\n\t\t\t[serverName]: serverConfig,\n\t\t},\n\t} satisfies VSCodeServerConfig;\n\n\treturn ok(stringify(newContent, { format: true }));\n}\n\nfunction updateJsonFile(\n\tfilePath: AbsolutePath,\n\tconfig: ServerConfig,\n\ttype: 'generic' | 'vscode' = 'generic'\n): Result<void, CLIError> {\n\tconst existingContent = existsSync(filePath) ? readFileSync(filePath)._unsafeUnwrap() : '';\n\tconst newContent =\n\t\ttype === 'vscode'\n\t\t\t? updateVSCodeJsonConfig({\n\t\t\t\t\texistingContent,\n\t\t\t\t\tserverName: 'jsrepo',\n\t\t\t\t\tserverConfig: config,\n\t\t\t\t})\n\t\t\t: updateJsonConfig({\n\t\t\t\t\texistingContent,\n\t\t\t\t\tserverName: 'jsrepo',\n\t\t\t\t\tserverConfig: config,\n\t\t\t\t});\n\tif (newContent.isErr()) return err(newContent.error);\n\treturn writeConfig(filePath, newContent.value);\n}\n\nconst CLIENTS: Record<McpClient, ClientConfig> = {\n\tcursor: {\n\t\tname: 'Cursor',\n\t\tfilePath: (cwd: AbsolutePath) => joinAbsolute(cwd, '.cursor/mcp.json'),\n\t\twriteConfig: (filePath: AbsolutePath) => updateJsonFile(filePath, MCP_SERVER_CONFIG_JSON),\n\t},\n\tclaude: {\n\t\tname: 'Claude Code',\n\t\tfilePath: (cwd: AbsolutePath) => joinAbsolute(cwd, '.mcp.json'),\n\t\twriteConfig: (filePath: AbsolutePath) => updateJsonFile(filePath, MCP_SERVER_CONFIG_JSON),\n\t},\n\tvscode: {\n\t\tname: 'VS Code',\n\t\tfilePath: (cwd: AbsolutePath) => joinAbsolute(cwd, '.vscode/mcp.json'),\n\t\twriteConfig: (filePath: AbsolutePath) =>\n\t\t\tupdateJsonFile(filePath, MCP_SERVER_CONFIG_JSON, 'vscode'),\n\t},\n\tcodex: {\n\t\tname: 'Codex',\n\t\tfilePath: () => joinAbsolute(os.homedir() as AbsolutePath, '.codex/config.toml'),\n\t\twriteConfig: (filePath: AbsolutePath) => {\n\t\t\tconst existingContent = existsSync(filePath)\n\t\t\t\t? readFileSync(filePath)._unsafeUnwrap()\n\t\t\t\t: '';\n\t\t\tconst newContent = updateTomlConfig(existingContent);\n\t\t\treturn writeConfig(filePath, newContent);\n\t\t},\n\t},\n\tantigravity: {\n\t\tname: 'Antigravity',\n\t\tfilePath: () =>\n\t\t\tjoinAbsolute(os.homedir() as AbsolutePath, '.gemini/antigravity/mcp_config.json'),\n\t\twriteConfig: (filePath: AbsolutePath) => updateJsonFile(filePath, MCP_SERVER_CONFIG_JSON),\n\t},\n};\n"
  },
  {
    "path": "packages/jsrepo/src/commands/config/provider.ts",
    "content": "import { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport type { Config } from '@/utils/config';\nimport { addPluginsToConfig, parsePlugins } from '@/utils/config/mods/add-plugins';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, ConfigNotFoundError } from '@/utils/errors';\nimport { readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { intro, outro, promptInstallDependencies } from '@/utils/prompts';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n});\n\nexport type ConfigAddProviderOptions = z.infer<typeof schema>;\n\nexport const provider = new Command('provider')\n\t.description('Add a provider to your config.')\n\t.argument(\n\t\t'[providers...]',\n\t\t'Names of the providers you want to add to your config. ex: (jsrepo-provider-jsr, jsrepo-provider-npm)'\n\t)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.action(async (providers, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: !options.yes,\n\t\t});\n\n\t\tif (!configResult) error(new ConfigNotFoundError(options.cwd));\n\n\t\tconst config = configResult.config;\n\t\tconst cwd = path.dirname(configResult.path) as AbsolutePath;\n\t\tconst providerOptions = { ...options, cwd };\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'config.provider', options: providerOptions },\n\t\t\t{ cwd, yes: options.yes }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(runProvider(providers, providerOptions, configResult));\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'config.provider', options: providerOptions, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport type ConfigAddProviderCommandResult = {\n\tduration: number;\n\tproviders: number;\n};\n\nexport async function runProvider(\n\tprovidersArg: string[],\n\toptions: ConfigAddProviderOptions,\n\tconfig: { config: Config; path: AbsolutePath }\n): Promise<Result<ConfigAddProviderCommandResult, CLIError>> {\n\tconst start = performance.now();\n\n\tconst providersResult = parsePlugins(providersArg, 'provider');\n\tif (providersResult.isErr()) return err(providersResult.error);\n\tconst providers = providersResult.value;\n\n\tconst codeResult = readFileSync(config.path);\n\tif (codeResult.isErr()) return err(codeResult.error);\n\tconst code = codeResult.value;\n\n\tconst newCodeResult = await addPluginsToConfig({\n\t\tplugins: providers,\n\t\tkey: 'providers',\n\t\tconfig: { path: config.path, code },\n\t});\n\tif (newCodeResult.isErr()) return err(newCodeResult.error);\n\tconst newCode = newCodeResult.value;\n\n\tconst writeResult = writeFileSync(config.path, newCode);\n\tif (writeResult.isErr()) return err(writeResult.error);\n\n\tawait promptInstallDependencies(\n\t\t{\n\t\t\tdevDependencies: providers.map((provider) => ({\n\t\t\t\tname: provider.packageName,\n\t\t\t\tversion: provider.version,\n\t\t\t})),\n\t\t\tdependencies: [],\n\t\t},\n\t\t{ options, configPath: config.path }\n\t);\n\n\tconst end = performance.now();\n\tconst duration = end - start;\n\n\treturn ok({ duration, providers: providersArg.length });\n}\n\nexport function formatResult({ duration, providers }: ConfigAddProviderCommandResult): string {\n\treturn `Added ${pc.green(providers.toString())} ${providers > 1 ? 'providers' : 'provider'} in ${pc.green(\n\t\t`${duration.toFixed(2)}ms`\n\t)}.`;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/config/transform.ts",
    "content": "import { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport type { Config } from '@/utils/config';\nimport { addPluginsToConfig, parsePlugins } from '@/utils/config/mods/add-plugins';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, ConfigNotFoundError } from '@/utils/errors';\nimport { readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { intro, outro, promptInstallDependencies } from '@/utils/prompts';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n});\n\nexport type ConfigAddTransformOptions = z.infer<typeof schema>;\n\nexport const transform = new Command('transform')\n\t.description('Add a transform to your config.')\n\t.argument(\n\t\t'[transforms...]',\n\t\t'Names of the transforms you want to add to your config. ex: (@jsrepo/transform-prettier, @jsrepo/transform-biome)'\n\t)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.action(async (transforms, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: !options.yes,\n\t\t});\n\t\tif (!configResult) error(new ConfigNotFoundError(options.cwd));\n\n\t\tconst config = configResult.config;\n\t\tconst cwd = path.dirname(configResult.path) as AbsolutePath;\n\t\tconst transformOptions = { ...options, cwd };\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'config.transform', options: transformOptions },\n\t\t\t{ cwd, yes: options.yes }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(runTransform(transforms, transformOptions, configResult));\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'config.transform', options: transformOptions, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport type ConfigAddTransformCommandResult = {\n\tduration: number;\n\ttransforms: number;\n};\n\nexport async function runTransform(\n\ttransformsArg: string[],\n\toptions: ConfigAddTransformOptions,\n\tconfig: { config: Config; path: AbsolutePath }\n): Promise<Result<ConfigAddTransformCommandResult, CLIError>> {\n\tconst start = performance.now();\n\n\tconst transformsResult = parsePlugins(transformsArg, 'transform');\n\tif (transformsResult.isErr()) return err(transformsResult.error);\n\tconst transforms = transformsResult.value;\n\n\tconst codeResult = readFileSync(config.path);\n\tif (codeResult.isErr()) return err(codeResult.error);\n\tconst code = codeResult.value;\n\n\tconst newCodeResult = await addPluginsToConfig({\n\t\tplugins: transforms,\n\t\tkey: 'transforms',\n\t\tconfig: { path: config.path, code },\n\t});\n\tif (newCodeResult.isErr()) return err(newCodeResult.error);\n\tconst newCode = newCodeResult.value;\n\n\tconst writeResult = writeFileSync(config.path, newCode);\n\tif (writeResult.isErr()) return err(writeResult.error);\n\n\tawait promptInstallDependencies(\n\t\t{\n\t\t\tdevDependencies: transforms.map((transform) => ({\n\t\t\t\tname: transform.packageName,\n\t\t\t\tversion: transform.version,\n\t\t\t})),\n\t\t\tdependencies: [],\n\t\t},\n\t\t{ options, configPath: config.path }\n\t);\n\n\tconst end = performance.now();\n\tconst duration = end - start;\n\n\treturn ok({ duration, transforms: transformsArg.length });\n}\n\nexport function formatResult({\n\tduration,\n\ttransforms: items,\n}: ConfigAddTransformCommandResult): string {\n\treturn `Added ${pc.green(items.toString())} ${items > 1 ? 'transforms' : 'transform'} in ${pc.green(\n\t\t`${duration.toFixed(2)}ms`\n\t)}.`;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/index.ts",
    "content": "import { add } from '@/commands/add';\nimport { auth } from '@/commands/auth';\nimport { build } from '@/commands/build';\nimport { config } from '@/commands/config';\nimport { init } from '@/commands/init';\nimport { publish } from '@/commands/publish';\nimport { update } from '@/commands/update';\n\nexport { add, build, config, init, auth, publish, update };\n"
  },
  {
    "path": "packages/jsrepo/src/commands/init.ts",
    "content": "import { cancel, confirm, isCancel, log, multiselect, text } from '@clack/prompts';\nimport { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport pkg from '@/../package.json';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport { DEFAULT_PROVIDERS } from '@/providers';\nimport {\n\tgetPathsForItems,\n\tnormalizeItemTypeForPath,\n\tprepareUpdates,\n\ttype ResolvedRegistry,\n\tresolveAndFetchAllItems,\n\tresolveRegistries,\n\tupdateFiles,\n} from '@/utils/add';\nimport type { DependencyKey, RemoteDependency } from '@/utils/build';\nimport type { Config, RegistryPlugin } from '@/utils/config';\nimport {\n\taddPluginsToConfig,\n\tneededPlugins,\n\ttype Plugin,\n\tparsePluginName,\n} from '@/utils/config/mods/add-plugins';\nimport { addRegistriesToConfig } from '@/utils/config/mods/add-registries';\nimport { updateConfigPaths } from '@/utils/config/mods/update-paths';\nimport { loadConfig, loadConfigSearch } from '@/utils/config/utils';\nimport {\n\tAlreadyInitializedError,\n\ttype CLIError,\n\ttype ConfigObjectNotFoundError,\n\ttype CouldNotFindJsrepoImportError,\n\ttype InvalidKeyTypeError,\n\ttype InvalidPluginError,\n\tNoPackageJsonFoundError,\n} from '@/utils/errors';\nimport { readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { tryGetPackage } from '@/utils/package';\nimport { joinAbsolute } from '@/utils/path';\nimport {\n\tinitLogging,\n\tintro,\n\toutro,\n\tpromptAddEnvVars,\n\tpromptInstallDependencies,\n\tpromptInstallDependenciesByEcosystem,\n} from '@/utils/prompts';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n\toverwrite: z.boolean(),\n\tverbose: z.boolean(),\n\texpand: z.boolean(),\n\tmaxUnchanged: z.number(),\n\tjs: z.boolean(),\n});\n\nexport type InitOptions = z.infer<typeof schema>;\n\nexport type InitCommandResult = {\n\tregistries: string[];\n};\n\nexport const init = new Command('init')\n\t.description('Initialize a new jsrepo project.')\n\t.argument('[registries...]', 'The registries to initialize.')\n\t.option(\n\t\t'--js',\n\t\t'Initialize the project and automatically add the @jsrepo/transform-javascript transform plugin.',\n\t\tfalse\n\t)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.addOption(commonOptions.verbose)\n\t.addOption(commonOptions.overwrite)\n\t.addOption(commonOptions.expand)\n\t.addOption(commonOptions.maxUnchanged)\n\t.action(async (registries, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\tconst config = configResult?.config ?? {};\n\t\tconst cwd = configResult ? (path.dirname(configResult.path) as AbsolutePath) : options.cwd;\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig as Config,\n\t\t\t{ command: 'init', options: { ...options, cwd } },\n\t\t\t{ cwd, yes: options.yes }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(\n\t\t\trunInit(\n\t\t\t\tregistries,\n\t\t\t\t// this way if the config is found in a higher directory we base everything off of that directory\n\t\t\t\t{\n\t\t\t\t\t...options,\n\t\t\t\t\tcwd,\n\t\t\t\t},\n\t\t\t\tconfigResult\n\t\t\t)\n\t\t);\n\n\t\toutro(pc.green('Initialization complete!'));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig as Config,\n\t\t\t{ command: 'init', options: { ...options, cwd }, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport async function runInit(\n\tregistriesArg: string[],\n\toptions: InitOptions,\n\tconfigResult: { config: Config; path: AbsolutePath } | null\n): Promise<Result<InitCommandResult, CLIError>> {\n\tconst { verbose: _, spinner } = initLogging({ options });\n\n\tconst packagePath = joinAbsolute(options.cwd, 'package.json');\n\n\tconst packageJsonResult = tryGetPackage(packagePath);\n\tif (packageJsonResult.isErr()) return err(new NoPackageJsonFoundError());\n\tconst packageJson = packageJsonResult.value;\n\n\tlet { config, path: configPath } = configResult ?? {\n\t\tconfig: null,\n\t\tpath: joinAbsolute(\n\t\t\toptions.cwd,\n\t\t\tpackageJson.type === 'module' ? 'jsrepo.config.ts' : 'jsrepo.config.mts'\n\t\t),\n\t};\n\tconst providers = config?.providers ?? DEFAULT_PROVIDERS;\n\n\tlet configCode: string;\n\tif (config === null) {\n\t\tconfigCode = initBlankConfig();\n\t} else {\n\t\tconst configCodeResult = readFileSync(configPath);\n\t\tif (configCodeResult.isErr()) return err(configCodeResult.error);\n\t\tconfigCode = configCodeResult.value;\n\t}\n\n\tlet hasJsrepo = true;\n\tif (\n\t\tpackageJson.dependencies?.jsrepo === undefined &&\n\t\tpackageJson.devDependencies?.jsrepo === undefined\n\t) {\n\t\tif (!packageJson.devDependencies) {\n\t\t\tpackageJson.devDependencies = {};\n\t\t}\n\t\tpackageJson.devDependencies.jsrepo = `^${pkg.version}`;\n\n\t\tconst writePackageJsonResult = writeFileSync(\n\t\t\tpackagePath,\n\t\t\tJSON.stringify(packageJson, null, '\\t')\n\t\t);\n\t\tif (writePackageJsonResult.isErr()) return err(writePackageJsonResult.error);\n\t\thasJsrepo = false;\n\t}\n\n\tif (options.js) {\n\t\tconst addPluginsToConfigResult = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'stripTypes',\n\t\t\t\t\tpackageName: '@jsrepo/transform-javascript',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tkey: 'transforms',\n\t\t\tconfig: { path: configPath, code: configCode },\n\t\t});\n\t\tif (addPluginsToConfigResult.isErr()) return err(addPluginsToConfigResult.error);\n\t\tconfigCode = addPluginsToConfigResult.value;\n\t}\n\n\tif (registriesArg.length === 0) {\n\t\tif (config !== null) return err(new AlreadyInitializedError());\n\n\t\tconst writeConfigResult = writeFileSync(configPath, configCode);\n\t\tif (writeConfigResult.isErr()) return err(writeConfigResult.error);\n\n\t\tlog.success(`Wrote config to ${pc.cyan(path.relative(options.cwd, configPath))}`);\n\n\t\tif (!hasJsrepo) {\n\t\t\tawait promptInstallDependencies(\n\t\t\t\t{\n\t\t\t\t\tdependencies: [],\n\t\t\t\t\tdevDependencies: [\n\t\t\t\t\t\t...(options.js\n\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tname: '@jsrepo/transform-javascript',\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t{ configPath, options }\n\t\t\t);\n\t\t}\n\t\treturn ok({ registries: [] });\n\t}\n\n\tconst registries = registriesArg.length > 0 ? registriesArg : (config?.registries ?? []);\n\n\tspinner.start(\n\t\t`Retrieving manifest${registries.length > 1 ? 's' : ''} from ${pc.cyan(registries.join(', '))}`\n\t);\n\n\tconst resolvedRegistriesResult = await resolveRegistries(registries, {\n\t\tcwd: options.cwd,\n\t\tproviders,\n\t});\n\n\tif (resolvedRegistriesResult.isErr()) {\n\t\tspinner.stop('Failed to retrieve manifests');\n\t\treturn err(resolvedRegistriesResult.error);\n\t}\n\tspinner.stop(\n\t\t`Retrieved manifest${registries.length > 1 ? 's' : ''} from ${pc.cyan(registries.join(', '))}`\n\t);\n\tconst resolvedRegistries = resolvedRegistriesResult.value;\n\n\tconst pluginChoices = new Map<string, { install: boolean; plugin: Plugin }>();\n\tfor (const [_, resolved] of resolvedRegistries) {\n\t\tconst initRegistryResult = await initRegistry(resolved, {\n\t\t\tconfigCode,\n\t\t\tconfigPath,\n\t\t\toptions,\n\t\t\tpluginChoices,\n\t\t\tconfig,\n\t\t});\n\t\tif (initRegistryResult.isErr()) return err(initRegistryResult.error);\n\t\tconfigCode = initRegistryResult.value;\n\t}\n\n\tconst addRegistriesToConfigResult = await addRegistriesToConfig(registriesArg, {\n\t\tconfig: { path: configPath, code: configCode },\n\t});\n\tif (addRegistriesToConfigResult.isErr()) return err(addRegistriesToConfigResult.error);\n\tconfigCode = addRegistriesToConfigResult.value;\n\n\tconst writeConfigResult = writeFileSync(configPath, configCode);\n\tif (writeConfigResult.isErr()) return err(writeConfigResult.error);\n\n\tlog.success(`Wrote config to ${pc.cyan(path.relative(options.cwd, configPath))}`);\n\n\tconst neededDeps = new Map<DependencyKey, RemoteDependency>(\n\t\tArray.from(pluginChoices.values())\n\t\t\t.filter((plugin) => plugin.install)\n\t\t\t.map(\n\t\t\t\t(plugin) =>\n\t\t\t\t\t[\n\t\t\t\t\t\t`js:${plugin.plugin.packageName}@${plugin.plugin.version ?? ''}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: plugin.plugin.packageName,\n\t\t\t\t\t\t\tversion: plugin.plugin.version,\n\t\t\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\t\t},\n\t\t\t\t\t] as [DependencyKey, RemoteDependency]\n\t\t\t)\n\t);\n\n\tif (neededDeps.size > 0) {\n\t\tawait promptInstallDependencies(\n\t\t\t{\n\t\t\t\tdependencies: [],\n\t\t\t\tdevDependencies: [\n\t\t\t\t\t...Array.from(neededDeps.values()),\n\t\t\t\t\t...(options.js\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tname: '@jsrepo/transform-javascript',\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: []),\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\toptions: { yes: true },\n\t\t\t\tconfigPath,\n\t\t\t}\n\t\t);\n\t}\n\n\tneededDeps.clear();\n\n\tconst parsedPath = path.parse(configPath);\n\tconst loadConfigResult = await loadConfig({ cwd: parsedPath.dir });\n\tif (loadConfigResult.isErr()) return err(loadConfigResult.error);\n\tconfig = loadConfigResult.value;\n\n\tconst itemsToAdd = Array.from(resolvedRegistries.values()).flatMap((registry) =>\n\t\tregistry.manifest.items\n\t\t\t.filter((item) => item.add === 'on-init' || item.name === 'index')\n\t\t\t.map((item) => ({ registry, item }))\n\t);\n\n\tconst optionallyOnInitItems = Array.from(resolvedRegistries.values()).flatMap((registry) =>\n\t\tregistry.manifest.items\n\t\t\t.filter((item) => item.add === 'optionally-on-init')\n\t\t\t.map((item) => ({ registry, item }))\n\t);\n\n\tif (optionallyOnInitItems.length > 0) {\n\t\tconst response = await multiselect({\n\t\t\tmessage: `Would you like to add any of the following items?`,\n\t\t\toptions: optionallyOnInitItems.map((item) => ({\n\t\t\t\tlabel: item.item.name,\n\t\t\t\tvalue: item.item.name,\n\t\t\t})),\n\t\t\trequired: false,\n\t\t});\n\n\t\tif (isCancel(response)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\titemsToAdd.push(\n\t\t\t...optionallyOnInitItems.filter((item) => response.includes(item.item.name))\n\t\t);\n\t}\n\n\tconst neededDependencies: {\n\t\tdependencies: RemoteDependency[];\n\t\tdevDependencies: RemoteDependency[];\n\t} = { dependencies: [], devDependencies: [] };\n\n\tif (itemsToAdd.length > 0) {\n\t\tspinner.start(\n\t\t\t`Fetching ${pc.cyan(itemsToAdd.map((item) => item.item.name).join(', '))}...`\n\t\t);\n\n\t\tconst itemsResult = await resolveAndFetchAllItems(itemsToAdd);\n\t\tif (itemsResult.isErr()) {\n\t\t\tspinner.stop('Failed to fetch items');\n\t\t\treturn err(itemsResult.error);\n\t\t}\n\t\tspinner.stop(`Fetched ${pc.cyan(itemsToAdd.map((item) => item.item.name).join(', '))}`);\n\t\tconst items = itemsResult.value;\n\n\t\tconst itemPathsResult = await getPathsForItems({ items, config, options });\n\t\tif (itemPathsResult.isErr()) return err(itemPathsResult.error);\n\t\tconst { itemPaths } = itemPathsResult.value;\n\n\t\tconst prepareUpdatesResult = await prepareUpdates({\n\t\t\tconfigResult: { path: configPath, config },\n\t\t\toptions: {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tyes: options.yes,\n\t\t\t},\n\t\t\titemPaths,\n\t\t\titems,\n\t\t});\n\t\tif (prepareUpdatesResult.isErr()) return err(prepareUpdatesResult.error);\n\t\tconst {\n\t\t\tneededDependencies: neededDeps,\n\t\t\tneededEnvVars,\n\t\t\tneededFiles,\n\t\t} = prepareUpdatesResult.value;\n\n\t\tconst updatedFilesResult = await updateFiles({ files: neededFiles, options });\n\t\tif (updatedFilesResult.isErr()) return err(updatedFilesResult.error);\n\t\tconst updatedFiles = updatedFilesResult.value;\n\n\t\tlog.success(`Updated ${pc.green(updatedFiles.length)} files`);\n\n\t\tif (neededEnvVars) {\n\t\t\tconst updatedEnvVars = await promptAddEnvVars(neededEnvVars, { options });\n\n\t\t\tif (updatedEnvVars) {\n\t\t\t\tlog.success(\n\t\t\t\t\t`Updated ${pc.green(Object.keys(updatedEnvVars).length)} environment variables`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tneededDependencies.dependencies.push(...neededDeps.dependencies);\n\t\tneededDependencies.devDependencies.push(...neededDeps.devDependencies);\n\t}\n\n\tif (config.paths !== undefined) {\n\t\tconst updateConfigPathsResult = await updateConfigPaths(config.paths, {\n\t\t\tconfig: { path: configPath, code: configCode },\n\t\t});\n\t\tif (updateConfigPathsResult.isErr()) return err(updateConfigPathsResult.error);\n\t\tconfigCode = updateConfigPathsResult.value;\n\n\t\tconst writeConfigResult = writeFileSync(configPath, configCode);\n\t\tif (writeConfigResult.isErr()) return err(writeConfigResult.error);\n\n\t\tlog.success(`Updated paths`);\n\t}\n\n\tawait promptInstallDependenciesByEcosystem(neededDependencies, { options, config });\n\n\treturn ok({ registries: registriesArg });\n}\n\nasync function initRegistry(\n\tregistry: ResolvedRegistry,\n\t{\n\t\tconfigCode,\n\t\tconfigPath,\n\t\toptions,\n\t\tpluginChoices,\n\t\tconfig,\n\t}: {\n\t\tconfigCode: string;\n\t\tconfigPath: AbsolutePath;\n\t\toptions: InitOptions;\n\t\tpluginChoices: Map<string, { install: boolean; plugin: Plugin }>;\n\t\tconfig: Config | null;\n\t}\n): Promise<\n\tResult<string, InvalidKeyTypeError | ConfigObjectNotFoundError | CouldNotFindJsrepoImportError>\n> {\n\tconst initPluginsResult = await initPlugins(registry, {\n\t\tconfigCode,\n\t\tconfigPath,\n\t\toptions,\n\t\tpluginChoices,\n\t});\n\tif (initPluginsResult.isErr()) return err(initPluginsResult.error);\n\tconfigCode = initPluginsResult.value;\n\n\tconst initDefaultPathsResult = await initDefaultPaths(registry, {\n\t\tconfigCode,\n\t\tconfigPath,\n\t\toptions,\n\t\tconfig,\n\t});\n\tif (initDefaultPathsResult.isErr()) return err(initDefaultPathsResult.error);\n\tconfigCode = initDefaultPathsResult.value;\n\n\treturn ok(configCode);\n}\n\nasync function initPlugins(\n\tregistry: ResolvedRegistry,\n\t{\n\t\tconfigCode,\n\t\tconfigPath,\n\t\toptions,\n\t\tpluginChoices,\n\t}: {\n\t\tconfigCode: string;\n\t\tconfigPath: AbsolutePath;\n\t\toptions: InitOptions;\n\t\tpluginChoices: Map<string, { install: boolean; plugin: Plugin }>;\n\t}\n): Promise<\n\tResult<string, InvalidKeyTypeError | ConfigObjectNotFoundError | CouldNotFindJsrepoImportError>\n> {\n\tif (!registry.manifest.plugins) return ok(configCode);\n\n\tfor (const [key, plugins] of Object.entries(registry.manifest.plugins)) {\n\t\tconst getWantedPluginsResult = await getWantedPlugins(plugins, {\n\t\t\tpluginChoices,\n\t\t\toptions,\n\t\t\ttype: key.slice(0, -1) as never,\n\t\t\tconfig: { path: configPath, code: configCode },\n\t\t});\n\t\tif (getWantedPluginsResult.isErr()) return err(getWantedPluginsResult.error);\n\t\tconst wantedPlugins = getWantedPluginsResult.value;\n\t\tconst addPluginsToConfigResult = await addPluginsToConfig({\n\t\t\tplugins: wantedPlugins,\n\t\t\tkey: key as keyof typeof registry.manifest.plugins,\n\t\t\tconfig: { path: configPath, code: configCode },\n\t\t});\n\t\tif (addPluginsToConfigResult.isErr()) return err(addPluginsToConfigResult.error);\n\t\tconfigCode = addPluginsToConfigResult.value;\n\t}\n\n\treturn ok(configCode);\n}\n\nasync function initDefaultPaths(\n\tregistry: ResolvedRegistry,\n\t{\n\t\tconfigCode,\n\t\tconfigPath,\n\t\tconfig,\n\t\toptions,\n\t}: {\n\t\tconfigCode: string;\n\t\tconfigPath: string;\n\t\tconfig: Config | null;\n\t\toptions: InitOptions;\n\t}\n) {\n\tconst types = Array.from(\n\t\tnew Set(\n\t\t\tregistry.manifest.items.flatMap((item) =>\n\t\t\t\titem.files\n\t\t\t\t\t.filter((file) => file.target === undefined)\n\t\t\t\t\t.map((file) => normalizeItemTypeForPath(file.type))\n\t\t\t)\n\t\t)\n\t);\n\n\tlet paths = config?.paths ?? {};\n\n\tif (!options.yes && types.length > 0) {\n\t\tconst configurePaths = await multiselect({\n\t\t\tmessage: 'Which paths would you like to configure?',\n\t\t\toptions: types.map((type) => ({\n\t\t\t\tlabel: type,\n\t\t\t\tvalue: type,\n\t\t\t\thint: registry.manifest.defaultPaths?.[type]\n\t\t\t\t\t? `Default: ${registry.manifest.defaultPaths?.[type]}`\n\t\t\t\t\t: undefined,\n\t\t\t})),\n\t\t\trequired: false,\n\t\t});\n\n\t\tif (isCancel(configurePaths)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tif (configurePaths.length > 0) {\n\t\t\tfor (const type of configurePaths) {\n\t\t\t\tconst configuredValue = paths[type] ?? registry.manifest.defaultPaths?.[type];\n\n\t\t\t\tconst categoryPath = await text({\n\t\t\t\t\tmessage: `Where should ${type} be added in your project?`,\n\t\t\t\t\tvalidate(value) {\n\t\t\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t\t\t},\n\t\t\t\t\tplaceholder: configuredValue ? configuredValue : `./src/${type}`,\n\t\t\t\t\tdefaultValue: configuredValue,\n\t\t\t\t\tinitialValue: configuredValue,\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(categoryPath)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\tpaths[type] = categoryPath;\n\t\t\t}\n\t\t}\n\t}\n\n\tpaths = {\n\t\t...registry.manifest.defaultPaths,\n\t\t...paths,\n\t};\n\n\treturn await updateConfigPaths(paths, { config: { path: configPath, code: configCode } });\n}\n\nasync function getWantedPlugins(\n\tplugins: RegistryPlugin[],\n\t{\n\t\tpluginChoices,\n\t\toptions,\n\t\ttype,\n\t\tconfig,\n\t}: {\n\t\tpluginChoices: Map<string, { install: boolean; plugin: Plugin }>;\n\t\toptions: InitOptions;\n\t\ttype: 'provider' | 'transform' | 'language';\n\t\tconfig: { path: AbsolutePath; code: string };\n\t}\n): Promise<Result<Plugin[], InvalidPluginError>> {\n\tconst wantedPlugins: Plugin[] = [];\n\tconst addPlugin = (plugin: Plugin, install: boolean) => {\n\t\tpluginChoices.set(plugin.packageName, { install, plugin });\n\t\twantedPlugins.push(plugin);\n\t};\n\tconst unAddedPlugins = await neededPlugins({ config, plugins });\n\tfor (const plugin of unAddedPlugins) {\n\t\tif (pluginChoices.has(plugin.package)) continue;\n\t\tconst parsePluginNameResult = parsePluginName(plugin.package, type);\n\t\tif (parsePluginNameResult.isErr()) return err(parsePluginNameResult.error);\n\t\tconst pluginName = parsePluginNameResult.value.name;\n\t\tconst mappedPlugin: Plugin = {\n\t\t\tname: pluginName,\n\t\t\tpackageName: plugin.package,\n\t\t\tversion: plugin.version,\n\t\t};\n\t\tif (plugin.optional !== true) {\n\t\t\taddPlugin(mappedPlugin, true);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet shouldAddPlugin = options.yes;\n\t\tif (!options.yes) {\n\t\t\tconst response = await confirm({\n\t\t\t\tmessage: `Would you like to add the ${pc.cyan(plugin.package)} ${type} plugin?`,\n\t\t\t});\n\n\t\t\tif (isCancel(response)) {\n\t\t\t\tcancel('Canceled!');\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\n\t\t\tshouldAddPlugin = response;\n\t\t}\n\n\t\tif (!shouldAddPlugin) {\n\t\t\tpluginChoices.set(plugin.package, { install: false, plugin: mappedPlugin });\n\t\t\tcontinue;\n\t\t}\n\n\t\taddPlugin(mappedPlugin, true);\n\t}\n\treturn ok(wantedPlugins);\n}\n\nfunction initBlankConfig() {\n\treturn `import { defineConfig } from 'jsrepo';\n\nexport default defineConfig({\n    // configure where stuff comes from here\n    registries: [],\n    // configure where stuff goes here\n    paths: {},\n});`;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/publish.ts",
    "content": "import { buffer } from 'node:stream/consumers';\nimport { createGzip } from 'node:zlib';\nimport { log } from '@clack/prompts';\nimport { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport semver from 'semver';\nimport tar from 'tar-stream';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tforEachRegistry,\n\thasRegistries,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport type { DistributedOutputItem, DistributedOutputManifest } from '@/outputs/distributed';\nimport { DEFAULT_PROVIDERS } from '@/providers';\nimport { type jsrepo, NAME_REGEX } from '@/providers/jsrepo';\nimport { type BuildResult, buildRegistry, MANIFEST_FILE } from '@/utils/build';\nimport type { Config } from '@/utils/config';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport {\n\ttype CLIError,\n\tConfigNotFoundError,\n\tInvalidRegistryNameError,\n\tInvalidRegistryVersionError,\n\tJsrepoError,\n\tNoPackageJsonFoundError,\n\tNoProviderFoundError,\n\tNoRegistriesError,\n} from '@/utils/errors';\nimport { existsSync, readFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport { findNearestPackageJson, type PackageJson } from '@/utils/package';\nimport { joinAbsolute } from '@/utils/path';\nimport { initLogging, intro, outro } from '@/utils/prompts';\nimport { TokenManager } from '@/utils/token-manager';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tdryRun: z.boolean(),\n\tverbose: z.boolean(),\n});\n\nexport type PublishOptions = z.infer<typeof schema>;\n\nexport const publish = new Command('publish')\n\t.description('Publish your registry to jsrepo.com.')\n\t.argument(\n\t\t'[registries...]',\n\t\t'One or more registries to publish. If not provided, publishes all registries.'\n\t)\n\t.option('--dry-run', 'Build the registry without publishing.', false)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.verbose)\n\t.action(async (registries, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\t\tif (!configResult) error(new ConfigNotFoundError(options.cwd));\n\n\t\tconst config = configResult.config;\n\t\tconst cwd = path.dirname(configResult.path) as AbsolutePath;\n\t\tconst publishOptions = { ...options, cwd };\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'publish', options: publishOptions },\n\t\t\t{ cwd, yes: false }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(\n\t\t\trunPublish(registries, {\n\t\t\t\tconfig,\n\t\t\t\toptions: publishOptions,\n\t\t\t})\n\t\t);\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'publish', options: publishOptions, result },\n\t\t\t{ cwd }\n\t\t);\n\n\t\t// if any of the registries failed to publish, exit with an error\n\t\tif (\n\t\t\tresult.publishedRegistries.some(\n\t\t\t\t(registry) => !registry.skipped && registry.result.isErr()\n\t\t\t)\n\t\t) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n\nexport type PublishCommandResult = {\n\tduration: number;\n\tdryRun: boolean;\n\tpublishedRegistries: (\n\t\t| {\n\t\t\t\tskipped: true;\n\t\t\t\tname: string;\n\t\t  }\n\t\t| {\n\t\t\t\tskipped: false;\n\t\t\t\tname: string;\n\t\t\t\tversion: string;\n\t\t\t\tresult: Result<{ version: string; tag?: string }, CLIError>;\n\t\t  }\n\t)[];\n};\n\ntype JsrepoProvider = ReturnType<typeof jsrepo>;\n\nexport async function runPublish(\n\tregistries: string[],\n\t{ options, config }: { options: PublishOptions; config: Config }\n): Promise<Result<PublishCommandResult, CLIError>> {\n\tconst { verbose: _, spinner } = initLogging({ options });\n\n\tconst publishStart = performance.now();\n\n\tif (options.dryRun) {\n\t\tlog.warn(\n\t\t\t`${pc.bgYellow(pc.black(' DRY RUN '))} ${pc.dim('No registries will be published.')}`\n\t\t);\n\t}\n\n\tif (!hasRegistries(config)) return err(new NoRegistriesError());\n\n\tconst providers = config.providers ?? DEFAULT_PROVIDERS;\n\tconst jsrepoProvider = providers.find((p) => p.name === 'jsrepo') as JsrepoProvider | undefined;\n\tif (!jsrepoProvider) return err(new NoProviderFoundError('jsrepo'));\n\n\tconst tokenManager = new TokenManager();\n\tconst token = tokenManager.get(jsrepoProvider, undefined);\n\tif (!token) {\n\t\treturn err(\n\t\t\tnew JsrepoError('You need to be authenticated to publish your registry.', {\n\t\t\t\tsuggestion: `Run ${pc.bold(`\\`jsrepo auth jsrepo\\``)} to authenticate or set the ${pc.bold(\n\t\t\t\t\t`JSREPO_TOKEN`\n\t\t\t\t)} environment variable.`,\n\t\t\t})\n\t\t);\n\t}\n\n\tconst pkg = findNearestPackageJson(options.cwd);\n\tif (!pkg) return err(new NoPackageJsonFoundError());\n\n\tconst buildStart = performance.now();\n\n\tspinner.start(\n\t\t`Building ${registries.length > 0 ? pc.cyan(registries.join(', ')) : 'all registries'}...`\n\t);\n\n\tconst buildResults = await forEachRegistry<\n\t\tResult<\n\t\t\t{ skipped: true; name: string } | { skipped: false; buildResult: BuildResult },\n\t\t\tCLIError\n\t\t>\n\t>(\n\t\tconfig,\n\t\tasync (registry) => {\n\t\t\tif (registries.length > 0 && !registries.includes(registry.name))\n\t\t\t\treturn ok({ skipped: true, name: registry.name });\n\n\t\t\tconst buildResult = await buildRegistry(registry, { options, config });\n\t\t\tif (buildResult.isErr()) return err(buildResult.error);\n\t\t\treturn ok({ skipped: false, buildResult: buildResult.value });\n\t\t},\n\t\t{ cwd: options.cwd }\n\t);\n\n\tconst buildEnd = performance.now();\n\n\tspinner.stop(\n\t\t`Built ${pc.green(buildResults.length)} ${buildResults.length > 1 ? 'registries' : 'registry'} in ${pc.green(\n\t\t\t`${(buildEnd - buildStart).toFixed(2)}ms`\n\t\t)}`\n\t);\n\n\tconst preparedRegistries: BuildResult[] = [];\n\tconst skippedRegistries: { skipped: true; name: string }[] = [];\n\tfor (const buildResult of buildResults) {\n\t\tif (buildResult.isErr()) return err(buildResult.error);\n\n\t\tif (buildResult.value.skipped) {\n\t\t\tskippedRegistries.push({ skipped: true, name: buildResult.value.name });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst result = validateRegistryPrepublish(buildResult.value.buildResult, {\n\t\t\tpkg: pkg.package,\n\t\t});\n\t\tif (result.isErr()) return err(result.error);\n\t\tpreparedRegistries.push({\n\t\t\t...buildResult.value.buildResult,\n\t\t\tversion: result.value.version,\n\t\t});\n\t}\n\n\tspinner.start(\n\t\t`Publishing ${pc.green(preparedRegistries.length)} ${preparedRegistries.length > 1 ? 'registries' : 'registry'}...`\n\t);\n\n\tconst publishedRegistries: {\n\t\tskipped: false;\n\t\tname: string;\n\t\tversion: string;\n\t\tresult: Result<{ version: string; tag?: string }, CLIError>;\n\t}[] = [];\n\tfor (const registry of preparedRegistries) {\n\t\tconst publishResult = await publishRegistry(registry, {\n\t\t\ttoken,\n\t\t\tprovider: jsrepoProvider,\n\t\t\toptions,\n\t\t});\n\t\tpublishedRegistries.push({\n\t\t\tskipped: false,\n\t\t\tname: registry.name,\n\t\t\t// we just validated this above\n\t\t\tversion: registry.version!,\n\t\t\tresult: publishResult,\n\t\t});\n\t}\n\n\tspinner.stop(\n\t\t`Published ${pc.green(publishedRegistries.length)} ${publishedRegistries.length > 1 ? 'registries' : 'registry'}`\n\t);\n\n\tconst publishEnd = performance.now();\n\n\treturn ok({\n\t\tduration: publishEnd - publishStart,\n\t\tdryRun: options.dryRun,\n\t\tpublishedRegistries: [...skippedRegistries, ...publishedRegistries],\n\t});\n}\n\nfunction validateRegistryPrepublish(\n\tbuildResult: BuildResult,\n\t{ pkg }: { pkg: Partial<PackageJson> }\n): Result<{ name: string; version: string }, CLIError> {\n\tconst [scope, registryName, ...rest] = buildResult.name.split('/');\n\n\tif (rest.length > 0) {\n\t\treturn err(new InvalidRegistryNameError(buildResult.name));\n\t}\n\n\tif (!scope || !scope.startsWith('@')) {\n\t\treturn err(new InvalidRegistryNameError(buildResult.name));\n\t}\n\n\tif (!scope.slice(1).match(NAME_REGEX)) {\n\t\treturn err(new InvalidRegistryNameError(buildResult.name));\n\t}\n\n\tif (!registryName || !registryName.match(NAME_REGEX)) {\n\t\treturn err(new InvalidRegistryNameError(buildResult.name));\n\t}\n\n\tif (buildResult.version === undefined) {\n\t\treturn err(new InvalidRegistryVersionError(buildResult.version, buildResult.name));\n\t}\n\n\tlet version: string | undefined;\n\tif (buildResult.version === 'package' && pkg.version !== undefined) {\n\t\tversion = pkg.version;\n\t} else {\n\t\tversion = buildResult.version;\n\t}\n\n\tif (version === undefined) {\n\t\treturn err(new InvalidRegistryVersionError(version, buildResult.name));\n\t}\n\n\tconst valid = semver.valid(version);\n\n\tif (!valid) {\n\t\treturn err(new InvalidRegistryVersionError(version, buildResult.name));\n\t}\n\n\treturn ok({ name: buildResult.name, version });\n}\n\nasync function publishRegistry(\n\tregistry: BuildResult,\n\t{\n\t\ttoken,\n\t\tprovider,\n\t\toptions,\n\t}: { token: string; provider: JsrepoProvider; options: PublishOptions }\n): Promise<Result<{ version: string; tag?: string }, CLIError>> {\n\tconst collectResult = collectRegistryFiles(registry, options.cwd);\n\tif (collectResult.isErr()) return err(collectResult.error);\n\tconst files = collectResult.value;\n\n\tconst pack = tar.pack();\n\tfor (const file of files) {\n\t\tpack.entry({ name: file.name }, file.content);\n\t}\n\tpack.finalize();\n\n\tconst tarBuffer = await buffer(pack.pipe(createGzip()));\n\n\ttry {\n\t\tconst response = await fetch(`${provider.baseUrl}/api/publish/v3`, {\n\t\t\tbody: tarBuffer,\n\t\t\theaders: {\n\t\t\t\t'content-type': 'application/gzip',\n\t\t\t\t'content-encoding': 'gzip',\n\t\t\t\t'x-api-key': token,\n\t\t\t\t'x-dry-run': options.dryRun ? '1' : '0',\n\t\t\t\t'x-access': registry.access ?? 'public',\n\t\t\t},\n\t\t\tmethod: 'POST',\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst error = (await response.json()) as { message?: string };\n\t\t\treturn err(\n\t\t\t\tnew JsrepoError(\n\t\t\t\t\t`Failed to publish ${pc.bold(registry.name)}: ${response.status} ${\n\t\t\t\t\t\tresponse.statusText\n\t\t\t\t\t} ${error.message}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\tconst data = await response.json();\n\t\treturn ok({ version: data.version, tag: data.tag });\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to publish registry: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nfunction collectRegistryFiles(\n\tbuildResult: BuildResult,\n\tcwd: AbsolutePath\n): Result<{ name: string; content: string }[], JsrepoError> {\n\tconst files: { name: string; content: string }[] = [];\n\tconst readmePath = joinAbsolute(cwd, 'README.md');\n\tif (existsSync(readmePath)) {\n\t\tconst readmeContent = readFileSync(readmePath);\n\t\tif (readmeContent.isErr()) return err(readmeContent.error);\n\t\tfiles.push({ name: 'README.md', content: readmeContent.value });\n\t}\n\n\tconst manifest: DistributedOutputManifest = {\n\t\tname: buildResult.name,\n\t\tauthors: buildResult.authors,\n\t\tbugs: buildResult.bugs,\n\t\tdescription: buildResult.description,\n\t\thomepage: buildResult.homepage,\n\t\trepository: buildResult.repository,\n\t\ttags: buildResult.tags,\n\t\tversion: buildResult.version,\n\t\tmeta: buildResult.meta,\n\t\ttype: 'distributed',\n\t\tplugins: buildResult.plugins,\n\t\tdefaultPaths: buildResult.defaultPaths,\n\t\titems: buildResult.items.map((item) => ({\n\t\t\tname: item.name,\n\t\t\ttitle: item.title,\n\t\t\tdescription: item.description,\n\t\t\ttype: item.type,\n\t\t\tadd: item.add,\n\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\tdependencies: item.dependencies,\n\t\t\tdevDependencies: item.devDependencies,\n\t\t\tenvVars: item.envVars,\n\t\t\tfiles: item.files.map((file) => ({\n\t\t\t\ttype: file.type,\n\t\t\t\trole: file.role,\n\t\t\t\tpath: file.path,\n\t\t\t\ttarget: file.target,\n\t\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\t\tdependencies: file.dependencies,\n\t\t\t\tdevDependencies: file.devDependencies,\n\t\t\t})),\n\t\t\tcategories: item.categories,\n\t\t\tmeta: item.meta,\n\t\t})),\n\t};\n\tfiles.push({\n\t\tname: MANIFEST_FILE,\n\t\tcontent: JSON.stringify(manifest),\n\t});\n\n\tfor (const item of buildResult.items) {\n\t\tconst outputItem: DistributedOutputItem = {\n\t\t\tname: item.name,\n\t\t\ttitle: item.title,\n\t\t\tdescription: item.description,\n\t\t\ttype: item.type,\n\t\t\tadd: item.add,\n\t\t\tfiles: item.files.map((file) => ({\n\t\t\t\ttype: file.type,\n\t\t\t\trole: file.role,\n\t\t\t\tcontent: file.content,\n\t\t\t\tpath: file.path,\n\t\t\t\t_imports_: file._imports_,\n\t\t\t\ttarget: file.target,\n\t\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\t\tdependencies: file.dependencies,\n\t\t\t\tdevDependencies: file.devDependencies,\n\t\t\t})),\n\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\tdependencies: item.dependencies,\n\t\t\tdevDependencies: item.devDependencies,\n\t\t\tenvVars: item.envVars,\n\t\t\tcategories: item.categories,\n\t\t\tmeta: item.meta,\n\t\t};\n\t\tfiles.push({\n\t\t\tname: `${item.name}.json`,\n\t\t\tcontent: JSON.stringify(outputItem),\n\t\t});\n\t}\n\n\treturn ok(files);\n}\n\nfunction formatResult(result: PublishCommandResult): string {\n\treturn `Completed in ${pc.green(`${result.duration.toFixed(2)}ms`)}\n${result.publishedRegistries.map((registry) => `   ${formatRegistryResult(registry)}`).join('\\n')}`;\n}\n\nfunction formatRegistryResult(\n\tregistry: PublishCommandResult['publishedRegistries'][number]\n): string {\n\tif (registry.skipped) {\n\t\treturn pc.dim(`${pc.dim(registry.name)} >> Skipped`);\n\t}\n\n\tif (registry.result.isErr()) {\n\t\treturn `${pc.cyan(registry.name)} ${pc.dim('→')} ${pc.red(registry.result.error.toString())}`;\n\t}\n\n\tconst { version, tag } = registry.result.value;\n\n\treturn `${pc.cyan(registry.name)} → ${pc.green(version)} ${pc.dim(tag ?? '')}`;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/update.ts",
    "content": "import { cancel, isCancel, multiselect } from '@clack/prompts';\nimport { Command } from 'commander';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\terror,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport { DEFAULT_PROVIDERS } from '@/providers';\nimport {\n\tgetPathsForItems,\n\tgetTargetPath,\n\ttype ItemDistributed,\n\ttype ItemRepository,\n\tparseWantedItems,\n\tprepareUpdates,\n\ttype ResolvedItem,\n\ttype ResolvedRegistry,\n\ttype ResolvedWantedItem,\n\tresolveAndFetchAllItems,\n\tresolveRegistries,\n\tresolveWantedItems,\n\tupdateFiles,\n} from '@/utils/add';\nimport type { RemoteDependency } from '@/utils/build';\nimport type { Config } from '@/utils/config';\nimport { updateConfigPaths } from '@/utils/config/mods/update-paths';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport { type CLIError, ConfigNotFoundError, NoItemsToUpdateError } from '@/utils/errors';\nimport { existsSync, readFileSync, writeFileSync } from '@/utils/fs';\nimport { runAfterHooks, runBeforeHooks } from '@/utils/hooks';\nimport {\n\tinitLogging,\n\tintro,\n\toutro,\n\tpromptAddEnvVars,\n\tpromptInstallDependenciesByEcosystem,\n} from '@/utils/prompts';\nimport { resolveWithRoles } from '@/utils/roles';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n\tall: z.boolean(),\n\tverbose: z.boolean(),\n\tregistry: z.string().optional(),\n\toverwrite: z.boolean(),\n\texpand: z.boolean(),\n\tmaxUnchanged: z.number(),\n\twith: z.array(z.string()).default([]),\n\twithExamples: z.boolean(),\n\twithDocs: z.boolean(),\n\twithTests: z.boolean(),\n});\n\nexport type UpdateOptions = z.infer<typeof schema>;\n\nexport const update = new Command('update')\n\t.description('Update items in your project.')\n\t.argument(\n\t\t'[items...]',\n\t\t'Names of the items you want to update. ex: (math, github/ieedan/std/math)'\n\t)\n\t.option('--registry <registry>', 'The registry to update items from.', undefined)\n\t.option('--all', 'Update all items in the project.', false)\n\t.option('--with <roles...>', 'Include files with the given roles.')\n\t.option('--with-examples', 'Deprecated. Use `--with example`.', false)\n\t.option('--with-docs', 'Deprecated. Use `--with doc`.', false)\n\t.option('--with-tests', 'Deprecated. Use `--with test`.', false)\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.addOption(commonOptions.verbose)\n\t.addOption(commonOptions.overwrite)\n\t.addOption(commonOptions.expand)\n\t.addOption(commonOptions.maxUnchanged)\n\t.action(async (blockNames, rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd,\n\t\t\tpromptForContinueIfNull: !options.yes,\n\t\t});\n\t\tif (!configResult) error(new ConfigNotFoundError(options.cwd));\n\n\t\tconst config = configResult.config;\n\t\tconst cwd = path.dirname(configResult.path) as AbsolutePath;\n\n\t\tawait runBeforeHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'update', options: { ...options, cwd } },\n\t\t\t{ cwd, yes: options.yes }\n\t\t);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(\n\t\t\trunUpdate(\n\t\t\t\tblockNames,\n\t\t\t\t// this way if the config is found in a higher directory we base everything off of that directory\n\t\t\t\t{ ...options, cwd },\n\t\t\t\tconfigResult\n\t\t\t)\n\t\t);\n\n\t\toutro(formatResult(result));\n\n\t\tawait runAfterHooks(\n\t\t\tconfig,\n\t\t\t{ command: 'update', options: { ...options, cwd }, result },\n\t\t\t{ cwd }\n\t\t);\n\t});\n\nexport type UpdateCommandResult = {\n\titems: (ItemRepository | ItemDistributed)[];\n\tupdatedFiles: string[];\n\tupdatedDependencies: {\n\t\tinstalled: boolean;\n\t\tdependencies: RemoteDependency[];\n\t};\n\tupdatedEnvVars: Record<string, string> | undefined;\n\tupdatedPaths: Config['paths'] | undefined;\n};\n\nexport async function runUpdate(\n\titemsArg: string[],\n\toptions: UpdateOptions,\n\tconfigResult: { path: AbsolutePath; config: Config } | null\n): Promise<Result<UpdateCommandResult, CLIError>> {\n\tconst { verbose: _, spinner } = initLogging({ options });\n\n\tconst config = configResult?.config;\n\tconst providers = config?.providers ?? DEFAULT_PROVIDERS;\n\tconst registries = options.registry ? [options.registry] : (config?.registries ?? []);\n\n\tlet resolvedWantedItems: ResolvedWantedItem[];\n\tif (itemsArg.length > 0) {\n\t\tconst parsedWantedItemsResult = parseWantedItems(itemsArg, {\n\t\t\tproviders,\n\t\t\tregistries,\n\t\t});\n\t\tif (parsedWantedItemsResult.isErr()) return err(parsedWantedItemsResult.error);\n\t\tconst { wantedItems, neededRegistries } = parsedWantedItemsResult.value;\n\n\t\tspinner.start(\n\t\t\t`Retrieving manifest${neededRegistries.length > 1 ? 's' : ''} from ${pc.cyan(neededRegistries.join(', '))}`\n\t\t);\n\n\t\tconst resolvedRegistriesResult = await resolveRegistries(neededRegistries, {\n\t\t\tcwd: options.cwd,\n\t\t\tproviders,\n\t\t});\n\t\tif (resolvedRegistriesResult.isErr()) {\n\t\t\tspinner.stop('Failed to retrieve manifests');\n\t\t\treturn err(resolvedRegistriesResult.error);\n\t\t}\n\t\tspinner.stop(\n\t\t\t`Retrieved manifest${neededRegistries.length > 1 ? 's' : ''} from ${pc.cyan(neededRegistries.join(', '))}`\n\t\t);\n\t\tconst resolvedRegistries = resolvedRegistriesResult.value;\n\n\t\tconst resolvedWantedItemsResult = await resolveWantedItems(wantedItems, {\n\t\t\tresolvedRegistries,\n\t\t\tnonInteractive: options.yes,\n\t\t});\n\t\tif (resolvedWantedItemsResult.isErr()) return err(resolvedWantedItemsResult.error);\n\t\tresolvedWantedItems = resolvedWantedItemsResult.value;\n\t} else {\n\t\tspinner.start(\n\t\t\t`Retrieving manifest${registries.length > 1 ? 's' : ''} from ${pc.cyan(registries.join(', '))}`\n\t\t);\n\n\t\tconst resolvedRegistriesResult = await resolveRegistries(registries, {\n\t\t\tcwd: options.cwd,\n\t\t\tproviders,\n\t\t});\n\t\tif (resolvedRegistriesResult.isErr()) {\n\t\t\tspinner.stop('Failed to retrieve manifests');\n\t\t\treturn err(resolvedRegistriesResult.error);\n\t\t}\n\t\tspinner.stop(\n\t\t\t`Retrieved manifest${registries.length > 1 ? 's' : ''} from ${pc.cyan(registries.join(', '))}`\n\t\t);\n\t\tconst resolvedRegistries = resolvedRegistriesResult.value;\n\n\t\tconst allItems: ResolvedWantedItem[] = Array.from(resolvedRegistries.entries()).flatMap(\n\t\t\t([_, registry]) => {\n\t\t\t\treturn registry.manifest.items\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(item) =>\n\t\t\t\t\t\t\t(item.add ?? 'when-added') === 'when-added' && item.name !== 'index'\n\t\t\t\t\t)\n\t\t\t\t\t.map((item) => ({ item, registry }));\n\t\t\t}\n\t\t);\n\n\t\tconst itemPathsResult = await getPathsForItems({\n\t\t\titems: allItems.map((item) => ({\n\t\t\t\tname: item.item.name,\n\t\t\t\ttype: item.item.type,\n\t\t\t\tfiles: item.item.files,\n\t\t\t\tregistry: item.registry,\n\t\t\t})),\n\t\t\tconfig,\n\t\t\toptions: {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\t// make non interactive\n\t\t\t\tyes: true,\n\t\t\t},\n\t\t\tcontinueOnNoPath: true,\n\t\t});\n\t\tif (itemPathsResult.isErr()) return err(itemPathsResult.error);\n\t\tconst { itemPaths } = itemPathsResult.value;\n\n\t\tconst updateCandidates: {\n\t\t\titem: ResolvedItem;\n\t\t\tregistry: ResolvedRegistry;\n\t\t}[] = [];\n\t\tfor (const { item, registry } of allItems) {\n\t\t\tconst itemPath = itemPaths[`${item.type}/${item.name}`]?.path;\n\t\t\tif (!itemPath) continue; // don't know where it is so we can't update it\n\n\t\t\tfor (const file of item.files) {\n\t\t\t\tlet filePath = file.path;\n\t\t\t\t// check to see if the file path changes after applying transforms\n\t\t\t\tfor (const transform of config?.transforms ?? []) {\n\t\t\t\t\tconst result = await transform.transform({\n\t\t\t\t\t\tcode: '', // content is not needed\n\t\t\t\t\t\tfileName: file.path,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tcwd: options.cwd,\n\t\t\t\t\t\t\tregistryUrl: registry.url,\n\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tfilePath = result.fileName ?? file.path;\n\t\t\t\t}\n\n\t\t\t\tconst filePathResult = getTargetPath(\n\t\t\t\t\t{ ...file, path: filePath },\n\t\t\t\t\t{ itemPath: { path: itemPath }, options }\n\t\t\t\t);\n\n\t\t\t\tif (!existsSync(filePathResult)) continue;\n\t\t\t\tupdateCandidates.push({ item: { ...item, registry }, registry });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (updateCandidates.length === 0) return err(new NoItemsToUpdateError());\n\n\t\tif (options.all) {\n\t\t\tresolvedWantedItems = updateCandidates.map(({ item, registry }) => ({\n\t\t\t\titem,\n\t\t\t\tregistry,\n\t\t\t}));\n\t\t} else {\n\t\t\tconst registryUrls = new Set<string>();\n\t\t\tfor (const { registry } of updateCandidates) {\n\t\t\t\tregistryUrls.add(registry.url);\n\t\t\t}\n\t\t\tconst areDifferentRegistryUrls = registryUrls.size > 1;\n\n\t\t\tconst selectedItems = await multiselect({\n\t\t\t\tmessage: 'Which items would you like to update?',\n\t\t\t\toptions: updateCandidates.map(({ item }) => ({\n\t\t\t\t\tlabel: areDifferentRegistryUrls\n\t\t\t\t\t\t? `${item.registry.url}/${item.name}`\n\t\t\t\t\t\t: item.name,\n\t\t\t\t\tvalue: `${item.registry.url}/${item.name}`,\n\t\t\t\t})),\n\t\t\t});\n\n\t\t\tif (isCancel(selectedItems)) {\n\t\t\t\tcancel('Canceled!');\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\n\t\t\tresolvedWantedItems = updateCandidates.filter(({ item }) =>\n\t\t\t\tselectedItems.includes(`${item.registry.url}/${item.name}`)\n\t\t\t);\n\t\t}\n\t}\n\n\tspinner.start(\n\t\t`Fetching ${pc.cyan(resolvedWantedItems.map((item) => item.item.name).join(', '))}...`\n\t);\n\n\tconst withRoles = resolveWithRoles(options);\n\n\tconst itemsResult = await resolveAndFetchAllItems(resolvedWantedItems, {\n\t\twithRoles,\n\t});\n\tif (itemsResult.isErr()) {\n\t\tspinner.stop('Failed to fetch items');\n\t\treturn err(itemsResult.error);\n\t}\n\tspinner.stop(\n\t\t`Fetched ${pc.cyan(resolvedWantedItems.map((item) => item.item.name).join(', '))}`\n\t);\n\tconst items = itemsResult.value;\n\n\tconst itemPathsResult = await getPathsForItems({ items, config, options });\n\tif (itemPathsResult.isErr()) return err(itemPathsResult.error);\n\tconst { itemPaths, updatedPaths } = itemPathsResult.value;\n\n\tconst prepareUpdatesResult = await prepareUpdates({\n\t\tconfigResult,\n\t\toptions: { cwd: options.cwd, yes: options.yes, withRoles },\n\t\titemPaths,\n\t\titems,\n\t});\n\tif (prepareUpdatesResult.isErr()) return err(prepareUpdatesResult.error);\n\tconst { neededDependencies, neededEnvVars, neededFiles } = prepareUpdatesResult.value;\n\n\tconst updatedFilesResult = await updateFiles({ files: neededFiles, options });\n\tif (updatedFilesResult.isErr()) return err(updatedFilesResult.error);\n\tconst updatedFiles = updatedFilesResult.value;\n\n\tif (configResult && updatedPaths) {\n\t\tconst configCodeResult = readFileSync(configResult.path);\n\t\tif (configCodeResult.isErr()) return err(configCodeResult.error);\n\t\tconst configCode = configCodeResult.value;\n\t\tconst updatedConfigCode = await updateConfigPaths(config?.paths ?? updatedPaths, {\n\t\t\tconfig: { path: configResult.path, code: configCode },\n\t\t});\n\t\tif (updatedConfigCode.isErr()) return err(updatedConfigCode.error);\n\t\tconst writeResult = writeFileSync(configResult.path, updatedConfigCode.value);\n\t\tif (writeResult.isErr()) return err(writeResult.error);\n\t}\n\n\tlet updatedEnvVars: Record<string, string> | undefined;\n\tif (neededEnvVars) {\n\t\tconst promptAddEnvVarsResult = await promptAddEnvVars(neededEnvVars, { options });\n\t\tif (promptAddEnvVarsResult.isErr()) return err(promptAddEnvVarsResult.error);\n\t\tupdatedEnvVars = promptAddEnvVarsResult.value;\n\t}\n\n\tconst updatedDependencies = await promptInstallDependenciesByEcosystem(neededDependencies, {\n\t\toptions,\n\t\tconfig,\n\t});\n\n\treturn ok({\n\t\titems,\n\t\tupdatedDependencies,\n\t\tupdatedEnvVars,\n\t\tupdatedFiles,\n\t\tupdatedPaths,\n\t});\n}\n\nfunction formatResult(result: UpdateCommandResult): string {\n\tconst parts: string[] = [\n\t\t`Updated ${pc.cyan(result.items.map((item) => item.name).join(', '))} in your project.`,\n\t];\n\n\tif (result.updatedFiles.length > 0) {\n\t\tparts.push(\n\t\t\t`    Updated ${pc.green(result.updatedFiles.length)} ${\n\t\t\t\tresult.updatedFiles.length === 1 ? 'file' : 'files'\n\t\t\t}.`\n\t\t);\n\t}\n\n\tif (result.updatedPaths && Object.keys(result.updatedPaths).length > 0) {\n\t\tparts.push(\n\t\t\t`    Updated ${pc.green(Object.keys(result.updatedPaths).length)} ${\n\t\t\t\tObject.keys(result.updatedPaths).length === 1 ? 'path' : 'paths'\n\t\t\t}.`\n\t\t);\n\t}\n\n\tif (result.updatedDependencies.dependencies.length > 0) {\n\t\tif (result.updatedDependencies.installed) {\n\t\t\tparts.push(\n\t\t\t\t`    Installed ${pc.green(result.updatedDependencies.dependencies.length)} ${\n\t\t\t\t\tresult.updatedDependencies.dependencies.length === 1\n\t\t\t\t\t\t? 'dependency'\n\t\t\t\t\t\t: 'dependencies'\n\t\t\t\t}.`\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(\n\t\t\t\t`    Skipped installation of ${pc.cyan(\n\t\t\t\t\tresult.updatedDependencies.dependencies\n\t\t\t\t\t\t.map((dep) => `${dep.name}${dep.version ? `@${dep.version}` : ''}`)\n\t\t\t\t\t\t.join(', ')\n\t\t\t\t)}.`\n\t\t\t);\n\t\t}\n\t}\n\n\tif (result.updatedEnvVars && Object.keys(result.updatedEnvVars).length > 0) {\n\t\tparts.push(\n\t\t\t`    Updated ${pc.green(Object.keys(result.updatedEnvVars).length)} ${\n\t\t\t\tObject.keys(result.updatedEnvVars).length === 1\n\t\t\t\t\t? 'environment variable'\n\t\t\t\t\t: 'environment variables'\n\t\t\t}.`\n\t\t);\n\t}\n\n\treturn parts.join('\\n');\n}\n"
  },
  {
    "path": "packages/jsrepo/src/commands/utils.ts",
    "content": "import * as p from '@clack/prompts';\nimport { Option } from 'commander';\nimport type { Result } from 'nevereverthrow';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport type { Config, RegistryConfig, RegistryConfigArgs } from '@/utils/config';\nimport { InvalidOptionsError, JsrepoError } from '@/utils/errors';\nimport type { AbsolutePath } from '@/utils/types';\nimport { extractAsync } from '@/utils/utils';\nimport { safeValidate } from '@/utils/zod';\n\nexport const TRACE_ENV_VAR = 'JSREPO_TRACE';\n\nexport const defaultCommandOptionsSchema = z.object({\n\tcwd: z.string().transform((v) => v as AbsolutePath),\n});\n\nexport const commonOptions = {\n\tyes: new Option('--yes', 'Skip the confirmation prompt.').default(false),\n\tnoCache: new Option('--no-cache', 'Disable caching of resolved git urls.').default(false),\n\tverbose: new Option('--verbose', 'Include debug logs.').default(false),\n\toverwrite: new Option('--overwrite', 'Overwrite files without prompting.').default(false),\n\texpand: new Option('-E, --expand', 'Expands the diff so you see the entire file.').default(\n\t\tfalse\n\t),\n\tmaxUnchanged: new Option(\n\t\t'--max-unchanged <lines>',\n\t\t'Maximum unchanged lines that will show without being collapsed.'\n\t)\n\t\t.argParser((value) => Number.parseInt(value, 10))\n\t\t.default(5),\n\tcwd: new Option('--cwd <path>', 'The current working directory.').default(process.cwd()),\n};\n\nexport function parseOptions<T>(\n\tschema: z.ZodSchema<T>,\n\trawOptions: unknown\n): z.infer<typeof schema> {\n\treturn safeValidate(schema, rawOptions).match(\n\t\t(v) => v,\n\t\t(e) => error(new InvalidOptionsError(e.zodError))\n\t);\n}\n\n/**\n * Tries to run the command. If the command fails, it will log the error and exit the program.\n * @param command\n * @returns\n */\nexport async function tryCommand<T, E extends JsrepoError>(\n\tcommand: Promise<Result<T, E>>\n): Promise<T> {\n\ttry {\n\t\tconst result = await command;\n\t\tif (result.isErr()) return error(result.error);\n\t\treturn result.value;\n\t} catch (e) {\n\t\tif (e instanceof JsrepoError) error(e);\n\t\terror(\n\t\t\tnew JsrepoError(e instanceof Error ? e.message : String(e), {\n\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t})\n\t\t);\n\t}\n}\n\n/**\n * Log error and exit the program.\n * @param error - The error to print.\n */\nexport function error(err: Error): never {\n\treturn handleError(err);\n}\n\nfunction handleError(err: Error): never {\n\tp.log.message();\n\tif (process.env[TRACE_ENV_VAR] === '1') {\n\t\tconsole.trace(err);\n\t\tprocess.exit(1);\n\t} else {\n\t\tp.cancel(pc.red(err.toString()));\n\t\tprocess.exit(1);\n\t}\n}\n\n/**\n * Iterates over every registry in the config fetching the config on demand if provided as a callback.\n *\n * @param config\n * @param callback\n * @param args\n */\nexport async function forEachRegistry<T>(\n\tconfig: Config,\n\tcallback: (registry: RegistryConfig) => Promise<T>,\n\targs: RegistryConfigArgs[0]\n): Promise<T[]> {\n\tif (Array.isArray(config.registry)) {\n\t\tconst results = [];\n\t\tfor (const rc of config.registry) {\n\t\t\tresults.push(await callback(await extractAsync(rc, ...[args])));\n\t\t}\n\t\treturn results;\n\t} else {\n\t\treturn [await callback(await extractAsync(config.registry, ...[args]))];\n\t}\n}\n\nexport function hasRegistries(config: Config): boolean {\n\treturn Array.isArray(config.registry) ? config.registry.length > 0 : true;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/langs/css.ts",
    "content": "import * as c from 'css-dependency';\nimport { installDependencies, resolveImports, transformImports } from '@/langs/js';\nimport type { Language } from '@/langs/types';\n\nexport type CssOptions = {\n\t/**\n\t * Whether to allow tailwind directives to be parsed as imports.\n\t * @default true\n\t */\n\tallowTailwindDirectives: boolean;\n};\n\n/**\n * Detect dependencies in `.css`, `.scss`, and `.sass` files.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import { css } from \"jsrepo/langs\";\n *\n * export default defineConfig({\n *  // ...\n *  languages: [css()],\n * });\n * ```\n *\n * @param options - The options for the language plugin.\n */\nexport function css({ allowTailwindDirectives = true }: Partial<CssOptions> = {}): Language {\n\treturn {\n\t\tname: 'css',\n\t\tcanResolveDependencies: (fileName) =>\n\t\t\tfileName.endsWith('.css') || fileName.endsWith('.scss') || fileName.endsWith('.sass'),\n\t\tresolveDependencies: async (code, opts) => {\n\t\t\tconst importsResult = c.parse(code, { allowTailwindDirectives });\n\t\t\tif (importsResult.isErr())\n\t\t\t\treturn { localDependencies: [], dependencies: [], devDependencies: [] };\n\t\t\tlet imports = importsResult.unwrap();\n\n\t\t\t// filter out http imports\n\t\t\timports = imports.filter(\n\t\t\t\t(imp) => !imp.module.startsWith('https://') && !imp.module.startsWith('http://')\n\t\t\t);\n\n\t\t\treturn resolveImports(\n\t\t\t\timports.map((imp) => imp.module),\n\t\t\t\topts\n\t\t\t);\n\t\t},\n\t\ttransformImports,\n\t\tcanInstallDependencies: (ecosystem) => ecosystem === 'js',\n\t\tinstallDependencies,\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/langs/html.ts",
    "content": "import * as parse5 from 'parse5';\nimport { getImports, installDependencies, resolveImports, transformImports } from '@/langs/js';\nimport type { Language } from '@/langs/types';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport type HtmlOptions = {\n\tscripts: {\n\t\t/**\n\t\t * Whether to resolve the src attribute of a script tag as a dependency.\n\t\t * @default true\n\t\t */\n\t\tresolveSrc: boolean;\n\t\t/**\n\t\t * Whether to resolve dependencies within the code of a script tag.\n\t\t * @default true\n\t\t */\n\t\tresolveCode: boolean;\n\t};\n\t/**\n\t * Whether to resolve the href attribute of a link tag as a dependency.\n\t * @default true\n\t */\n\tresolveLinks: boolean;\n};\n\n// Minimal type definition for parse5 nodes\ninterface HtmlAttribute {\n\tname: string;\n\tvalue: string;\n}\n\ninterface HtmlNode {\n\ttagName?: string;\n\tattrs?: HtmlAttribute[];\n\tchildNodes?: HtmlNode[];\n}\n\n/**\n * Detect dependencies in `.html` files by parsing script src attributes and code and link href attributes.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import { html } from \"jsrepo/langs\";\n *\n * export default defineConfig({\n *  // ...\n *  languages: [html()],\n * });\n * ```\n *\n * @param options - The options for the language plugin.\n */\nexport function html({\n\tscripts = { resolveSrc: true, resolveCode: true },\n\tresolveLinks = true,\n}: Partial<HtmlOptions> = {}): Language {\n\treturn {\n\t\tname: 'html',\n\t\tcanResolveDependencies: (fileName) => fileName.endsWith('.html'),\n\t\tresolveDependencies: async (code, opts) => {\n\t\t\tconst ast = parse5.parse(code) as unknown as HtmlNode;\n\n\t\t\tconst imports: string[] = [];\n\n\t\t\t// Walk the AST tree to find imports\n\t\t\tconst walk = async (\n\t\t\t\tnode: HtmlNode | undefined,\n\t\t\t\tenter: (node: HtmlNode) => Promise<void>\n\t\t\t): Promise<void> => {\n\t\t\t\tif (!node) return;\n\n\t\t\t\tawait enter(node);\n\n\t\t\t\tif (node.childNodes && node.childNodes.length > 0) {\n\t\t\t\t\tfor (const n of node.childNodes) {\n\t\t\t\t\t\tawait walk(n, enter);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (ast.childNodes) {\n\t\t\t\tfor (const node of ast.childNodes) {\n\t\t\t\t\tawait walk(node, async (n) => {\n\t\t\t\t\t\t// Extract script src attributes\n\t\t\t\t\t\tif (n.tagName === 'script') {\n\t\t\t\t\t\t\tif (n.childNodes !== undefined) {\n\t\t\t\t\t\t\t\tif (scripts.resolveCode) {\n\t\t\t\t\t\t\t\t\tconst codeNode = n.childNodes[0];\n\t\t\t\t\t\t\t\t\tif (codeNode && 'nodeName' in codeNode && 'value' in codeNode) {\n\t\t\t\t\t\t\t\t\t\tconst code = codeNode.value as string;\n\t\t\t\t\t\t\t\t\t\tconst imps = await getImports(code, {\n\t\t\t\t\t\t\t\t\t\t\t...opts,\n\t\t\t\t\t\t\t\t\t\t\t// weird and hacky ik but this is an easy way to get oxc to parse the code as ts\n\t\t\t\t\t\t\t\t\t\t\tfileName: `${opts.fileName}.ts` as AbsolutePath,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\timports.push(...imps);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (scripts.resolveSrc) {\n\t\t\t\t\t\t\t\t\tfor (const attr of n.attrs || []) {\n\t\t\t\t\t\t\t\t\t\tif (attr.name === 'src') {\n\t\t\t\t\t\t\t\t\t\t\timports.push(attr.value);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Extract stylesheet link href attributes\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tresolveLinks &&\n\t\t\t\t\t\t\tn.tagName === 'link' &&\n\t\t\t\t\t\t\tn.attrs?.find(\n\t\t\t\t\t\t\t\t(attr) => attr.name === 'rel' && attr.value === 'stylesheet'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tfor (const attr of n.attrs) {\n\t\t\t\t\t\t\t\tif (attr.name === 'href' && !attr.value.startsWith('http')) {\n\t\t\t\t\t\t\t\t\timports.push(attr.value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn resolveImports(imports, opts);\n\t\t},\n\t\ttransformImports,\n\t\tcanInstallDependencies: (ecosystem) => ecosystem === 'js',\n\t\tinstallDependencies,\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/langs/index.ts",
    "content": "import { createRequire } from 'node:module';\nimport { type CssOptions, css } from '@/langs/css';\nimport { type HtmlOptions, html } from '@/langs/html';\nimport { type JsOptions, js } from '@/langs/js';\nimport { type SvelteOptions, svelte } from '@/langs/svelte';\nimport type { Language } from '@/langs/types';\nimport { type VueOptions, vue } from '@/langs/vue';\n\nconst require = createRequire(import.meta.url);\n\nfunction isModuleAvailable(moduleName: string): boolean {\n\ttry {\n\t\trequire.resolve(moduleName);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction createDefaultLangs(): Language[] {\n\tconst langs: Language[] = [js(), css(), html()];\n\n\tif (isModuleAvailable('svelte/compiler')) {\n\t\tlangs.push(svelte());\n\t}\n\n\tif (isModuleAvailable('vue/compiler-sfc')) {\n\t\tlangs.push(vue());\n\t}\n\n\treturn langs;\n}\n\nexport const DEFAULT_LANGS = createDefaultLangs();\n\nexport {\n\tjs,\n\ttype JsOptions,\n\tsvelte,\n\ttype SvelteOptions,\n\tvue,\n\ttype VueOptions,\n\tcss,\n\ttype CssOptions,\n\thtml,\n\ttype HtmlOptions,\n};\nexport * from '@/langs/types';\n"
  },
  {
    "path": "packages/jsrepo/src/langs/js.ts",
    "content": "import { builtinModules } from 'node:module';\nimport escapeStringRegexp from 'escape-string-regexp';\nimport { err, ok, Result } from 'nevereverthrow';\nimport { parse } from 'oxc-parser';\nimport { resolveCommand } from 'package-manager-detector';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { ModuleNotFoundError } from '@/api';\nimport type {\n\tImportTransform,\n\tInstallDependenciesOptions,\n\tLanguage,\n\tResolveDependenciesOptions,\n\tTransformImportsOptions,\n} from '@/langs/types';\nimport type { LocalDependency, RemoteDependency, UnresolvedImport } from '@/utils/build';\nimport { transformShadcnImports } from '@/utils/compat/shadcn';\nimport { existsSync, readdirSync, statSync } from '@/utils/fs';\nimport { findNearestPackageJson, shouldInstall } from '@/utils/package';\nimport { parsePackageName } from '@/utils/parse-package-name';\nimport { dirname, joinAbsolute } from '@/utils/path';\nimport { detectPackageManager, runCommands } from '@/utils/prompts';\nimport { createPathsMatcher, tryGetTsconfig } from '@/utils/tsconfig';\nimport type { AbsolutePath } from '@/utils/types';\nimport { noop } from '@/utils/utils';\nimport { validateNpmPackageName } from '@/utils/validate-npm-package-name';\nimport { InvalidImportWarning, UnresolvableDynamicImportWarning } from '@/utils/warnings';\n\nconst SUPPORTED_EXTENSIONS = ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.mts'] as const;\nconst SUPPORTED_PACKAGE_IMPORT_CONDITIONS = new Set(['default', 'import', 'node']);\n\n// biome-ignore lint/complexity/noBannedTypes: leave me alone for a minute\nexport type JsOptions = {};\n\n/**\n * Despite the name this is for javascript and typescript.\n */\nexport function js(_options: JsOptions = {}): Language {\n\treturn {\n\t\tname: 'javascript',\n\t\tcanResolveDependencies: (fileName) =>\n\t\t\tSUPPORTED_EXTENSIONS.some((ext) => fileName.endsWith(ext)),\n\t\tresolveDependencies: async (code, opts) =>\n\t\t\tresolveImports(await getImports(code, opts), opts),\n\t\ttransformImports,\n\t\tcanInstallDependencies: (ecosystem) => ecosystem === 'js',\n\t\tinstallDependencies,\n\t};\n}\n\n/**\n * Resolves dependencies for javascript and typescript.\n * @param code\n * @param opts\n */\nexport async function resolveImports(\n\timports: string[],\n\topts: ResolveDependenciesOptions\n): Promise<{\n\tlocalDependencies: LocalDependency[];\n\tdependencies: RemoteDependency[];\n\tdevDependencies: RemoteDependency[];\n}> {\n\tconst localDeps: LocalDependency[] = [];\n\tconst remoteDeps: RemoteDependency[] = [];\n\n\tfor (const specifier of imports) {\n\t\t// don't resolve node builtins or node: imports\n\t\tif (builtinModules.includes(specifier) || specifier.startsWith('node:')) continue;\n\n\t\tif (specifier.startsWith('.')) {\n\t\t\tconst actualPath = joinAbsolute(dirname(opts.fileName), specifier);\n\n\t\t\tconst mod = searchForModule(actualPath);\n\t\t\tif (mod === undefined) continue;\n\n\t\t\tlocalDeps.push({\n\t\t\t\tfileName: mod.path,\n\t\t\t\timport: specifier,\n\t\t\t\tcreateTemplate: () => ({}),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// if specifier wasn't a local dependency then it might be a path alias or subpath import\n\t\tconst resolvedAlias = tryResolveAliasImport(specifier, {\n\t\t\tfileName: opts.fileName,\n\t\t\tcwd: opts.cwd,\n\t\t});\n\n\t\tif (resolvedAlias.isOk() && resolvedAlias.value?.localDependency) {\n\t\t\tlocalDeps.push(resolvedAlias.value.localDependency);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parsedSpecifier =\n\t\t\tresolvedAlias.isOk() && resolvedAlias.value?.remoteSpecifier\n\t\t\t\t? resolvedAlias.value.remoteSpecifier\n\t\t\t\t: specifier;\n\t\tconst parsed = parsePackageName(parsedSpecifier);\n\n\t\t// if the specifier is not a valid package either then we know it's unresolvable and we throw an error\n\t\tif (parsed.isErr() && resolvedAlias.isErr()) {\n\t\t\tthrow new ModuleNotFoundError(specifier, { fileName: opts.fileName });\n\t\t}\n\n\t\tif (!parsed.isErr()) {\n\t\t\tconst depInfo = parsed.value;\n\n\t\t\tif (validateNpmPackageName(depInfo.name).validForNewPackages) {\n\t\t\t\tif (opts.excludeDeps.includes(depInfo.name)) continue;\n\n\t\t\t\tremoteDeps.push({\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\tname: depInfo.name,\n\t\t\t\t\tversion: depInfo.version,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\topts.warn(new InvalidImportWarning({ specifier, fileName: opts.fileName }));\n\t}\n\n\tconst { dependencies, devDependencies } = resolveRemoteDeps(\n\t\tremoteDeps,\n\t\tjoinAbsolute(opts.cwd, opts.fileName)\n\t);\n\n\treturn {\n\t\tlocalDependencies: localDeps,\n\t\tdependencies,\n\t\tdevDependencies,\n\t};\n}\n\nexport async function getImports(\n\tcode: string,\n\t{\n\t\tfileName,\n\t\twarn,\n\t}: {\n\t\tfileName: ResolveDependenciesOptions['fileName'];\n\t\twarn: ResolveDependenciesOptions['warn'];\n\t}\n): Promise<string[]> {\n\tconst result = await parse(fileName, code);\n\n\tconst modules = new Set<string>();\n\n\t// handle static imports\n\tfor (const imp of result.module.staticImports) {\n\t\tmodules.add(imp.moduleRequest.value);\n\t}\n\n\t// handle dynamic imports\n\tfor (const imp of result.module.dynamicImports) {\n\t\tconst fullImport = code.slice(imp.moduleRequest.start, imp.moduleRequest.end);\n\t\tconst parsedImport = await parse(fileName, fullImport);\n\n\t\t// an literal expression or a template literal with a single quasi\n\t\tconst isLiteral =\n\t\t\tparsedImport.program.body[0]?.type === 'ExpressionStatement' &&\n\t\t\t(parsedImport.program.body[0].expression.type === 'Literal' ||\n\t\t\t\t(parsedImport.program.body[0].expression.type === 'TemplateLiteral' &&\n\t\t\t\t\tparsedImport.program.body[0].expression.quasis.length === 1));\n\n\t\t// we can't resolve dynamic imports that are not literals so we just skip them and warn the user\n\t\tif (!isLiteral) {\n\t\t\twarn(new UnresolvableDynamicImportWarning({ specifier: fullImport, fileName }));\n\t\t\tcontinue;\n\t\t}\n\n\t\t// trim quotes from the start and end\n\t\tmodules.add(code.slice(imp.moduleRequest.start + 1, imp.moduleRequest.end - 1));\n\t}\n\n\t// handle `export x from y` syntax\n\tfor (const exp of result.module.staticExports) {\n\t\tfor (const entry of exp.entries) {\n\t\t\tif (entry.moduleRequest) {\n\t\t\t\tmodules.add(entry.moduleRequest.value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(modules);\n}\n\n/** Searches around for the module\n *\n * @param path\n */\nfunction searchForModule(\n\tmodPath: AbsolutePath,\n\t{ allowExtensionlessFallback = true }: { allowExtensionlessFallback?: boolean } = {}\n): { path: AbsolutePath; prettyPath: string; type: 'file' | 'directory' } | undefined {\n\tif (existsSync(modPath)) {\n\t\t// if it's already pointing to a file then return it\n\t\tif (!statSync(modPath)._unsafeUnwrap().isDirectory()) {\n\t\t\treturn {\n\t\t\t\tpath: modPath,\n\t\t\t\tprettyPath: modPath,\n\t\t\t\ttype: 'file',\n\t\t\t};\n\t\t}\n\n\t\tconst indexPath = searchForModule(joinAbsolute(modPath, 'index.js'));\n\n\t\tif (indexPath !== undefined) {\n\t\t\treturn {\n\t\t\t\tpath: indexPath.path,\n\t\t\t\tprettyPath: modPath,\n\t\t\t\ttype: 'file',\n\t\t\t};\n\t\t}\n\n\t\t// it's also possible to reference a file without providing the extension\n\n\t\tconst filePath = searchForModule(`${modPath}.js` as AbsolutePath);\n\n\t\tif (filePath === undefined) return undefined;\n\n\t\treturn {\n\t\t\tpath: filePath.path,\n\t\t\tprettyPath: modPath,\n\t\t\ttype: 'file',\n\t\t};\n\t}\n\n\tconst containing = joinAbsolute(modPath, '../');\n\n\t// if containing folder doesn't exist this can't exist\n\tif (!existsSync(containing)) return undefined;\n\n\tconst modParsed = path.parse(modPath);\n\n\t// sometimes it will point to .js because it will resolve in prod but not for us\n\tif (modParsed.ext === '.js') {\n\t\tconst newPath = `${modPath.slice(0, modPath.length - 3)}.ts` as AbsolutePath;\n\n\t\tif (existsSync(newPath)) return { path: newPath, prettyPath: modPath, type: 'file' };\n\t}\n\n\tif (!allowExtensionlessFallback) return undefined;\n\n\tconst filesResult = readdirSync(containing);\n\tif (filesResult.isErr()) return undefined;\n\tconst files = filesResult.value;\n\n\tfor (const file of files) {\n\t\tconst fileParsed = path.parse(file);\n\n\t\t// this way the extension doesn't matter\n\t\tif (fileParsed.name === modParsed.base) {\n\t\t\tconst filePath = joinAbsolute(containing, file);\n\t\t\tconst fileStats = statSync(filePath);\n\t\t\tif (fileStats.isErr()) continue;\n\t\t\tconst isDirectory = fileStats.value.isDirectory();\n\n\t\t\t// we remove the extension since it wasn't included by the user\n\t\t\tconst prettyPath = filePath.slice(0, filePath.length - fileParsed.ext.length);\n\n\t\t\treturn {\n\t\t\t\tpath: filePath,\n\t\t\t\tprettyPath: prettyPath,\n\t\t\t\ttype: isDirectory ? 'directory' : 'file',\n\t\t\t};\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\ntype AliasResolution =\n\t| {\n\t\t\tlocalDependency: LocalDependency;\n\t\t\tremoteSpecifier?: never;\n\t  }\n\t| {\n\t\t\tremoteSpecifier: string;\n\t\t\tlocalDependency?: never;\n\t  };\n\n/** Tries to resolve the modules as an alias using tsconfig or package.json imports. */\nfunction tryResolveAliasImport(\n\tmod: string,\n\t{ fileName, cwd }: { fileName: string; cwd: AbsolutePath }\n): Result<AliasResolution | null, string> {\n\tconst tsconfigAlias = tryResolveTsconfigAlias(mod, { fileName, cwd });\n\n\tif (tsconfigAlias.isErr()) return err(tsconfigAlias.error);\n\n\tif (tsconfigAlias.value !== null) {\n\t\treturn ok({ localDependency: tsconfigAlias.value });\n\t}\n\n\tif (!mod.startsWith('#')) return ok(null);\n\n\treturn tryResolvePackageImportsAlias(mod, { fileName, cwd });\n}\n\n/** Tries to resolve the modules as an alias using the tsconfig. */\nfunction tryResolveTsconfigAlias(\n\tmod: string,\n\t{ fileName, cwd }: { fileName: string; cwd: AbsolutePath }\n): Result<LocalDependency | null, string> {\n\tconst configResult = tryGetTsconfig(joinAbsolute(cwd, fileName));\n\n\tif (configResult.isErr()) return ok(null);\n\n\tconst config = configResult.value;\n\n\tif (config === null) return ok(null);\n\n\tconst matcher = createPathsMatcher(config, { cwd });\n\tconst hasMatchingPathAlias = hasMatchingTsconfigPathAlias(\n\t\tmod,\n\t\tconfig.config.compilerOptions?.paths\n\t);\n\tconst allowExtensionlessFallback =\n\t\t!isPotentiallyRemotePackageSpecifier(mod) || hasMatchingPathAlias;\n\n\tif (matcher) {\n\t\t// if the mod is actually remote the returns paths will be empty\n\t\tconst paths = matcher(mod);\n\n\t\tif (paths.length === 0) return ok(null);\n\n\t\tfor (const modPath of paths) {\n\t\t\tconst foundMod = searchForModule(modPath as AbsolutePath, {\n\t\t\t\tallowExtensionlessFallback,\n\t\t\t});\n\t\t\tif (!foundMod) continue;\n\t\t\treturn ok({\n\t\t\t\tfileName: foundMod.path,\n\t\t\t\timport: mod,\n\t\t\t\tcreateTemplate: () => ({}),\n\t\t\t});\n\t\t}\n\n\t\treturn err('Module not found');\n\t}\n\n\treturn ok(null);\n}\n\nfunction tryResolvePackageImportsAlias(\n\tmod: string,\n\t{ fileName, cwd }: { fileName: string; cwd: AbsolutePath }\n): Result<AliasResolution | null, string> {\n\tconst nearestPackage = findNearestPackageJson(dirname(joinAbsolute(cwd, fileName)));\n\tif (!nearestPackage) return ok(null);\n\n\tconst imports = nearestPackage.package.imports;\n\tif (!isRecord(imports)) return ok(null);\n\n\tconst matchedImport = getMatchingPackageImport(mod, imports);\n\tif (!matchedImport) return ok(null);\n\n\tconst resolvedTargets = resolvePackageImportTargets(\n\t\tmatchedImport.target,\n\t\tmatchedImport.wildcardMatch\n\t);\n\tif (resolvedTargets.length === 0) return err('Module not found');\n\n\tconst packageRoot = dirname(nearestPackage.path);\n\n\tfor (const target of resolvedTargets) {\n\t\tif (target.startsWith('.')) {\n\t\t\tconst foundMod = searchForModule(joinAbsolute(packageRoot, target));\n\t\t\tif (!foundMod) continue;\n\n\t\t\treturn ok({\n\t\t\t\tlocalDependency: {\n\t\t\t\t\tfileName: foundMod.path,\n\t\t\t\t\timport: mod,\n\t\t\t\t\tcreateTemplate: () => ({}),\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// package imports can also map to external npm packages\n\t\tif (parsePackageName(target).isOk()) {\n\t\t\treturn ok({ remoteSpecifier: target });\n\t\t}\n\t}\n\n\treturn err('Module not found');\n}\n\nfunction getMatchingPackageImport(\n\tspecifier: string,\n\timports: Record<string, unknown>\n): { target: unknown; wildcardMatch?: string } | null {\n\tif (Object.hasOwn(imports, specifier)) {\n\t\treturn { target: imports[specifier] };\n\t}\n\n\tconst patternMatches: Array<{\n\t\ttarget: unknown;\n\t\twildcardMatch: string;\n\t\tprefixLength: number;\n\t\tsuffixLength: number;\n\t}> = [];\n\n\tfor (const [key, target] of Object.entries(imports)) {\n\t\tconst wildcardIndex = key.indexOf('*');\n\t\tif (wildcardIndex === -1) continue;\n\n\t\tconst prefix = key.slice(0, wildcardIndex);\n\t\tconst suffix = key.slice(wildcardIndex + 1);\n\n\t\tif (!specifier.startsWith(prefix)) continue;\n\t\tif (!specifier.endsWith(suffix)) continue;\n\n\t\tpatternMatches.push({\n\t\t\ttarget,\n\t\t\twildcardMatch: specifier.slice(prefix.length, specifier.length - suffix.length),\n\t\t\tprefixLength: prefix.length,\n\t\t\tsuffixLength: suffix.length,\n\t\t});\n\t}\n\n\tif (patternMatches.length === 0) return null;\n\n\tpatternMatches.sort((a, b) => {\n\t\tif (a.prefixLength !== b.prefixLength) return b.prefixLength - a.prefixLength;\n\t\treturn b.suffixLength - a.suffixLength;\n\t});\n\n\tconst bestMatch = patternMatches[0];\n\tif (!bestMatch) return null;\n\n\treturn {\n\t\ttarget: bestMatch.target,\n\t\twildcardMatch: bestMatch.wildcardMatch,\n\t};\n}\n\nfunction resolvePackageImportTargets(target: unknown, wildcardMatch?: string): string[] {\n\tif (typeof target === 'string') {\n\t\tif (wildcardMatch === undefined) return [target];\n\t\treturn [target.replaceAll('*', wildcardMatch)];\n\t}\n\n\tif (Array.isArray(target)) {\n\t\treturn target.flatMap((entry) => resolvePackageImportTargets(entry, wildcardMatch));\n\t}\n\n\tif (isRecord(target)) {\n\t\tconst resolvedTargets: string[] = [];\n\n\t\tfor (const [condition, value] of Object.entries(target)) {\n\t\t\tif (!SUPPORTED_PACKAGE_IMPORT_CONDITIONS.has(condition)) continue;\n\t\t\tresolvedTargets.push(...resolvePackageImportTargets(value, wildcardMatch));\n\t\t}\n\n\t\treturn resolvedTargets;\n\t}\n\n\treturn [];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isPotentiallyRemotePackageSpecifier(specifier: string): boolean {\n\tif (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('#')) {\n\t\treturn false;\n\t}\n\n\t// Ignore url-style imports and protocol imports (e.g. node:, https:, npm:)\n\tif (specifier.includes(':')) return false;\n\n\treturn parsePackageName(specifier).isOk();\n}\n\nfunction hasMatchingTsconfigPathAlias(\n\tspecifier: string,\n\tpaths: Record<string, readonly string[]> | undefined\n): boolean {\n\tif (!paths) return false;\n\n\tfor (const pattern of Object.keys(paths)) {\n\t\tconst wildcardIndex = pattern.indexOf('*');\n\n\t\tif (wildcardIndex === -1) {\n\t\t\tif (pattern === specifier) return true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst prefix = pattern.slice(0, wildcardIndex);\n\t\tconst suffix = pattern.slice(wildcardIndex + 1);\n\t\tif (specifier.startsWith(prefix) && specifier.endsWith(suffix)) return true;\n\t}\n\n\treturn false;\n}\n\n/** Iterates over the dependency and resolves each one using the nearest package.json file.\n * Strips node APIs and pins the version of each dependency based on what is in the package.json.\n *\n * @param deps\n * @param filePath\n * @returns\n */\nfunction resolveRemoteDeps(\n\tdeps: RemoteDependency[],\n\tfilePath: AbsolutePath\n): { dependencies: RemoteDependency[]; devDependencies: RemoteDependency[] } {\n\tif (deps.length === 0) return { dependencies: [], devDependencies: [] };\n\n\tconst dependencies: RemoteDependency[] = [];\n\tconst devDependencies: RemoteDependency[] = [];\n\n\tconst packageResult = findNearestPackageJson(dirname(filePath));\n\n\tif (packageResult) {\n\t\tconst { devDependencies: packageDevDependencies, dependencies: packageDependencies } =\n\t\t\tpackageResult.package;\n\n\t\tfor (const dep of deps) {\n\t\t\tconst resolved = dependencies.filter((d) => d.name === dep.name);\n\t\t\tif (resolved.length > 0) {\n\t\t\t\tif (dep.version === undefined) continue;\n\n\t\t\t\t// we have already resolved the manually specified version\n\t\t\t\tif (resolved.find((d) => d.version === dep.version)) continue;\n\t\t\t}\n\n\t\t\tlet version: string | undefined;\n\t\t\tif (packageDependencies !== undefined) {\n\t\t\t\tversion = packageDependencies[dep.name];\n\t\t\t\tif (version !== undefined) {\n\t\t\t\t\tdependencies.push({ ...dep, version });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (packageDevDependencies !== undefined) {\n\t\t\t\tversion = packageDevDependencies[dep.name];\n\t\t\t\tif (version !== undefined) {\n\t\t\t\t\tdevDependencies.push({ ...dep, version });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdependencies.push({ ...dep });\n\t\t}\n\t}\n\n\treturn { dependencies, devDependencies };\n}\n\nexport async function transformImports(\n\tcode: string,\n\t_imports_: UnresolvedImport[],\n\topts: TransformImportsOptions\n): Promise<string> {\n\tconst fileImports = await getImports(code, {\n\t\tfileName: joinAbsolute(opts.cwd, opts.targetPath),\n\t\twarn: noop,\n\t});\n\n\tconst destDir = path.join(\n\t\topts.getItemPath({ item: opts.item, file: opts.file }).path,\n\t\topts.targetPath\n\t);\n\n\tconst transformedImports: ImportTransform[] = [];\n\n\tfor (const imp of _imports_) {\n\t\tconst importIndex = fileImports.indexOf(imp.import);\n\t\tif (importIndex !== -1) {\n\t\t\tfileImports.splice(importIndex, 1);\n\t\t}\n\t\tconst itemPath = opts.getItemPath({ item: imp.item, file: imp.file });\n\t\tif (!itemPath) continue;\n\n\t\tconst { dir: filePathRelativeToItemDir, name: filePathRelativeToItemName } = path.parse(\n\t\t\timp.file.path\n\t\t);\n\t\tconst importExt = path.parse(imp.import).ext;\n\n\t\t// this handles the case where the import is referencing an index file but by the directory name instead of the index file itself\n\t\t// for example: './utils/math' instead of './utils/math/index.ts'\n\t\tlet baseName =\n\t\t\tfilePathRelativeToItemName === 'index' && path.parse(imp.import).name !== 'index'\n\t\t\t\t? ''\n\t\t\t\t: filePathRelativeToItemName;\n\n\t\t// Preserve the original import's extension if the file name doesn't already include it\n\t\tif (baseName && importExt && !baseName.endsWith(importExt)) {\n\t\t\tbaseName += importExt;\n\t\t}\n\n\t\t// if relative make it relative\n\t\tif (itemPath.alias === undefined) {\n\t\t\tconst relative = path.relative(\n\t\t\t\tpath.join(opts.cwd, path.dirname(destDir)),\n\t\t\t\tpath.join(opts.cwd, itemPath.path, filePathRelativeToItemDir, baseName)\n\t\t\t);\n\n\t\t\ttransformedImports.push({\n\t\t\t\tpattern: createImportPattern(imp.import),\n\t\t\t\treplacement: createReplacement(\n\t\t\t\t\trelative.startsWith('.') ? relative : `./${relative}`\n\t\t\t\t),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\ttransformedImports.push({\n\t\t\tpattern: createImportPattern(imp.import),\n\t\t\treplacement: createReplacement(\n\t\t\t\tpath.join(itemPath.alias, filePathRelativeToItemDir, baseName)\n\t\t\t),\n\t\t});\n\t}\n\n\tconst componentsPaths = Result.fromThrowable(\n\t\t() => opts.getItemPath({ item: '', file: { type: 'component' } }),\n\t\t() => null\n\t)().unwrapOr(null);\n\tconst utilsPaths = Result.fromThrowable(\n\t\t() => opts.getItemPath({ item: '', file: { type: 'util' } }),\n\t\t() => null\n\t)().unwrapOr(null);\n\tconst uiPaths = Result.fromThrowable(\n\t\t() => opts.getItemPath({ item: '', file: { type: 'ui' } }),\n\t\t() => null\n\t)().unwrapOr(null);\n\tconst libPaths = Result.fromThrowable(\n\t\t() => opts.getItemPath({ item: '', file: { type: 'lib' } }),\n\t\t() => null\n\t)().unwrapOr(null);\n\tconst hooksPaths = Result.fromThrowable(\n\t\t() => opts.getItemPath({ item: '', file: { type: 'hook' } }),\n\t\t() => null\n\t)().unwrapOr(null);\n\n\t// any unresolved imports we can attempt to resolve with the shadcn compat transform\n\tcode = await transformShadcnImports({\n\t\tcode,\n\t\timports: fileImports,\n\t\tconfig: {\n\t\t\taliases: {\n\t\t\t\tcomponents: componentsPaths?.alias ?? componentsPaths?.path,\n\t\t\t\tutils: utilsPaths?.alias ?? utilsPaths?.path,\n\t\t\t\tui: uiPaths?.alias ?? uiPaths?.path,\n\t\t\t\tlib: libPaths?.alias ?? libPaths?.path,\n\t\t\t\thooks: hooksPaths?.alias ?? hooksPaths?.path,\n\t\t\t},\n\t\t},\n\t\tfileName: joinAbsolute(opts.cwd, opts.targetPath),\n\t});\n\n\tfor (const transformation of transformedImports) {\n\t\tcode = code.replace(transformation.pattern, transformation.replacement);\n\t}\n\n\treturn code;\n}\n\nexport function createImportPattern(literal: string): RegExp {\n\t// eventually we can use RegExp.escape I assume as soon as polyfills are available\n\treturn new RegExp(`(['\"])${escapeStringRegexp(literal)}\\\\1`, 'g');\n}\n\nexport function createReplacement(replacement: string): string {\n\treturn `$1${replacement}$1`;\n}\n\nexport async function installDependencies(\n\tdependencies: { dependencies: RemoteDependency[]; devDependencies: RemoteDependency[] },\n\t{ cwd }: InstallDependenciesOptions\n): Promise<void> {\n\tconst packageResult = findNearestPackageJson(cwd);\n\tif (!packageResult) return;\n\tconst pm = await detectPackageManager(cwd);\n\n\t// this is only if no dependencies were provided\n\tif (dependencies.dependencies.length === 0 && dependencies.devDependencies.length === 0) {\n\t\tconst installCmd = resolveCommand(pm, 'install', []);\n\n\t\tif (installCmd === null) return;\n\n\t\tawait runCommands({\n\t\t\ttitle: `Installing dependencies with ${pm}...`,\n\t\t\tcommands: [installCmd],\n\t\t\tcwd,\n\t\t\tmessages: {\n\t\t\t\tsuccess: () => `Installed dependencies`,\n\t\t\t\terror: (err) =>\n\t\t\t\t\t`Failed to install dependencies: ${err instanceof Error ? err.message : err}`,\n\t\t\t},\n\t\t});\n\t\treturn;\n\t}\n\n\tconst { dependencies: deps, devDependencies: devDeps } = shouldInstall(dependencies, {\n\t\tpkg: packageResult.package,\n\t});\n\n\tif (deps.length === 0 && devDeps.length === 0) return;\n\n\tconst add = resolveCommand(pm, 'add', [\n\t\t...deps.map((d) => `${d.name}${d.version ? `@${d.version}` : ''}`),\n\t]);\n\tconst addDev = resolveCommand(pm, 'add', [\n\t\t'-D',\n\t\t...devDeps.map((d) => `${d.name}${d.version ? `@${d.version}` : ''}`),\n\t]);\n\n\tawait runCommands({\n\t\ttitle: `Installing dependencies with ${pm}...`,\n\t\tcommands: [\n\t\t\t...(add && deps.length > 0 ? [add] : []),\n\t\t\t...(addDev && devDeps.length > 0 ? [addDev] : []),\n\t\t],\n\t\tcwd,\n\t\tmessages: {\n\t\t\tsuccess: () =>\n\t\t\t\t`Installed ${pc.cyan(\n\t\t\t\t\t[...deps, ...devDeps]\n\t\t\t\t\t\t.map((d) => `${d.name}${d.version ? `@${d.version}` : ''}`)\n\t\t\t\t\t\t.join(', ')\n\t\t\t\t)}`,\n\t\t\terror: (err) =>\n\t\t\t\t`Failed to install dependencies: ${err instanceof Error ? err.message : err}`,\n\t\t},\n\t});\n}\n"
  },
  {
    "path": "packages/jsrepo/src/langs/svelte.ts",
    "content": "import { getImports, installDependencies, resolveImports, transformImports } from '@/langs/js';\nimport type { Language } from '@/langs/types';\nimport { MissingPeerDependencyError } from '@/utils/errors';\nimport type { AbsolutePath } from '@/utils/types';\n\n// biome-ignore lint/complexity/noBannedTypes: leave me alone for a minute\nexport type SvelteOptions = {};\n\nlet svelteCompiler: typeof import('svelte/compiler') | null = null;\n\nasync function loadSvelteCompiler() {\n\tif (svelteCompiler) {\n\t\treturn svelteCompiler;\n\t}\n\n\ttry {\n\t\tsvelteCompiler = await import('svelte/compiler');\n\t\treturn svelteCompiler;\n\t} catch {\n\t\tthrow new MissingPeerDependencyError('svelte', 'Svelte language support');\n\t}\n}\n\n/**\n * Svelte language support.\n *\n * @remarks\n * Requires `svelte` to be installed in your project.\n */\nexport function svelte(_options: SvelteOptions = {}): Language {\n\treturn {\n\t\tname: 'svelte',\n\t\tcanResolveDependencies: (fileName) => fileName.endsWith('.svelte'),\n\t\tresolveDependencies: async (code, opts) => {\n\t\t\tconst sv = await loadSvelteCompiler();\n\t\t\tconst neededScripts: string[] = [];\n\t\t\tawait sv.preprocess(code, {\n\t\t\t\tscript: async ({ content }) => {\n\t\t\t\t\tneededScripts.push(content);\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst imports = await Promise.all(\n\t\t\t\tneededScripts.map(async (script) => {\n\t\t\t\t\treturn await resolveImports(\n\t\t\t\t\t\tawait getImports(script, {\n\t\t\t\t\t\t\t...opts,\n\t\t\t\t\t\t\t// weird and hacky ik but this is an easy way to get oxc to parse the code as ts\n\t\t\t\t\t\t\tfileName: `${opts.fileName}.ts` as AbsolutePath,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\topts\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tlocalDependencies: imports.flatMap((imp) => imp.localDependencies),\n\t\t\t\tdependencies: imports.flatMap((imp) => imp.dependencies),\n\t\t\t\tdevDependencies: imports.flatMap((imp) => imp.devDependencies),\n\t\t\t};\n\t\t},\n\t\ttransformImports: (code, imports, opts) => transformImports(code, imports, opts),\n\t\tcanInstallDependencies: (ecosystem) => ecosystem === 'js',\n\t\tinstallDependencies: (deps, opts) => installDependencies(deps, opts),\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/langs/types.ts",
    "content": "import type { Ecosystem, LocalDependency, RemoteDependency, UnresolvedImport } from '@/utils/build';\nimport type { RegistryItemType } from '@/utils/config';\nimport type { AbsolutePath, ItemRelativePath } from '@/utils/types';\nimport type { WarningHandler } from '@/utils/warnings';\n\nexport type ResolveDependenciesOptions = {\n\tfileName: AbsolutePath;\n\tcwd: AbsolutePath;\n\texcludeDeps: string[];\n\twarn: WarningHandler;\n};\n\nexport type InstallDependenciesOptions = {\n\tcwd: AbsolutePath;\n};\n\nexport type TransformImportsOptions = {\n\tcwd: AbsolutePath;\n\t/** The path of the file that the imports will be transformed for. */\n\ttargetPath: string;\n\titem: string;\n\tfile: { type: RegistryItemType; path: ItemRelativePath };\n\tgetItemPath(opts: { item: string; file: { type: RegistryItemType } }): {\n\t\t/** The resolved path of the dependency. */\n\t\tpath: string;\n\t\t/** The alias of the dependency. */\n\t\talias?: string;\n\t};\n};\n\nexport type ImportTransform = {\n\t/** The pattern to match the import. */\n\tpattern: string | RegExp;\n\t/** The replacement for the import. */\n\treplacement: string;\n};\n\nexport type ResolveDependenciesResult = {\n\tlocalDependencies: LocalDependency[];\n\tdependencies: RemoteDependency[];\n\tdevDependencies: RemoteDependency[];\n};\n\nexport interface Language {\n\t/** The name of the language. */\n\tname: string;\n\t/** Determines whether or not the language can resolve dependencies for the given file. */\n\tcanResolveDependencies(fileName: string): boolean;\n\tresolveDependencies(\n\t\tcode: string,\n\t\topts: ResolveDependenciesOptions\n\t): Promise<ResolveDependenciesResult> | ResolveDependenciesResult;\n\t/** Returns an object where the key is the import to be transformed and the value is the transformed import. */\n\ttransformImports(\n\t\tcode: string,\n\t\t_imports_: UnresolvedImport[],\n\t\topts: TransformImportsOptions\n\t): Promise<string>;\n\n\t/** Determines whether or not the language can install dependencies for the given ecosystem. */\n\tcanInstallDependencies(ecosystem: Ecosystem): boolean;\n\t/** Gets the install command to add the given dependencies to the project. */\n\tinstallDependencies(\n\t\tdependencies: { dependencies: RemoteDependency[]; devDependencies: RemoteDependency[] },\n\t\topts: InstallDependenciesOptions\n\t): Promise<void> | void;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/langs/vue.ts",
    "content": "import { getImports, installDependencies, resolveImports, transformImports } from '@/langs/js';\nimport type { Language } from '@/langs/types';\nimport { MissingPeerDependencyError } from '@/utils/errors';\nimport type { AbsolutePath } from '@/utils/types';\n\n// biome-ignore lint/complexity/noBannedTypes: leave me alone for a minute\nexport type VueOptions = {};\n\nlet vueCompiler: typeof import('vue/compiler-sfc') | null = null;\n\nasync function loadVueCompiler() {\n\tif (vueCompiler) {\n\t\treturn vueCompiler;\n\t}\n\n\ttry {\n\t\tvueCompiler = await import('vue/compiler-sfc');\n\t\treturn vueCompiler;\n\t} catch {\n\t\tthrow new MissingPeerDependencyError('vue', 'Vue language support');\n\t}\n}\n\n/**\n * Vue language support.\n *\n * @remarks\n * Requires `vue` to be installed in your project.\n */\nexport function vue(_options: VueOptions = {}): Language {\n\treturn {\n\t\tname: 'vue',\n\t\tcanResolveDependencies: (fileName) => fileName.endsWith('.vue'),\n\t\tresolveDependencies: async (code, opts) => {\n\t\t\tconst v = await loadVueCompiler();\n\t\t\tconst neededScripts: string[] = [];\n\t\t\tconst parsed = v.parse(code, {\n\t\t\t\tfilename: opts.fileName,\n\t\t\t});\n\t\t\tif (parsed.descriptor.script) {\n\t\t\t\tneededScripts.push(parsed.descriptor.script.content);\n\t\t\t}\n\t\t\tif (parsed.descriptor.scriptSetup) {\n\t\t\t\tneededScripts.push(parsed.descriptor.scriptSetup.content);\n\t\t\t}\n\t\t\tconst imports = await Promise.all(\n\t\t\t\tneededScripts.map(async (script) => {\n\t\t\t\t\treturn await resolveImports(\n\t\t\t\t\t\tawait getImports(script, {\n\t\t\t\t\t\t\t...opts,\n\t\t\t\t\t\t\t// weird and hacky ik but this is an easy way to get oxc to parse the code as ts\n\t\t\t\t\t\t\tfileName: `${opts.fileName}.ts` as AbsolutePath,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\topts\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tlocalDependencies: imports.flatMap((imp) => imp.localDependencies),\n\t\t\t\tdependencies: imports.flatMap((imp) => imp.dependencies),\n\t\t\t\tdevDependencies: imports.flatMap((imp) => imp.devDependencies),\n\t\t\t};\n\t\t},\n\t\ttransformImports,\n\t\tcanInstallDependencies: (ecosystem) => ecosystem === 'js',\n\t\tinstallDependencies,\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/outputs/distributed.ts",
    "content": "import { z } from 'zod';\nimport { type Output, RegistryPluginsSchema, RemoteDependencySchema } from '@/outputs/types';\nimport { MANIFEST_FILE, UnresolvedImportSchema } from '@/utils/build';\nimport { RegistryItemAddSchema, RegistryMetaSchema } from '@/utils/config';\nimport { existsSync, readFileSync, rmSync, writeFileSync } from '@/utils/fs';\nimport { stringify } from '@/utils/json';\nimport { joinAbsolute } from '@/utils/path';\nimport type { AbsolutePath, ItemRelativePath } from '@/utils/types';\nimport { safeParseFromJSON } from '@/utils/zod';\n\nexport type DistributedOutputOptions = {\n\t/** The directory to output the files to */\n\tdir: string;\n\t/** Whether or not to format the output. @default false */\n\tformat?: boolean;\n};\n\n/**\n * Use this output type when you are going to serve your registry as a static asset.\n *\n * ```ts\n * import { distributed } from \"jsrepo/outputs\";\n *\n * export default defineConfig({\n *   // ...\n *   outputs: [distributed({ dir: \"./public/r\" })]\n * });\n * ```\n *\n * This will create a file structure like:\n * ```plaintext\n * 📁 public/r\n * ├── registry.json\n * ├── button.json\n * └── math.json\n * ```\n * @param options\n * @returns\n */\nexport function distributed({ dir, format }: DistributedOutputOptions): Output {\n\treturn {\n\t\toutput: async (buildResult, { cwd }) => {\n\t\t\tconst files: { path: AbsolutePath; content: string }[] = [];\n\t\t\tconst manifest: DistributedOutputManifest = {\n\t\t\t\tname: buildResult.name,\n\t\t\t\tauthors: buildResult.authors,\n\t\t\t\tbugs: buildResult.bugs,\n\t\t\t\tdescription: buildResult.description,\n\t\t\t\thomepage: buildResult.homepage,\n\t\t\t\trepository: buildResult.repository,\n\t\t\t\ttags: buildResult.tags,\n\t\t\t\tversion: buildResult.version,\n\t\t\t\tmeta: buildResult.meta,\n\t\t\t\ttype: 'distributed',\n\t\t\t\tplugins: buildResult.plugins,\n\t\t\t\tdefaultPaths: buildResult.defaultPaths,\n\t\t\t\titems: buildResult.items.map((item) => ({\n\t\t\t\t\tname: item.name,\n\t\t\t\t\ttitle: item.title,\n\t\t\t\t\tdescription: item.description,\n\t\t\t\t\ttype: item.type,\n\t\t\t\t\tadd: item.add,\n\t\t\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\t\t\tdependencies: item.dependencies,\n\t\t\t\t\tdevDependencies: item.devDependencies,\n\t\t\t\t\tenvVars: item.envVars,\n\t\t\t\t\tfiles: item.files.map(\n\t\t\t\t\t\t(file) =>\n\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\ttype: file.type,\n\t\t\t\t\t\t\t\trole: file.role,\n\t\t\t\t\t\t\t\tpath: file.path,\n\t\t\t\t\t\t\t\ttarget: file.target,\n\t\t\t\t\t\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\t\t\t\t\t\tdependencies: file.dependencies,\n\t\t\t\t\t\t\t\tdevDependencies: file.devDependencies,\n\t\t\t\t\t\t\t}) satisfies DistributedOutputManifestFile\n\t\t\t\t\t),\n\t\t\t\t\tcategories: item.categories,\n\t\t\t\t\tmeta: item.meta,\n\t\t\t\t})),\n\t\t\t};\n\t\t\tfiles.push({\n\t\t\t\tpath: joinAbsolute(cwd, dir, MANIFEST_FILE),\n\t\t\t\tcontent: stringify(manifest, { format }),\n\t\t\t});\n\n\t\t\tfor (const item of buildResult.items) {\n\t\t\t\tconst outputItem: DistributedOutputItem = {\n\t\t\t\t\tname: item.name,\n\t\t\t\t\ttitle: item.title,\n\t\t\t\t\tdescription: item.description,\n\t\t\t\t\ttype: item.type,\n\t\t\t\t\tadd: item.add,\n\t\t\t\t\tfiles: item.files.map(\n\t\t\t\t\t\t(file) =>\n\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\ttype: file.type,\n\t\t\t\t\t\t\t\trole: file.role,\n\t\t\t\t\t\t\t\tcontent: file.content,\n\t\t\t\t\t\t\t\tpath: file.path,\n\t\t\t\t\t\t\t\t_imports_: file._imports_,\n\t\t\t\t\t\t\t\ttarget: file.target,\n\t\t\t\t\t\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\t\t\t\t\t\tdependencies: file.dependencies,\n\t\t\t\t\t\t\t\tdevDependencies: file.devDependencies,\n\t\t\t\t\t\t\t}) satisfies DistributedOutputFile\n\t\t\t\t\t),\n\t\t\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\t\t\tdependencies: item.dependencies,\n\t\t\t\t\tdevDependencies: item.devDependencies,\n\t\t\t\t\tenvVars: item.envVars,\n\t\t\t\t\tcategories: item.categories,\n\t\t\t\t\tmeta: item.meta,\n\t\t\t\t};\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: joinAbsolute(cwd, dir, `${item.name}.json`),\n\t\t\t\t\tcontent: stringify(outputItem, { format }),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const file of files) {\n\t\t\t\twriteFileSync(file.path, file.content);\n\t\t\t}\n\t\t},\n\t\tclean: async ({ cwd }) => {\n\t\t\tconst manifestPath = joinAbsolute(cwd, dir, MANIFEST_FILE);\n\t\t\tif (!existsSync(manifestPath)) return;\n\t\t\tconst contentsResult = readFileSync(manifestPath as AbsolutePath);\n\t\t\tif (contentsResult.isErr()) return;\n\t\t\tconst contents = contentsResult.value;\n\t\t\tconst manifestResult = safeParseFromJSON(DistributedOutputManifestSchema, contents);\n\t\t\tif (manifestResult.isErr()) return;\n\t\t\tconst manifest = manifestResult.value;\n\t\t\tfor (const item of manifest.items) {\n\t\t\t\tconst itemPath = joinAbsolute(cwd, dir, `${item.name}.json`);\n\t\t\t\trmSync(itemPath);\n\t\t\t}\n\t\t\trmSync(manifestPath);\n\t\t},\n\t};\n}\n\nexport const DistributedOutputManifestFileSchema = z.object({\n\tpath: z.string().transform((v) => v as ItemRelativePath),\n\ttype: z.string(),\n\trole: z.string().optional(),\n\ttarget: z.union([z.string(), z.undefined()]),\n\tregistryDependencies: z.union([z.array(z.string()), z.undefined()]),\n\tdependencies: z.union([z.array(RemoteDependencySchema), z.undefined()]),\n\tdevDependencies: z.union([z.array(RemoteDependencySchema), z.undefined()]),\n});\n\nexport type DistributedOutputManifestFile = z.infer<typeof DistributedOutputManifestFileSchema>;\n\nexport const DistributedOutputManifestItemSchema = z.object({\n\tname: z.string(),\n\ttitle: z.union([z.string(), z.undefined()]),\n\tdescription: z.union([z.string(), z.undefined()]),\n\ttype: z.string(),\n\tregistryDependencies: z.union([z.array(z.string()), z.undefined()]),\n\tadd: z.union([RegistryItemAddSchema, z.undefined()]),\n\tdependencies: z.union([z.array(z.union([RemoteDependencySchema, z.string()])), z.undefined()]),\n\tdevDependencies: z.union([\n\t\tz.array(z.union([RemoteDependencySchema, z.string()])),\n\t\tz.undefined(),\n\t]),\n\tenvVars: z.union([z.record(z.string(), z.string()), z.undefined()]),\n\tfiles: z.union([z.array(DistributedOutputManifestFileSchema), z.undefined()]).default([]),\n\tcategories: z.union([z.array(z.string()), z.undefined()]),\n\tmeta: z.union([z.record(z.string(), z.string()), z.undefined()]),\n});\n\nexport const DistributedOutputManifestSchema = RegistryMetaSchema.extend({\n\ttype: z.union([z.literal('distributed'), z.undefined()]).default('distributed'),\n\tplugins: z.union([RegistryPluginsSchema, z.undefined()]),\n\titems: z.array(DistributedOutputManifestItemSchema),\n\tdefaultPaths: z.union([z.record(z.string(), z.string()), z.undefined()]),\n});\n\nexport type DistributedOutputManifest = z.infer<typeof DistributedOutputManifestSchema>;\n\nexport const DistributedOutputFileSchema = z.object({\n\tpath: z.string().transform((v) => v as ItemRelativePath),\n\tcontent: z.string(),\n\ttype: z.string(),\n\trole: z.string().optional(),\n\t_imports_: z.union([z.array(UnresolvedImportSchema), z.undefined()]),\n\ttarget: z.union([z.string(), z.undefined()]),\n\tregistryDependencies: z.union([z.array(z.string()), z.undefined()]),\n\tdependencies: z.union([z.array(RemoteDependencySchema), z.undefined()]),\n\tdevDependencies: z.union([z.array(RemoteDependencySchema), z.undefined()]),\n});\n\nexport type DistributedOutputFile = z.infer<typeof DistributedOutputFileSchema>;\n\nexport const DistributedOutputItemSchema = z.object({\n\t$schema: z.string().optional(),\n\tname: z.string(),\n\ttitle: z.union([z.string(), z.undefined()]),\n\tdescription: z.union([z.string(), z.undefined()]),\n\ttype: z.string(),\n\tregistryDependencies: z.union([z.array(z.string()), z.undefined()]),\n\tadd: z.union([RegistryItemAddSchema, z.undefined()]),\n\tdependencies: z.union([z.array(z.union([RemoteDependencySchema, z.string()])), z.undefined()]),\n\tdevDependencies: z.union([\n\t\tz.array(z.union([RemoteDependencySchema, z.string()])),\n\t\tz.undefined(),\n\t]),\n\tenvVars: z.union([z.record(z.string(), z.string()), z.undefined()]),\n\tfiles: z.union([z.array(DistributedOutputFileSchema), z.undefined()]).default([]),\n\tcategories: z.union([z.array(z.string()), z.undefined()]),\n\tmeta: z.union([z.record(z.string(), z.string()), z.undefined()]),\n});\n\nexport type DistributedOutputItem = z.infer<typeof DistributedOutputItemSchema>;\n"
  },
  {
    "path": "packages/jsrepo/src/outputs/index.ts",
    "content": "import { z } from 'zod';\nimport { DistributedOutputManifestSchema } from '@/outputs/distributed';\nimport { RepositoryOutputManifestSchema } from '@/outputs/repository';\n\nexport {\n\ttype DistributedOutputManifest,\n\ttype DistributedOutputOptions,\n\tdistributed,\n} from '@/outputs/distributed';\n\nexport {\n\ttype RepositoryOutputManifest,\n\ttype RepositoryOutputOptions,\n\trepository,\n} from '@/outputs/repository';\n\nexport const ManifestSchema = z.discriminatedUnion('type', [\n\tDistributedOutputManifestSchema,\n\tRepositoryOutputManifestSchema,\n]);\n\nexport type Manifest = z.infer<typeof ManifestSchema>;\n\nexport type { Output } from '@/outputs/types';\n"
  },
  {
    "path": "packages/jsrepo/src/outputs/repository.ts",
    "content": "import { z } from 'zod';\nimport { type Output, RegistryPluginsSchema, RemoteDependencySchema } from '@/outputs/types';\nimport { MANIFEST_FILE, UnresolvedImportSchema } from '@/utils/build';\nimport { RegistryItemAddSchema, RegistryMetaSchema } from '@/utils/config';\nimport { rmSync, writeFileSync } from '@/utils/fs';\nimport { stringify } from '@/utils/json';\nimport { joinAbsolute, type RelativeToCwdPath, relativeToCwd } from '@/utils/path';\nimport type { ItemRelativePath } from '@/utils/types';\n\nexport type RepositoryOutputOptions = {\n\t/** Whether or not to format the output. @default false */\n\tformat?: boolean;\n};\n\n/**\n * Use this output type when you are serving your registry from a repository.\n *\n * ```ts\n * import { repository } from \"jsrepo/outputs\";\n *\n * export default defineConfig({\n *   // ...\n *   outputs: [repository()]\n * });\n * ```\n *\n * This will create a manifest file at the root of your repository like:\n * ```plaintext\n * 📁 .\n * └── registry.json\n * ```\n * @param options\n * @returns\n */\nexport function repository({ format }: RepositoryOutputOptions = {}): Output {\n\treturn {\n\t\toutput: async (buildResult, { cwd }) => {\n\t\t\tconst manifest: RepositoryOutputManifest = {\n\t\t\t\tname: buildResult.name,\n\t\t\t\tauthors: buildResult.authors,\n\t\t\t\tbugs: buildResult.bugs,\n\t\t\t\tdescription: buildResult.description,\n\t\t\t\thomepage: buildResult.homepage,\n\t\t\t\trepository: buildResult.repository,\n\t\t\t\ttags: buildResult.tags,\n\t\t\t\tversion: buildResult.version,\n\t\t\t\tmeta: buildResult.meta,\n\t\t\t\ttype: 'repository',\n\t\t\t\tplugins: buildResult.plugins,\n\t\t\t\tdefaultPaths: buildResult.defaultPaths,\n\t\t\t\titems: buildResult.items.map((item) => ({\n\t\t\t\t\tname: item.name,\n\t\t\t\t\ttitle: item.title,\n\t\t\t\t\tdescription: item.description,\n\t\t\t\t\ttype: item.type,\n\t\t\t\t\tadd: item.add,\n\t\t\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\t\t\tdependencies: item.dependencies,\n\t\t\t\t\tdevDependencies: item.devDependencies,\n\t\t\t\t\tfiles: item.files.map(\n\t\t\t\t\t\t(file) =>\n\t\t\t\t\t\t\t({\n\t\t\t\t\t\t\t\ttype: file.type,\n\t\t\t\t\t\t\t\trole: file.role,\n\t\t\t\t\t\t\t\tpath: file.path,\n\t\t\t\t\t\t\t\trelativePath: relativeToCwd(cwd, file.absolutePath),\n\t\t\t\t\t\t\t\t_imports_: file._imports_,\n\t\t\t\t\t\t\t\ttarget: file.target,\n\t\t\t\t\t\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\t\t\t\t\t\tdependencies: file.dependencies,\n\t\t\t\t\t\t\t\tdevDependencies: file.devDependencies,\n\t\t\t\t\t\t\t}) satisfies RepositoryOutputFile\n\t\t\t\t\t),\n\t\t\t\t\tenvVars: item.envVars,\n\t\t\t\t\tcategories: item.categories,\n\t\t\t\t\tmeta: item.meta,\n\t\t\t\t})),\n\t\t\t};\n\n\t\t\twriteFileSync(joinAbsolute(cwd, MANIFEST_FILE), stringify(manifest, { format }));\n\t\t},\n\t\tclean: async ({ cwd }) => {\n\t\t\trmSync(joinAbsolute(cwd, MANIFEST_FILE));\n\t\t},\n\t};\n}\n\nexport const RepositoryOutputFileSchema = z.object({\n\tpath: z.string().transform((v) => v as ItemRelativePath),\n\ttype: z.string(),\n\trole: z.string().optional(),\n\trelativePath: z.string().transform((v) => v as RelativeToCwdPath),\n\t_imports_: z.array(UnresolvedImportSchema),\n\ttarget: z.union([z.string(), z.undefined()]),\n\tregistryDependencies: z.union([z.array(z.string()), z.undefined()]),\n\tdependencies: z.union([z.array(RemoteDependencySchema), z.undefined()]),\n\tdevDependencies: z.union([z.array(RemoteDependencySchema), z.undefined()]),\n});\n\nexport type RepositoryOutputFile = z.infer<typeof RepositoryOutputFileSchema>;\n\nexport const RepositoryOutputManifestItemSchema = z.object({\n\tname: z.string(),\n\ttitle: z.union([z.string(), z.undefined()]),\n\tdescription: z.union([z.string(), z.undefined()]),\n\ttype: z.string(),\n\tregistryDependencies: z.union([z.array(z.string()), z.undefined()]),\n\tadd: z.union([RegistryItemAddSchema, z.undefined()]),\n\tfiles: z.union([z.array(RepositoryOutputFileSchema), z.undefined()]).default([]),\n\tdependencies: z.union([z.array(z.union([RemoteDependencySchema, z.string()])), z.undefined()]),\n\tdevDependencies: z.union([\n\t\tz.array(z.union([RemoteDependencySchema, z.string()])),\n\t\tz.undefined(),\n\t]),\n\tenvVars: z.union([z.record(z.string(), z.string()), z.undefined()]),\n\tcategories: z.union([z.array(z.string()), z.undefined()]),\n\tmeta: z.union([z.record(z.string(), z.string()), z.undefined()]),\n});\n\nexport type RepositoryOutputManifestItem = z.infer<typeof RepositoryOutputManifestItemSchema>;\n\nexport const RepositoryOutputManifestSchema = RegistryMetaSchema.extend({\n\ttype: z.literal('repository'),\n\tplugins: z.union([RegistryPluginsSchema, z.undefined()]),\n\titems: z.array(RepositoryOutputManifestItemSchema),\n\tdefaultPaths: z.union([z.record(z.string(), z.string()), z.undefined()]),\n});\n\nexport type RepositoryOutputManifest = z.infer<typeof RepositoryOutputManifestSchema>;\n"
  },
  {
    "path": "packages/jsrepo/src/outputs/types.ts",
    "content": "import { z } from 'zod';\nimport type { AbsolutePath } from '@/api';\nimport type { BuildResult } from '@/utils/build';\n\nexport interface Output {\n\toutput(buildResult: BuildResult, opts: { cwd: AbsolutePath }): Promise<void>;\n\tclean(opts: { cwd: AbsolutePath }): Promise<void>;\n}\n\nexport const RemoteDependencySchema = z.object({\n\tecosystem: z.string(),\n\tname: z.string(),\n\tversion: z.string().optional(),\n});\n\nexport const RegistryPluginSchema = z.object({\n\tpackage: z.string(),\n\tversion: z.string().optional(),\n\toptional: z.boolean().optional(),\n});\n\nexport const RegistryPluginsSchema = z.object({\n\tlanguages: z.array(RegistryPluginSchema).optional(),\n\tproviders: z.array(RegistryPluginSchema).optional(),\n\ttransforms: z.array(RegistryPluginSchema).optional(),\n});\n"
  },
  {
    "path": "packages/jsrepo/src/providers/azure.ts",
    "content": "import type { CreateOptions, FetchOptions, Provider, ProviderFactory } from '@/providers/types';\nimport { ProviderFetchError } from '@/utils/errors';\n\nconst BASE_URL = 'https://dev.azure.com';\nconst DEFAULT_BRANCH = 'main';\n\nexport type AzureOptions = {\n\t/** If you are self hosting Azure DevOps and you want azure/ to default to your base url instead of https://dev.azure.com */\n\tbaseUrl?: string;\n};\n\n/**\n * The built in Azure DevOps provider.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * 'azure/<org>/<project>/<repo>'\n * 'azure/<org>/<project>/<repo>/heads/<ref>'\n * 'azure/<org>/<project>/<repo>/tags/<ref>'\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { azure } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [azure()],\n * });\n * ```\n */\nexport function azure(options: AzureOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'azure',\n\t\tmatches: (url: string) =>\n\t\t\turl.startsWith(BASE_URL) || url.startsWith('azure/') || url.startsWith('azure:'),\n\t\tcreate: (url: string, createOpts: CreateOptions) => Azure.create(url, options, createOpts),\n\t\tauth: {\n\t\t\ttokenStoredFor: 'provider',\n\t\t\tenvVar: 'AZURE_TOKEN',\n\t\t},\n\t};\n}\n\ntype AzureState = {\n\tbaseUrl: string;\n\towner: string;\n\trepoName: string;\n\tproject: string;\n\trefs: 'heads' | 'tags';\n\tref: string;\n};\n\nclass Azure implements Provider {\n\tconstructor(\n\t\treadonly state: AzureState,\n\t\treadonly opts: AzureOptions\n\t) {}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: AzureOptions,\n\t\tcreateOpts: CreateOptions\n\t): Promise<Provider> {\n\t\tconst state = await Azure.getState(url, opts, createOpts);\n\t\treturn new Azure(state, opts);\n\t}\n\n\tasync fetch(resourcePath: string, { token, fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.resolveRaw(resourcePath);\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t...(Azure.authHeader(token) ?? {}),\n\t\t\t};\n\n\t\t\tconst response = await f(url.toString(), { headers });\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate static async getState(\n\t\turl: string,\n\t\topts: AzureOptions,\n\t\t_createOpts: CreateOptions\n\t): Promise<AzureState> {\n\t\treturn Azure.parseUrl(url, opts);\n\t}\n\n\tprivate static parseUrl(url: string, opts: AzureOptions): AzureState {\n\t\tlet baseUrl = opts.baseUrl ?? BASE_URL;\n\t\tif (url.startsWith('azure:')) {\n\t\t\tbaseUrl = new URL(url.slice(6)).origin;\n\t\t} else if (url.startsWith(BASE_URL)) {\n\t\t\tbaseUrl = BASE_URL;\n\t\t}\n\n\t\tconst repo = url.replaceAll(/(azure\\/)|(azure:https?:\\/\\/[^/]+\\/)/g, '');\n\n\t\tconst [owner, project, repoName, ...rest] = repo.split('/');\n\n\t\tlet ref: string = DEFAULT_BRANCH;\n\n\t\t// checks if the type of the ref is tags or heads\n\t\tlet refs: 'heads' | 'tags' = 'heads';\n\n\t\tif (rest[0] && ['tags', 'heads'].includes(rest[0])) {\n\t\t\trefs = rest[0] as 'heads' | 'tags';\n\n\t\t\tif (rest[1] && rest[1] !== '') {\n\t\t\t\tref = rest[1];\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tbaseUrl,\n\t\t\towner: owner!,\n\t\t\trepoName: repoName!,\n\t\t\tproject: project!,\n\t\t\tref,\n\t\t\trefs,\n\t\t};\n\t}\n\n\tprivate resolveRaw(resourcePath: string): URL {\n\t\tconst { owner, repoName, project, ref, refs, baseUrl } = this.state;\n\n\t\tconst versionType = refs === 'tags' ? 'tag' : 'branch';\n\n\t\treturn new URL(\n\t\t\t`${baseUrl}/${owner}/${project}/_apis/git/repositories/${repoName}/items?path=${resourcePath}&api-version=7.2-preview.1&versionDescriptor.version=${ref}&versionDescriptor.versionType=${versionType}`\n\t\t);\n\t}\n\n\tprivate static authHeader(token: string | undefined) {\n\t\tif (!token) return undefined;\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/bitbucket.ts",
    "content": "import type { CreateOptions, FetchOptions, Provider, ProviderFactory } from '@/providers/types';\nimport { ProviderFetchError } from '@/utils/errors';\n\nconst BASE_URL = 'https://bitbucket.org';\nconst DEFAULT_BRANCH = 'main';\n\nexport type BitBucketOptions = {\n\t/** If you are self hosting Bitbucket and you want bitbucket/ to default to your base url instead of https://bitbucket.org */\n\tbaseUrl?: string;\n};\n\n/**\n * The built in Bitbucket provider.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * 'https://bitbucket.org/<owner>/<repo>'\n * 'bitbucket/<owner>/<repo>'\n * 'bitbucket/<owner>/<repo>/src/<ref>'\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { bitbucket } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [bitbucket()],\n * });\n * ```\n */\nexport function bitbucket(options: BitBucketOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'bitbucket',\n\t\tmatches: (url: string) =>\n\t\t\turl.startsWith(BASE_URL) ||\n\t\t\turl.startsWith('bitbucket/') ||\n\t\t\turl.startsWith('bitbucket:'),\n\t\tcreate: (url: string, createOpts: CreateOptions) =>\n\t\t\tBitBucket.create(url, options, createOpts),\n\t\tauth: {\n\t\t\ttokenStoredFor: 'provider',\n\t\t\tenvVar: 'BITBUCKET_TOKEN',\n\t\t},\n\t};\n}\n\ntype BitBucketState = {\n\tbaseUrl: string;\n\towner: string;\n\trepoName: string;\n\tref?: string;\n};\n\nclass BitBucket implements Provider {\n\tconstructor(\n\t\treadonly state: BitBucketState,\n\t\treadonly opts: BitBucketOptions\n\t) {}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: BitBucketOptions,\n\t\tcreateOpts: CreateOptions\n\t): Promise<Provider> {\n\t\tconst state = await BitBucket.getState(url, opts, createOpts);\n\t\treturn new BitBucket(state, opts);\n\t}\n\n\tasync fetch(resourcePath: string, { token, fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.resolveRaw(resourcePath);\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t...(BitBucket.authHeader(token) ?? {}),\n\t\t\t};\n\n\t\t\tconst response = await f(url.toString(), { headers });\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate static async getState(\n\t\turl: string,\n\t\topts: BitBucketOptions,\n\t\t{ token, fetch: f = fetch }: CreateOptions\n\t): Promise<BitBucketState> {\n\t\tconst parsedResult = BitBucket.parseUrl(url, opts);\n\n\t\tlet { owner, repoName, ref, baseUrl } = parsedResult;\n\n\t\t// fetch default branch if ref was not provided\n\t\tif (ref === undefined) {\n\t\t\ttry {\n\t\t\t\tconst apiUrl = BitBucket.getApiUrl(parsedResult.baseUrl);\n\t\t\t\tconst response = await f(\n\t\t\t\t\tnew URL(`/repositories/${owner}/${repoName}`, apiUrl).toString(),\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: BitBucket.authHeader(token),\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst res = await response.json();\n\n\t\t\t\t\tref = res.mainbranch.name as string;\n\t\t\t\t} else {\n\t\t\t\t\tref = DEFAULT_BRANCH;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tref = DEFAULT_BRANCH;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\towner,\n\t\t\tref,\n\t\t\trepoName,\n\t\t\tbaseUrl,\n\t\t};\n\t}\n\n\tprivate static parseUrl(url: string, opts: BitBucketOptions): BitBucketState {\n\t\tlet baseUrl = opts.baseUrl ?? BASE_URL;\n\t\tif (url.startsWith('bitbucket:')) {\n\t\t\tbaseUrl = new URL(url.slice(10)).origin;\n\t\t} else if (url.startsWith(BASE_URL)) {\n\t\t\tbaseUrl = BASE_URL;\n\t\t}\n\n\t\tconst repo = url.replaceAll(\n\t\t\t/(https:\\/\\/bitbucket.org\\/)|(bitbucket\\/)|(bitbucket:https?:\\/\\/[^/]+\\/)/g,\n\t\t\t''\n\t\t);\n\n\t\tconst [owner, repoName, ...rest] = repo.split('/');\n\n\t\tlet ref: string | undefined;\n\n\t\tif (rest[0] === 'src') {\n\t\t\tref = rest[1];\n\t\t}\n\n\t\treturn {\n\t\t\tbaseUrl,\n\t\t\towner: owner!,\n\t\t\trepoName: repoName!,\n\t\t\tref: ref,\n\t\t};\n\t}\n\n\tprivate static getApiUrl(baseUrl: string): string {\n\t\tconst parsedBaseUrl = new URL(baseUrl ?? BASE_URL);\n\t\treturn `${parsedBaseUrl.protocol}//api.${parsedBaseUrl.host}/2.0`;\n\t}\n\n\tprivate resolveRaw(resourcePath: string): URL {\n\t\tconst { owner, repoName, ref, baseUrl } = this.state;\n\n\t\treturn new URL(\n\t\t\t`repositories/${owner}/${repoName}/src/${ref}/${resourcePath}`,\n\t\t\t`${BitBucket.getApiUrl(baseUrl)}/`\n\t\t);\n\t}\n\n\tprivate static authHeader(token: string | undefined) {\n\t\tif (!token) return undefined;\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/fs.ts",
    "content": "import path from 'pathe';\nimport type { CreateOptions, Provider, ProviderFactory } from '@/providers/types';\nimport { ProviderFetchError } from '@/utils/errors';\nimport { readFileSync } from '@/utils/fs';\nimport { joinAbsolute } from '@/utils/path';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport type FsOptions = {\n\t/** In case you want all your registry paths to be relative to a base directory. */\n\tbaseDir?: string;\n};\n\n/**\n * The built in File System provider. Allows you to run registries locally using the file system.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * 'fs://<path>'\n * 'fs://../relative/path' // relative paths\n * 'fs://users/john' // absolute path\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { fs } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [fs()],\n * });\n * ```\n */\nfunction _fs(options: FsOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'Fs',\n\t\tmatches: (url: string) => url.startsWith('fs://'),\n\t\tcreate: (url: string, createOpts: CreateOptions) => Fs.create(url, options, createOpts),\n\t};\n}\n\nexport { _fs as fs };\n\ntype FsState = {\n\tpath: AbsolutePath;\n};\n\nclass Fs implements Provider {\n\tconstructor(\n\t\treadonly state: FsState,\n\t\treadonly opts: FsOptions\n\t) {}\n\n\tasync fetch(resourcePath: string): Promise<string> {\n\t\tconst filePath = joinAbsolute(this.state.path, resourcePath);\n\n\t\ttry {\n\t\t\treturn readFileSync(filePath)._unsafeUnwrap();\n\t\t} catch (error) {\n\t\t\tconst resourcePathStr = path.isAbsolute(filePath)\n\t\t\t\t? filePath\n\t\t\t\t: path.join(process.cwd(), filePath);\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, resourcePathStr);\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\tresourcePathStr\n\t\t\t);\n\t\t}\n\t}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: FsOptions,\n\t\tcreateOpts: CreateOptions\n\t): Promise<Provider> {\n\t\tconst actualUrl = url.slice(5);\n\t\tlet p: AbsolutePath;\n\t\tif (opts.baseDir) {\n\t\t\tif (opts.baseDir.startsWith('.')) {\n\t\t\t\tp = joinAbsolute(createOpts.cwd, opts.baseDir, actualUrl);\n\t\t\t} else {\n\t\t\t\tp = joinAbsolute(opts.baseDir as AbsolutePath, actualUrl);\n\t\t\t}\n\t\t} else {\n\t\t\tif (actualUrl.startsWith('.')) {\n\t\t\t\tp = joinAbsolute(createOpts.cwd, actualUrl);\n\t\t\t} else {\n\t\t\t\tp = actualUrl as AbsolutePath;\n\t\t\t}\n\t\t}\n\n\t\treturn new Fs({ path: p }, opts);\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/github.ts",
    "content": "import type { CreateOptions, FetchOptions, Provider, ProviderFactory } from '@/providers/types';\nimport { ProviderFetchError } from '@/utils/errors';\n\nconst BASE_URL = 'https://github.com';\nconst DEFAULT_BRANCH = 'main';\n\nexport type GitHubOptions = {\n\t/** If you are self hosting GitHub and you want github/ to default to your base url instead of https://github.com */\n\tbaseUrl?: string;\n};\n\n/**\n * The built in GitHub provider.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * 'https://github.com/<owner>/<repo>'\n * 'github/<owner>/<repo>'\n * 'github/<owner>/<repo>/tree/<ref>'\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { github } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [github()],\n * });\n * ```\n */\nexport function github(options: GitHubOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'github',\n\t\tmatches: (url: string) =>\n\t\t\turl.startsWith(options.baseUrl ?? BASE_URL) ||\n\t\t\turl.startsWith('github/') ||\n\t\t\turl.startsWith('github:'),\n\t\tcreate: (url: string, createOpts: CreateOptions) => GitHub.create(url, options, createOpts),\n\t\tauth: {\n\t\t\ttokenStoredFor: 'provider',\n\t\t\tenvVar: 'GITHUB_TOKEN',\n\t\t},\n\t};\n}\n\ntype GitHubState = {\n\tbaseUrl: string;\n\turl: string;\n\towner: string;\n\trepoName: string;\n\tref: string;\n};\n\nclass GitHub implements Provider {\n\tconstructor(\n\t\treadonly state: GitHubState,\n\t\treadonly opts: GitHubOptions\n\t) {}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: GitHubOptions,\n\t\tcreateOpts: CreateOptions\n\t): Promise<Provider> {\n\t\tconst state = await GitHub.getState(url, opts, createOpts);\n\t\treturn new GitHub(state, opts);\n\t}\n\n\tasync fetch(resourcePath: string, { token, fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.resolveRaw(resourcePath);\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t...(GitHub.authHeader(token) ?? {}),\n\t\t\t\tAccept: 'application/vnd.github.raw+json',\n\t\t\t};\n\n\t\t\tconst response = await f(url.toString(), { headers });\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate static async getState(\n\t\turl: string,\n\t\topts: GitHubOptions,\n\t\t{ token, fetch: f = fetch }: CreateOptions\n\t): Promise<GitHubState> {\n\t\tconst parsedResult = GitHub.parseUrl(url, opts);\n\n\t\tlet { owner, repoName, ref } = parsedResult;\n\n\t\t// fetch default branch if ref was not provided\n\t\tif (ref === undefined) {\n\t\t\ttry {\n\t\t\t\tconst apiUrl = GitHub.getApiUrl(opts.baseUrl ?? BASE_URL);\n\t\t\t\tconst response = await f(\n\t\t\t\t\tnew URL(`/repos/${owner}/${repoName}`, apiUrl).toString(),\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: GitHub.authHeader(token),\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst res = await response.json();\n\t\t\t\t\tref = res.default_branch as string;\n\t\t\t\t} else {\n\t\t\t\t\tref = DEFAULT_BRANCH;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tref = DEFAULT_BRANCH;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\towner,\n\t\t\tref,\n\t\t\trepoName,\n\t\t\turl,\n\t\t\tbaseUrl: opts.baseUrl ?? BASE_URL,\n\t\t};\n\t}\n\n\tprivate static parseUrl(\n\t\turl: string,\n\t\topts: GitHubOptions\n\t): {\n\t\tbaseUrl: string;\n\t\towner: string;\n\t\trepoName: string;\n\t\tref?: string;\n\t} {\n\t\tlet baseUrl = opts.baseUrl ?? BASE_URL;\n\t\tif (url.startsWith('github:')) {\n\t\t\tbaseUrl = new URL(url.slice(7)).origin;\n\t\t} else if (url.startsWith(BASE_URL)) {\n\t\t\tbaseUrl = BASE_URL;\n\t\t}\n\n\t\tconst repo = url.replaceAll(\n\t\t\t/github\\/|https:\\/\\/github\\.com\\/|github:https?:\\/\\/[^/]+\\//g,\n\t\t\t''\n\t\t);\n\n\t\tconst [owner, repoName, ...rest] = repo.split('/');\n\n\t\tlet ref: string | undefined;\n\n\t\tif (rest.length > 0) {\n\t\t\tif (rest[0] === 'tree' && rest[1]) {\n\t\t\t\tref = rest[1];\n\t\t\t}\n\t\t}\n\n\t\tif (!owner || !repoName) {\n\t\t\tthrow new Error(`Failed to parse invalid URL: ${url}`);\n\t\t}\n\n\t\treturn {\n\t\t\tbaseUrl,\n\t\t\towner: owner,\n\t\t\trepoName: repoName,\n\t\t\tref,\n\t\t};\n\t}\n\n\tprivate resolveRaw(resourcePath: string): URL {\n\t\tconst { owner, repoName, ref, baseUrl } = this.state;\n\n\t\treturn new URL(\n\t\t\t`${resourcePath}?ref=${ref}`,\n\t\t\t`${GitHub.getApiUrl(baseUrl)}/repos/${owner}/${repoName}/contents/`\n\t\t);\n\t}\n\n\tprivate static getApiUrl(baseUrl: string): string {\n\t\tconst parsedBaseUrl = new URL(baseUrl ?? BASE_URL);\n\t\treturn `${parsedBaseUrl.protocol}//api.${parsedBaseUrl.host}`;\n\t}\n\n\tprivate static authHeader(token: string | undefined) {\n\t\tif (!token) return undefined;\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/gitlab.ts",
    "content": "import type { CreateOptions, FetchOptions, Provider, ProviderFactory } from '@/providers/types';\nimport { ProviderFetchError } from '@/utils/errors';\n\nconst BASE_URL = 'https://gitlab.com';\nconst DEFAULT_BRANCH = 'main';\n\nexport type GitLabOptions = {\n\t/** If you are self hosting GitLab and you want gitlab/ to default to your base url instead of https://gitlab.com */\n\tbaseUrl?: string;\n};\n\n/**\n * The built in GitLab provider.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * 'https://gitlab.com/<owner>/<repo>'\n * 'gitlab/<owner>/<repo>'\n * 'gitlab/<owner>/<repo>/-/tree/<ref>'\n * 'gitlab/<owner>/[...group]/<repo>'\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { gitlab } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [gitlab()],\n * });\n * ```\n */\nexport function gitlab(options: GitLabOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'gitlab',\n\t\tmatches: (url: string) =>\n\t\t\turl.startsWith(options.baseUrl ?? BASE_URL) ||\n\t\t\turl.startsWith('gitlab/') ||\n\t\t\turl.startsWith('gitlab:'),\n\t\tcreate: (url: string, createOpts: CreateOptions) => GitLab.create(url, options, createOpts),\n\t\tauth: {\n\t\t\ttokenStoredFor: 'provider',\n\t\t\tenvVar: 'GITLAB_TOKEN',\n\t\t},\n\t};\n}\n\ntype GitLabState = {\n\tbaseUrl: string;\n\turl: string;\n\t/** The id is the full path with all groups and the repo name */\n\tid: string;\n\tref?: string;\n};\n\nclass GitLab implements Provider {\n\tconstructor(\n\t\treadonly state: GitLabState,\n\t\treadonly opts: GitLabOptions\n\t) {}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: GitLabOptions,\n\t\tcreateOpts: CreateOptions\n\t): Promise<Provider> {\n\t\tconst state = await GitLab.getState(url, opts, createOpts);\n\t\treturn new GitLab(state, opts);\n\t}\n\n\tasync fetch(resourcePath: string, { token, fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.resolveRaw(resourcePath);\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t...(GitLab.authHeader(token) ?? {}),\n\t\t\t};\n\n\t\t\tconst response = await f(url.toString(), { headers });\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate static async getState(\n\t\turl: string,\n\t\topts: GitLabOptions,\n\t\t{ token, fetch: f = fetch }: CreateOptions\n\t): Promise<GitLabState> {\n\t\tconst parsedResult = GitLab.parseUrl(url, opts);\n\n\t\tlet { id, ref } = parsedResult;\n\n\t\t// fetch default branch if ref was not provided\n\t\tif (ref === undefined) {\n\t\t\ttry {\n\t\t\t\tconst apiUrl = GitLab.getApiUrl(parsedResult.baseUrl);\n\t\t\t\tconst response = await f(\n\t\t\t\t\tnew URL(`/projects/${encodeURIComponent(id)}`, apiUrl).toString(),\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: GitLab.authHeader(token),\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst res = await response.json();\n\t\t\t\t\tref = res.default_branch as string;\n\t\t\t\t} else {\n\t\t\t\t\tref = DEFAULT_BRANCH;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tref = DEFAULT_BRANCH;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tref,\n\t\t\turl,\n\t\t\tbaseUrl: opts.baseUrl ?? BASE_URL,\n\t\t};\n\t}\n\n\tprivate static parseUrl(url: string, opts: GitLabOptions): GitLabState {\n\t\tlet baseUrl = opts.baseUrl ?? BASE_URL;\n\t\tif (url.startsWith('gitlab:')) {\n\t\t\tbaseUrl = new URL(url.slice(7)).origin;\n\t\t} else if (url.startsWith(BASE_URL)) {\n\t\t\tbaseUrl = BASE_URL;\n\t\t}\n\n\t\tconst repo = url.replaceAll(\n\t\t\t/gitlab\\/|https:\\/\\/gitlab\\.com\\/|gitlab:https?:\\/\\/[^/]+\\//g,\n\t\t\t''\n\t\t);\n\n\t\tlet id = repo;\n\t\tconst refStartIndex = repo.indexOf('/-/tree/');\n\t\tlet ref: string | undefined;\n\t\tif (refStartIndex !== -1) {\n\t\t\tref = repo.slice(refStartIndex + 8);\n\t\t\tconst searchParamIndex = ref.indexOf('?');\n\t\t\tif (searchParamIndex !== -1) {\n\t\t\t\tref = ref.slice(0, searchParamIndex);\n\t\t\t}\n\t\t\tid = id.slice(0, refStartIndex);\n\t\t}\n\n\t\treturn {\n\t\t\tbaseUrl,\n\t\t\tid,\n\t\t\tref,\n\t\t\turl,\n\t\t};\n\t}\n\n\tprivate static getApiUrl(baseUrl: string): string {\n\t\tconst parsedBaseUrl = new URL(baseUrl ?? BASE_URL);\n\t\treturn `${parsedBaseUrl.protocol}//${parsedBaseUrl.host}/api/v4`;\n\t}\n\n\tprivate resolveRaw(resourcePath: string): URL {\n\t\tconst { id, ref, baseUrl } = this.state;\n\n\t\treturn new URL(\n\t\t\t`projects/${encodeURIComponent(id)}/repository/files/${encodeURIComponent(\n\t\t\t\tresourcePath\n\t\t\t)}/raw?ref=${ref}`,\n\t\t\t`${GitLab.getApiUrl(baseUrl)}/`\n\t\t);\n\t}\n\n\tprivate static authHeader(token: string | undefined) {\n\t\tif (!token) return undefined;\n\t\treturn {\n\t\t\t'PRIVATE-TOKEN': token,\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/http.ts",
    "content": "import type { FetchOptions, Provider, ProviderFactory } from '@/providers/types';\nimport { ProviderFetchError } from '@/utils/errors';\nimport { addTrailingSlash } from '@/utils/url';\n\nexport type HttpOptions = (\n\t| {\n\t\t\tbaseUrl?: never;\n\t\t\t/**\n\t\t\t * Override the auth header for all sites.\n\t\t\t *\n\t\t\t * @default\n\t\t\t * ```ts\n\t\t\t * (token) => ({ Authorization: `Bearer ${token}` })\n\t\t\t * ```\n\t\t\t */\n\t\t\tauthHeader?: (token: string) => Record<string, string>;\n\t  }\n\t| {\n\t\t\t/** The base url for your site. */\n\t\t\tbaseUrl: string;\n\t\t\t/** The auth header for your site. */\n\t\t\tauthHeader?: (token: string) => Record<string, string>;\n\t  }\n) & {\n\t/**\n\t * Additional headers to add to the request. The authHeader function result will override these headers.\n\t */\n\theaders?: Record<string, string>;\n};\n\n/**\n * The built in http provider. When using this provider you should place it at the end of the providers array otherwise it will match all urls.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * 'https://<domain>/<path to registry.json>'\n * ```\n *\n * @note\n * If you aren't providing a base url then you should place this provider at the end of the providers array. Otherwise it will match all urls.\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { http } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [http()],\n * });\n * ```\n * @example\n * ### Custom Provider\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { http } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [http({\n *      baseUrl: \"https://privateui.com\",\n *      authHeader: (token) => ({\n *          \"Token\": token\n *      })\n *  })],\n * });\n * ```\n *\n * Now when you use `https://privateui.com` it will use the auth header you provided.\n */\nexport function http(options: HttpOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'http',\n\t\tmatches: (url: string) => {\n\t\t\tif (options.baseUrl) {\n\t\t\t\treturn url.startsWith(options.baseUrl);\n\t\t\t}\n\n\t\t\treturn url.startsWith('http');\n\t\t},\n\t\tcreate: (url: string) => Http.create(url, options),\n\t\tauth: {\n\t\t\ttokenStoredFor: 'registry',\n\t\t},\n\t};\n}\n\ntype HttpState = {\n\turl: string;\n};\n\nclass Http implements Provider {\n\tconstructor(\n\t\treadonly state: HttpState,\n\t\treadonly opts: HttpOptions\n\t) {}\n\n\tasync fetch(resourcePath: string, { token, fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.resolveRaw(resourcePath);\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t...(this.opts.headers ?? {}),\n\t\t\t\t...(this.authHeader(token) ?? {}),\n\t\t\t};\n\n\t\t\tconst response = await f(url.toString(), { headers });\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate resolveRaw(resourcePath: string): URL {\n\t\treturn new URL(resourcePath, addTrailingSlash(this.state.url));\n\t}\n\n\tprivate authHeader(token: string | undefined): Record<string, string> | undefined {\n\t\tif (!token) return;\n\t\treturn this.opts.authHeader\n\t\t\t? this.opts.authHeader(token)\n\t\t\t: { Authorization: `Bearer ${token}` };\n\t}\n\n\tstatic async create(url: string, opts: HttpOptions): Promise<Provider> {\n\t\treturn new Http({ url }, opts);\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/index.ts",
    "content": "import { err, type Result } from 'nevereverthrow';\nimport { type Manifest, ManifestSchema } from '@/api';\nimport { type AzureOptions, azure } from '@/providers/azure';\nimport { type BitBucketOptions, bitbucket } from '@/providers/bitbucket';\nimport { type FsOptions, fs } from '@/providers/fs';\nimport { type GitHubOptions, github } from '@/providers/github';\nimport { type GitLabOptions, gitlab } from '@/providers/gitlab';\nimport { type HttpOptions, http } from '@/providers/http';\nimport { type JsrepoOptions, jsrepo } from '@/providers/jsrepo';\nimport type { FetchOptions, Provider } from '@/providers/types';\nimport { MANIFEST_FILE } from '@/utils/build';\nimport { type InvalidJSONError, ManifestFetchError, type ZodError } from '@/utils/errors';\nimport { safeParseFromJSON } from '@/utils/zod';\n\nexport const DEFAULT_PROVIDERS = [jsrepo(), github(), gitlab(), bitbucket(), azure(), http()];\n\nexport {\n\tgithub,\n\ttype GitHubOptions,\n\tgitlab,\n\ttype GitLabOptions,\n\tbitbucket,\n\ttype BitBucketOptions,\n\tazure,\n\ttype AzureOptions,\n\thttp,\n\ttype HttpOptions,\n\tjsrepo,\n\ttype JsrepoOptions,\n\tfs,\n\ttype FsOptions,\n};\nexport * from '@/providers/types';\n\nexport async function fetchManifest(\n\tprovider: Provider,\n\toptions: FetchOptions\n): Promise<Result<Manifest, InvalidJSONError | ZodError | ManifestFetchError>> {\n\ttry {\n\t\tconst manifestJson = await provider.fetch(MANIFEST_FILE, options);\n\t\treturn safeParseFromJSON(ManifestSchema, manifestJson);\n\t} catch (e) {\n\t\treturn err(new ManifestFetchError(e));\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/providers/jsrepo.ts",
    "content": "import nodeMachineId from 'node-machine-id';\nimport type {\n\tCreateOptions,\n\tFetchOptions,\n\tPrompts,\n\tProvider,\n\tProviderFactory,\n} from '@/providers/types';\nimport { JsrepoError, ProviderFetchError } from '@/utils/errors';\nimport { addTrailingSlash } from '@/utils/url';\nimport { sleep } from '@/utils/utils';\n\n/** Regex for scopes and registry names.\n * Names that don't match this regex will be rejected.\n *\n * ### Valid\n * ```txt\n * console\n * console0\n * console-0\n * ```\n *\n * ### Invalid\n * ```txt\n * Console\n * 0console\n * -console\n * console-\n * console--0\n * ```\n */\nexport const NAME_REGEX = /^(?![-0-9])(?!.*--)[a-z0-9]*(?:-[a-z0-9]+)*$/i;\n\nconst BASE_URL = 'https://www.jsrepo.com';\n\nexport type JsrepoOptions = {\n\tbaseUrl?: string;\n};\n\n/**\n * The built in jsrepo provider.\n * @param options\n * @returns\n *\n * @urlFormat\n *\n * ```\n * '@<scope>/<registry>'\n * '@<scope>/<registry>@<version>'\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import { jsrepo } from \"jsrepo/providers\";\n *\n * export default defineConfig({\n * \tproviders: [jsrepo()],\n * });\n * ```\n */\nexport function jsrepo(options: JsrepoOptions = {}): ProviderFactory & { baseUrl: string } {\n\tconst baseUrl = options.baseUrl ?? BASE_URL;\n\treturn {\n\t\tname: 'jsrepo',\n\t\tmatches: (url: string) => url.startsWith('@'),\n\t\tcreate: (url: string, createOpts: CreateOptions) => Jsrepo.create(url, options, createOpts),\n\t\tauth: {\n\t\t\ttokenStoredFor: 'provider',\n\t\t\tenvVar: 'JSREPO_TOKEN',\n\t\t\tgetToken: (opts) => getToken(opts, baseUrl),\n\t\t},\n\t\tbaseUrl,\n\t};\n}\n\ntype JsrepoState = {\n\tbaseUrl: string;\n\turl: string;\n\tscope: string;\n\tregistryName: string;\n\tversion: string;\n\tspecifier?: string;\n};\n\nclass Jsrepo implements Provider {\n\tconstructor(\n\t\treadonly state: JsrepoState,\n\t\treadonly opts: JsrepoOptions\n\t) {}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: JsrepoOptions,\n\t\t_createOpts: CreateOptions\n\t): Promise<Provider> {\n\t\tconst state = await Jsrepo.getState(url, opts);\n\t\treturn new Jsrepo(state, opts);\n\t}\n\n\tasync fetch(resourcePath: string, { token, fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.resolveRaw(resourcePath);\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\t...(Jsrepo.authHeader(token) ?? {}),\n\t\t\t};\n\n\t\t\tconst response = await f(url.toString(), { headers });\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate static async getState(url: string, opts: JsrepoOptions): Promise<JsrepoState> {\n\t\tconst parsed = Jsrepo.parseUrl(url);\n\n\t\treturn {\n\t\t\t...parsed,\n\t\t\tbaseUrl: opts.baseUrl ?? BASE_URL,\n\t\t};\n\t}\n\n\tprivate static parseUrl(url: string): {\n\t\turl: string;\n\t\tspecifier?: string;\n\t\tscope: string;\n\t\tregistryName: string;\n\t\tversion: string;\n\t} {\n\t\tconst [scope, name, ...rest] = url.split('/');\n\n\t\tif (!scope || !name) {\n\t\t\tthrow new Error(`Failed to parse invalid URL: ${url}`);\n\t\t}\n\n\t\tconst [registryName, version] = name.split('@');\n\n\t\tif (!registryName) {\n\t\t\tthrow new Error(`Failed to parse invalid URL: ${url}`);\n\t\t}\n\n\t\tlet specifier: string | undefined;\n\n\t\tif (rest.length > 0) {\n\t\t\tspecifier = rest.join('/');\n\t\t}\n\n\t\tconst parsedUrl = `${scope}/${name}`;\n\n\t\treturn {\n\t\t\turl: parsedUrl,\n\t\t\tspecifier,\n\t\t\tscope,\n\t\t\tregistryName,\n\t\t\tversion: version ?? 'latest',\n\t\t};\n\t}\n\n\tprivate resolveRaw(resourcePath: string): URL {\n\t\tconst { scope, registryName, version, baseUrl } = this.state;\n\n\t\treturn new URL(\n\t\t\t`${addTrailingSlash(baseUrl)}api/scopes/${scope}/${registryName}/v/${version}/files/${resourcePath}`\n\t\t);\n\t}\n\n\tprivate static authHeader(token: string | undefined): Record<string, string> | undefined {\n\t\tif (!token) return undefined;\n\t\treturn {\n\t\t\t'x-api-key': token,\n\t\t};\n\t}\n}\n\n/**\n * Initiates the device auth flow for jsrepo.com. Polls until the user has signed in through the site.\n *\n * @param param0\n * @returns\n */\nconst getToken = async ({ p }: { p: Prompts }, baseUrl: string) => {\n\tconst hardwareId = nodeMachineId.machineIdSync(true);\n\n\tlet anonSessionId: string;\n\n\ttry {\n\t\tconst response = await fetch(`${baseUrl}/api/login/device`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\tbody: JSON.stringify({ hardwareId }),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('There was an error creating the session');\n\t\t}\n\n\t\tconst res = await response.json();\n\n\t\tanonSessionId = res.id;\n\t} catch (err) {\n\t\tthrow new JsrepoError(err instanceof Error ? err.message : String(err), {\n\t\t\tsuggestion: 'Please try again.',\n\t\t});\n\t}\n\n\tp.log.step(`Sign in at ${p.color.cyan(`${baseUrl}/login/device/${anonSessionId}`)}`);\n\n\tconst timeout = 1000 * 60 * 60 * 15; // 15 minutes\n\n\tconst loading = p.spinner();\n\n\tconst pollingTimeout = setTimeout(() => {\n\t\tloading.stop('You never signed in.');\n\n\t\tthrow new JsrepoError('Session timed out!', {\n\t\t\tsuggestion: 'Please try again.',\n\t\t});\n\t}, timeout);\n\n\tloading.start('Waiting for you to sign in...');\n\n\twhile (true) {\n\t\t// wait initially cause c'mon ain't no way\n\t\tawait sleep(5000); // wait 5 seconds\n\n\t\tconst endpoint = `${baseUrl}/api/login/device/${anonSessionId}`;\n\n\t\ttry {\n\t\t\tconst response = await fetch(endpoint, {\n\t\t\t\tmethod: 'PATCH',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ hardwareId }),\n\t\t\t});\n\n\t\t\tif (!response.ok) continue;\n\n\t\t\tclearTimeout(pollingTimeout);\n\n\t\t\tconst token = await response.text();\n\n\t\t\tloading.stop('Done');\n\n\t\t\treturn token;\n\t\t} catch {\n\t\t\t// continue\n\t\t}\n\t}\n};\n"
  },
  {
    "path": "packages/jsrepo/src/providers/types.ts",
    "content": "import type {\n\tcancel,\n\tconfirm,\n\tgroupMultiselect,\n\tisCancel,\n\tlog,\n\tmultiselect,\n\tpassword,\n\tselect,\n\tspinner,\n\ttext,\n} from '@clack/prompts';\nimport type pc from 'picocolors';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport type FetchOptions = {\n\ttoken: string | undefined;\n\tfetch?: typeof fetch;\n};\n\nexport type CreateOptions = {\n\ttoken: string | undefined;\n\tfetch?: typeof fetch;\n\tcwd: AbsolutePath;\n};\n\nexport type Prompts = {\n\tmultiselect: typeof multiselect;\n\tselect: typeof select;\n\ttext: typeof text;\n\tlog: typeof log;\n\tpassword: typeof password;\n\tspinner: typeof spinner;\n\tconfirm: typeof confirm;\n\tgroupMultiselect: typeof groupMultiselect;\n\tisCancel: typeof isCancel;\n\tcancel: typeof cancel;\n\tcolor: typeof pc;\n};\n\nexport type GetToken = (options: {\n\t/**\n\t * Prompts to use to authenticate the user.\n\t */\n\tp: Prompts;\n}) => Promise<string>;\n\nexport type GetTokenWithRegistry = (options: {\n\t/**\n\t * The registry the user is attempting to authenticate to.\n\t */\n\tregistry: string;\n\t/**\n\t * Prompts to use to authenticate the user.\n\t */\n\tp: Prompts;\n}) => Promise<string>;\n\nexport interface ProviderFactory {\n\t/** Must be unique used for identifying the provider. */\n\tname: string;\n\t/** Determines whether or not the provider can handle the given URL. */\n\tmatches(url: string): boolean;\n\tcreate(url: string, createOpts: CreateOptions): Promise<Provider>;\n\t/**\n\t * Configures how jsrepo will authenticate the user for this provider.\n\t *\n\t * Leaving this blank if your provider doesn't require authentication.\n\t */\n\tauth?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Configures how jsrepo will store the token for this provider.\n\t\t\t\t *\n\t\t\t\t * - \"provider\" - Tokens are stored at the provider level and used by all registries for this provider.\n\t\t\t\t * - \"registry\" - Tokens are stored at the registry level and used only by that registry.\n\t\t\t\t */\n\t\t\t\ttokenStoredFor: 'provider';\n\t\t\t\t/**\n\t\t\t\t * Name of the environment variable to fallback on if no token is provided.\n\t\t\t\t */\n\t\t\t\tenvVar?: string;\n\t\t\t\t/**\n\t\t\t\t * Authenticate the user for this provider by returning a token.\n\t\t\t\t *\n\t\t\t\t * If left blank jsrepo will prompt the user for a token.\n\t\t\t\t *\n\t\t\t\t * @param options\n\t\t\t\t * @returns\n\t\t\t\t */\n\t\t\t\tgetToken?: GetToken;\n\t\t  }\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Configures how jsrepo will store the token for this provider.\n\t\t\t\t *\n\t\t\t\t * - \"provider\" - Tokens are stored at the provider level and used by all registries for this provider.\n\t\t\t\t * - \"registry\" - Tokens are stored at the registry level and used only by that registry.\n\t\t\t\t */\n\t\t\t\ttokenStoredFor: 'registry';\n\t\t\t\t/**\n\t\t\t\t * Authenticate the user for this provider/registry by returning a token.\n\t\t\t\t *\n\t\t\t\t * If left blank jsrepo will prompt the user for a token.\n\t\t\t\t *\n\t\t\t\t * @param options\n\t\t\t\t * @returns\n\t\t\t\t */\n\t\t\t\tgetToken?: GetTokenWithRegistry;\n\t\t  };\n}\n\nexport interface Provider {\n\tfetch(resourcePath: string, fetchOpts: FetchOptions): Promise<string>;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/add.ts",
    "content": "import { cancel, isCancel, select, text } from '@clack/prompts';\nimport { diffLines } from 'diff';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { DEFAULT_LANGS, type Language } from '@/langs';\nimport type { Manifest } from '@/outputs';\nimport {\n\ttype DistributedOutputItem,\n\tDistributedOutputItemSchema,\n\ttype DistributedOutputManifestFile,\n} from '@/outputs/distributed';\nimport type { RepositoryOutputFile } from '@/outputs/repository';\nimport { fetchManifest, type Provider, type ProviderFactory } from '@/providers';\nimport type { DependencyKey, RemoteDependency, UnresolvedImport } from '@/utils/build';\nimport type { Config, RegistryItemAdd, RegistryItemType } from '@/utils/config';\nimport { getPathsMatcher, resolvePath, resolvePaths } from '@/utils/config/utils';\nimport { formatDiff } from '@/utils/diff';\nimport { shouldIncludeRole } from '@/utils/roles';\nimport type { PathsMatcher } from '@/utils/tsconfig';\nimport { safeParseFromJSON } from '@/utils/zod';\nimport {\n\tInvalidDependencyError,\n\ttype InvalidJSONError,\n\tInvalidRegistryError,\n\ttype JsrepoError,\n\ttype ManifestFetchError,\n\tMultipleRegistriesError,\n\tNoPathProvidedError,\n\tProviderFetchError,\n\tRegistryFileFetchError,\n\tRegistryItemFetchError,\n\tRegistryItemNotFoundError,\n\tRegistryNotProvidedError,\n\tUnreachable,\n\ttype ZodError,\n} from './errors';\nimport { existsSync, readFileSync, writeFileSync } from './fs';\nimport { parsePackageName } from './parse-package-name';\nimport { joinAbsolute } from './path';\nimport { VERTICAL_LINE } from './prompts';\nimport { TokenManager } from './token-manager';\nimport type { AbsolutePath, ItemRelativePath } from './types';\n\nexport type ResolvedRegistry = {\n\turl: string;\n\tprovider: Provider;\n\tmanifest: Manifest;\n\ttoken?: string;\n};\n\n/**\n * Resolves each registry url to it's manifest and provider so we can start fetching items.\n *\n * @param registries - An array of registry urls to resolve. i.e. [\"github/ieedan/std\", \"gitlab/ieedan/std\"]\n * @param options\n * @returns\n */\nexport async function resolveRegistries(\n\tregistries: string[],\n\t{ cwd, providers }: { cwd: AbsolutePath; providers: ProviderFactory[] }\n): Promise<\n\tResult<\n\t\tMap<string, ResolvedRegistry>,\n\t\tInvalidRegistryError | ManifestFetchError | InvalidJSONError | ZodError\n\t>\n> {\n\tif (registries.length === 0) return ok(new Map());\n\tconst tokenManager = new TokenManager();\n\tconst resolvedRegistries: Result<\n\t\tResolvedRegistry,\n\t\tInvalidRegistryError | ManifestFetchError | InvalidJSONError | ZodError\n\t>[] = await Promise.all(\n\t\tregistries.map(async (registry) => {\n\t\t\tconst pf = providers.find((p) => p?.matches(registry));\n\t\t\tif (!pf) return err(new InvalidRegistryError(registry));\n\t\t\tconst token = tokenManager.get(pf, registry);\n\t\t\tconst provider = await pf?.create(registry, { cwd, token });\n\t\t\tconst manifestResult = await fetchManifest(provider, { token });\n\t\t\tif (manifestResult.isErr()) return err(manifestResult.error);\n\t\t\treturn ok({ url: registry, provider, manifest: manifestResult.value, token });\n\t\t})\n\t);\n\tconst resultMap = new Map<string, ResolvedRegistry>();\n\tfor (const result of resolvedRegistries) {\n\t\tif (result.isErr()) return err(result.error);\n\t\tresultMap.set(result.value.url, result.value);\n\t}\n\treturn ok(resultMap);\n}\n\nexport type WantedItem = {\n\tregistryUrl?: string;\n\titemName: string;\n};\n\n/**\n * Parses the wanted items and needed registries from an array of fully-qualified and or unqualified items.\n *\n * @remarks this only parses the items and does not actually resolve the items or registries\n *\n * @param items - An array of fully-qualified and or unqualified items. i.e. [\"github/ieedan/std\", \"gitlab/ieedan/std\", \"math\"]\n * @param options\n * @returns\n */\nexport function parseWantedItems(\n\titems: string[],\n\t{ providers, registries }: { providers: ProviderFactory[]; registries: string[] }\n): Result<{ wantedItems: WantedItem[]; neededRegistries: string[] }, RegistryNotProvidedError> {\n\tconst wantedItems: WantedItem[] = items.map((item) => {\n\t\tif (providers.some((p) => p.matches(item))) {\n\t\t\tconst index = item.lastIndexOf('/');\n\t\t\tconst registryUrl = item.substring(0, index);\n\t\t\tconst itemName = item.substring(index + 1);\n\t\t\treturn {\n\t\t\t\tregistryUrl,\n\t\t\t\titemName,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\titemName: item,\n\t\t};\n\t});\n\n\tconst allFullyQualified = wantedItems.every((item) => item.registryUrl);\n\tconst noneFullyQualified = wantedItems.every((item) => item.registryUrl === undefined);\n\tconst wantedRegistries = wantedItems\n\t\t.filter((item) => item.registryUrl !== undefined)\n\t\t.map((item) => item.registryUrl!);\n\n\t// can't have unqualified items if no registries are provided\n\tif (noneFullyQualified && registries.length === 0) return err(new RegistryNotProvidedError());\n\n\treturn ok({\n\t\twantedItems,\n\t\tneededRegistries: [...wantedRegistries, ...(allFullyQualified ? [] : registries)],\n\t});\n}\n\nexport type ResolvedWantedItem = {\n\tregistry: ResolvedRegistry;\n\titem: {\n\t\tname: string;\n\t\tdescription: string | undefined;\n\t\tadd: RegistryItemAdd | undefined;\n\t\ttype: RegistryItemType;\n\t\tregistryDependencies: string[] | undefined;\n\t\tdependencies: (RemoteDependency | string)[] | undefined;\n\t\tdevDependencies: (RemoteDependency | string)[] | undefined;\n\t\tfiles: Array<RepositoryOutputFile | DistributedOutputManifestFile>;\n\t\tenvVars: Record<string, string> | undefined;\n\t\tcategories: string[] | undefined;\n\t\tmeta: Record<string, string> | undefined;\n\t};\n};\n\n/**\n * Resolve the wanted items to the resolved registry. This will ask the user to select a registry if there are multiple matches to remove an ambiguity.\n * @remarks The only async \"work\" that should be done here is waiting for the user to respond\n * @param wantedItems\n * @param options\n */\nexport async function resolveWantedItems(\n\twantedItems: WantedItem[],\n\t{\n\t\tresolvedRegistries,\n\t\tnonInteractive,\n\t}: { resolvedRegistries: Map<string, ResolvedRegistry>; nonInteractive: boolean }\n): Promise<Result<ResolvedWantedItem[], MultipleRegistriesError | RegistryItemNotFoundError>> {\n\tconst resolvedWantedItems: ResolvedWantedItem[] = [];\n\tfor (const wantedItem of wantedItems) {\n\t\tlet resolvedRegistry: ResolvedRegistry;\n\t\tlet resolvedItem: ResolvedWantedItem['item'];\n\n\t\tif (wantedItem.registryUrl) {\n\t\t\tresolvedRegistry = resolvedRegistries.get(wantedItem.registryUrl)!;\n\n\t\t\tconst item = resolvedRegistry.manifest.items.find(\n\t\t\t\t(i) => i.name === wantedItem.itemName\n\t\t\t);\n\t\t\tif (!item) {\n\t\t\t\treturn err(\n\t\t\t\t\tnew RegistryItemNotFoundError(wantedItem.itemName, resolvedRegistry.url)\n\t\t\t\t);\n\t\t\t}\n\t\t\tresolvedItem = { ...item, add: item.add ?? 'when-added' };\n\t\t} else {\n\t\t\tconst blockMatches = new Map<\n\t\t\t\tstring,\n\t\t\t\t{ registry: ResolvedRegistry; item: ResolvedWantedItem['item'] }\n\t\t\t>();\n\t\t\tfor (const [url, registry] of resolvedRegistries.entries()) {\n\t\t\t\tconst item = registry.manifest.items.find((i) => i.name === wantedItem.itemName);\n\t\t\t\tif (item)\n\t\t\t\t\tblockMatches.set(url, {\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\titem,\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (blockMatches.size === 0) {\n\t\t\t\treturn err(new RegistryItemNotFoundError(wantedItem.itemName));\n\t\t\t}\n\n\t\t\tif (blockMatches.size > 1) {\n\t\t\t\tif (nonInteractive)\n\t\t\t\t\treturn err(\n\t\t\t\t\t\tnew MultipleRegistriesError(\n\t\t\t\t\t\t\twantedItem.itemName,\n\t\t\t\t\t\t\tArray.from(blockMatches.keys())\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\n\t\t\t\tconst response = await select({\n\t\t\t\t\tmessage: `Multiple registries contain ${pc.cyan(wantedItem.itemName)}. Please select one:`,\n\t\t\t\t\toptions: Array.from(blockMatches.entries()).map(([url]) => ({\n\t\t\t\t\t\tlabel: wantedItem.itemName,\n\t\t\t\t\t\tvalue: url,\n\t\t\t\t\t\thint: url,\n\t\t\t\t\t})),\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(response)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\tconst { registry, item } = blockMatches.get(response)!;\n\n\t\t\t\tresolvedRegistry = registry;\n\t\t\t\tresolvedItem = item;\n\t\t\t} else {\n\t\t\t\tconst { registry, item } = blockMatches.values().next().value!;\n\t\t\t\tresolvedRegistry = registry;\n\t\t\t\tresolvedItem = item;\n\t\t\t}\n\t\t}\n\n\t\tresolvedWantedItems.push({ registry: resolvedRegistry, item: resolvedItem });\n\t}\n\n\treturn ok(resolvedWantedItems);\n}\n\nexport type ResolvedItem = {\n\tname: string;\n\tdescription: string | undefined;\n\tadd: RegistryItemAdd | undefined;\n\ttype: RegistryItemType;\n\tregistryDependencies: string[] | undefined;\n\tdependencies: (RemoteDependency | string)[] | undefined;\n\tdevDependencies: (RemoteDependency | string)[] | undefined;\n\tregistry: ResolvedRegistry;\n\tfiles: Array<RepositoryOutputFile | DistributedOutputManifestFile>;\n\tenvVars: Record<string, string> | undefined;\n\tcategories: string[] | undefined;\n\tmeta: Record<string, string> | undefined;\n};\n\nexport type ItemRepository = {\n\tname: string;\n\ttype: RegistryItemType;\n\tadd: RegistryItemAdd | undefined;\n\tdescription: string | undefined;\n\tdependencies: (RemoteDependency | string)[] | undefined;\n\tdevDependencies: (RemoteDependency | string)[] | undefined;\n\tregistry: ResolvedRegistry;\n\tfiles: Array<RepositoryOutputFile & { content: string }>;\n\tenvVars: Record<string, string> | undefined;\n\tcategories: string[] | undefined;\n\tmeta: Record<string, string> | undefined;\n};\n\nexport type ItemDistributed = DistributedOutputItem & { registry: ResolvedRegistry };\n\nexport async function fetchItem(block: ResolvedItem) {\n\ttry {\n\t\tconst contents = await block.registry.provider.fetch(`${block.name}.json`, {\n\t\t\ttoken: block.registry.token,\n\t\t});\n\n\t\treturn safeParseFromJSON(DistributedOutputItemSchema, contents);\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew RegistryItemFetchError(e, {\n\t\t\t\tregistry: block.registry.url,\n\t\t\t\titem: block.name,\n\t\t\t})\n\t\t);\n\t}\n}\n\nexport async function fetchFile(fileName: string, block: ResolvedItem) {\n\ttry {\n\t\treturn ok(await block.registry.provider.fetch(fileName, { token: block.registry.token }));\n\t} catch (error) {\n\t\tif (error instanceof ProviderFetchError)\n\t\t\treturn err(\n\t\t\t\tnew RegistryFileFetchError(error.originalMessage, {\n\t\t\t\t\tregistry: block.registry.url,\n\t\t\t\t\titem: block.name,\n\t\t\t\t\tresourcePath: error.resourcePath,\n\t\t\t\t})\n\t\t\t);\n\t\treturn err(\n\t\t\tnew RegistryFileFetchError(error instanceof Error ? error.message : String(error), {\n\t\t\t\tregistry: block.registry.url,\n\t\t\t\titem: block.name,\n\t\t\t\tresourcePath: fileName,\n\t\t\t})\n\t\t);\n\t}\n}\n\n/**\n * Fetches an item from the registry in repository mode.\n *\n * @param item\n * @param options\n * @returns\n */\nasync function fetchItemRepositoryMode(\n\titem: ResolvedItem\n): Promise<Result<ItemRepository, RegistryFileFetchError>> {\n\tconst files = await Promise.all(\n\t\t(item.files as RepositoryOutputFile[]).map(async (file) => {\n\t\t\tconst result = await fetchFile(file.relativePath, item);\n\t\t\tif (result.isErr()) return err(result.error);\n\t\t\treturn ok({\n\t\t\t\t...file,\n\t\t\t\tcontent: result.value,\n\t\t\t});\n\t\t})\n\t);\n\n\tconst filesResult = [];\n\tfor (const file of files) {\n\t\tif (file.isErr()) return err(file.error);\n\t\tfilesResult.push(file.value);\n\t}\n\n\treturn ok({\n\t\tname: item.name,\n\t\ttype: item.type,\n\t\tadd: item.add,\n\t\tdescription: item.description,\n\t\tdependencies: item.dependencies,\n\t\tdevDependencies: item.devDependencies,\n\t\tregistry: item.registry,\n\t\tfiles: filesResult,\n\t\tenvVars: item.envVars,\n\t\tcategories: item.categories,\n\t\tmeta: item.meta,\n\t});\n}\n\n/**\n * Fetches an item from the registry in distributed mode.\n *\n * @param item\n * @param options\n * @returns\n */\nasync function fetchItemDistributedMode(\n\titem: ResolvedItem\n): Promise<Result<ItemDistributed, RegistryItemFetchError | InvalidJSONError>> {\n\tconst result = await fetchItem(item);\n\tif (result.isErr()) return err(result.error);\n\treturn ok({\n\t\t...result.value,\n\t\tfiles: result.value.files.map((file) => ({\n\t\t\t...file,\n\t\t\tpath:\n\t\t\t\t// this is a weird compat thing.\n\t\t\t\t// shadcn/ui has the full path to the file even though the client only needs the basename\n\t\t\t\tresult.value.$schema === 'https://ui.shadcn.com/schema/registry-item.json'\n\t\t\t\t\t? (path.basename(file.path) as ItemRelativePath)\n\t\t\t\t\t: file.path,\n\t\t})),\n\t\tregistry: item.registry,\n\t});\n}\n\ntype ExtractResultValue<T> = T extends Result<infer V, unknown> ? V : never;\n\nexport async function fetchAllResolvedItems(\n\titems: ResolvedItem[]\n): Promise<\n\tResult<\n\t\tArray<ItemRepository | ItemDistributed>,\n\t\tRegistryItemFetchError | RegistryFileFetchError | InvalidJSONError\n\t>\n> {\n\tconst itemsResult = await Promise.all(\n\t\titems.map(async (item) => {\n\t\t\tif (item.registry.manifest.type === 'repository') {\n\t\t\t\treturn await fetchItemRepositoryMode(item);\n\t\t\t}\n\n\t\t\treturn await fetchItemDistributedMode(item);\n\t\t})\n\t);\n\n\tconst finalItems = [];\n\tfor (const item of itemsResult) {\n\t\tif (item.isErr()) return err(item.error);\n\t\tfinalItems.push(item.value);\n\t}\n\n\treturn ok(finalItems);\n}\n\n/**\n * Tries to get the path for an item. If the path is not set, it will prompt the user for a path.\n *\n * @remarks the only async \"work\" is prompting the user for a path if they don't have a path set.\n *\n * @param item\n * @param param1\n * @returns\n */\nexport async function getItemLocation(\n\titem: {\n\t\tname: string;\n\t\ttype: RegistryItemType;\n\t\tfiles: { path: string; target?: string }[];\n\t\tregistry: ResolvedRegistry;\n\t},\n\t{\n\t\tpaths,\n\t\tnonInteractive,\n\t\toptions,\n\t\tmatcher,\n\t}: {\n\t\tpaths: Config['paths'];\n\t\tnonInteractive: boolean;\n\t\toptions: { cwd: string };\n\t\tmatcher: PathsMatcher;\n\t}\n): Promise<Result<{ resolvedPath: string; path: string }, NoPathProvidedError>> {\n\t// if all the files are just target files we don't need to prompt the user for a path\n\tif (item.files.filter((file) => file.target !== undefined).length === item.files.length) {\n\t\treturn ok({\n\t\t\tpath: '',\n\t\t\tresolvedPath: '',\n\t\t});\n\t}\n\n\tconst catchAllPath = paths['*'];\n\tconst type = normalizeItemTypeForPath(item.type);\n\tconst path = paths[type];\n\tif (!path) {\n\t\tif (catchAllPath) {\n\t\t\treturn ok({\n\t\t\t\tpath: catchAllPath,\n\t\t\t\t// already resolved\n\t\t\t\tresolvedPath: catchAllPath,\n\t\t\t});\n\t\t}\n\n\t\t// we just error in non-interactive mode\n\t\tif (nonInteractive) return err(new NoPathProvidedError({ item: item.name, type }));\n\n\t\tconst defaultPath = item.registry.manifest.defaultPaths?.[type] ?? `./src/${type}`;\n\n\t\tconst blocksPath = await text({\n\t\t\tmessage: `Where would you like to add ${pc.cyan(type)}?`,\n\t\t\tplaceholder: defaultPath,\n\t\t\tinitialValue: defaultPath,\n\t\t\tdefaultValue: defaultPath,\n\t\t\tvalidate(value) {\n\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t},\n\t\t});\n\n\t\tif (isCancel(blocksPath)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\treturn ok({\n\t\t\tpath: blocksPath,\n\t\t\tresolvedPath: resolvePath(blocksPath, { cwd: options.cwd, matcher }),\n\t\t});\n\t}\n\treturn ok({ path, resolvedPath: path });\n}\n\nexport type FetchedItem = ExtractResultValue<\n\tAwaited<ReturnType<typeof fetchAllResolvedItems>>\n>[number];\n\nexport type FileWithContents = ExtractResultValue<\n\tAwaited<ReturnType<typeof fetchAllResolvedItems>>\n>[number]['files'][number];\n\nexport async function transformRemoteContent(\n\tfile: FileWithContents,\n\t{\n\t\titem,\n\t\toptions,\n\t\tconfig,\n\t}: {\n\t\titem: FetchedItem;\n\t\tlanguages: Language[];\n\t\toptions: { cwd: AbsolutePath };\n\t\tconfig: Config | undefined;\n\t\titemPaths: Record<string, { path: string; alias?: string }>;\n\t}\n): Promise<FileWithContents> {\n\tfor (const transform of config?.transforms ?? []) {\n\t\tconst result = await transform.transform({\n\t\t\tcode: file.content,\n\t\t\tfileName: file.path,\n\t\t\toptions: {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tregistryUrl: item.registry.url,\n\t\t\t\titem: item,\n\t\t\t},\n\t\t});\n\t\tfile.content = result.code ?? file.content;\n\t\tfile.path = result.fileName ?? file.path;\n\t}\n\n\treturn file;\n}\n\nexport function getTargetPath(\n\tfile: { path: ItemRelativePath; target?: string },\n\t{\n\t\titemPath,\n\t\toptions,\n\t}: {\n\t\titemPath: { path: string };\n\t\toptions: { cwd: AbsolutePath };\n\t}\n): AbsolutePath {\n\tif (file.target) {\n\t\treturn joinAbsolute(options.cwd, file.target);\n\t} else {\n\t\treturn joinAbsolute(options.cwd, itemPath.path, file.path);\n\t}\n}\n\nexport type RegistryItemWithContent = ItemRepository | ItemDistributed;\n\nexport async function resolveAndFetchAllItems(\n\twantedItems: ResolvedWantedItem[],\n\toptions?: {\n\t\twithRoles?: Set<string>;\n\t}\n): Promise<\n\tResult<\n\t\tArray<RegistryItemWithContent>,\n\t\t| RegistryItemNotFoundError\n\t\t| RegistryItemFetchError\n\t\t| RegistryFileFetchError\n\t\t| InvalidJSONError\n\t>\n> {\n\tconst resolvedResult = resolveTree(wantedItems, {\n\t\tresolvedItems: new Map(),\n\t\toptions: options ?? {},\n\t});\n\tif (resolvedResult.isErr()) return err(resolvedResult.error);\n\tconst resolvedItems = resolvedResult.value;\n\n\tconst itemsResult = await fetchAllResolvedItems(resolvedItems);\n\tif (itemsResult.isErr()) return err(itemsResult.error);\n\tconst items = itemsResult.value;\n\n\treturn ok(items);\n}\n\n/**\n * Recursively resolves the tree of wanted items and their dependencies.\n *\n * @param wantedItems - The wanted items to resolve.\n * @param options\n * @returns\n */\nexport function resolveTree(\n\twantedItems: ResolvedWantedItem[],\n\t{\n\t\tresolvedItems,\n\t\toptions,\n\t}: {\n\t\tresolvedItems: Map<string, ResolvedItem>;\n\t\toptions: { withRoles?: Set<string> };\n\t}\n): Result<ResolvedItem[], RegistryItemNotFoundError> {\n\tfor (const wantedItem of wantedItems) {\n\t\tif (resolvedItems.has(wantedItem.item.name)) continue;\n\n\t\tconst needsResolving: ResolvedWantedItem[] = [];\n\t\tfor (const registryDependency of wantedItem.item.registryDependencies ?? []) {\n\t\t\tif (resolvedItems.has(registryDependency)) continue;\n\t\t\tconst item = wantedItem.registry.manifest.items.find(\n\t\t\t\t(i) => i.name === registryDependency\n\t\t\t);\n\t\t\tif (!item)\n\t\t\t\treturn err(\n\t\t\t\t\tnew RegistryItemNotFoundError(registryDependency, wantedItem.registry.url)\n\t\t\t\t);\n\t\t\tneedsResolving.push({\n\t\t\t\tregistry: wantedItem.registry,\n\t\t\t\titem,\n\t\t\t});\n\t\t}\n\n\t\t// ensure we also add any registry dependencies of added files\n\t\tfor (const file of wantedItem.item.files) {\n\t\t\tif (!shouldIncludeRole(file.role, options.withRoles ?? new Set())) continue;\n\n\t\t\tfor (const registryDependency of file.registryDependencies ?? []) {\n\t\t\t\tif (resolvedItems.has(registryDependency)) continue;\n\t\t\t\tconst item = wantedItem.registry.manifest.items.find(\n\t\t\t\t\t(i) => i.name === registryDependency\n\t\t\t\t);\n\t\t\t\tif (!item)\n\t\t\t\t\treturn err(\n\t\t\t\t\t\tnew RegistryItemNotFoundError(registryDependency, wantedItem.registry.url)\n\t\t\t\t\t);\n\t\t\t\tneedsResolving.push({\n\t\t\t\t\tregistry: wantedItem.registry,\n\t\t\t\t\titem,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tresolvedItems.set(wantedItem.item.name, {\n\t\t\tname: wantedItem.item.name,\n\t\t\ttype: wantedItem.item.type,\n\t\t\tdescription: wantedItem.item.description,\n\t\t\tadd: wantedItem.item.add,\n\t\t\tregistryDependencies: wantedItem.item.registryDependencies,\n\t\t\tdependencies: wantedItem.item.dependencies,\n\t\t\tdevDependencies: wantedItem.item.devDependencies,\n\t\t\tregistry: wantedItem.registry,\n\t\t\tfiles: wantedItem.item.files,\n\t\t\tenvVars: wantedItem.item.envVars,\n\t\t\tcategories: wantedItem.item.categories,\n\t\t\tmeta: wantedItem.item.meta,\n\t\t});\n\n\t\tconst resolvedRegistryDependencies = resolveTree(needsResolving, {\n\t\t\tresolvedItems,\n\t\t\toptions,\n\t\t});\n\t\tif (resolvedRegistryDependencies.isErr()) return err(resolvedRegistryDependencies.error);\n\t\tfor (const resolvedItem of resolvedRegistryDependencies.value) {\n\t\t\tresolvedItems.set(resolvedItem.name, resolvedItem);\n\t\t}\n\t}\n\treturn ok(Array.from(resolvedItems.values()));\n}\n\nexport async function getPathsForItems({\n\titems,\n\tconfig,\n\toptions,\n\tcontinueOnNoPath = false,\n}: {\n\titems: {\n\t\tname: string;\n\t\ttype: RegistryItemType;\n\t\tfiles: { path: ItemRelativePath; type: RegistryItemType; target?: string }[];\n\t\tregistry: ResolvedRegistry;\n\t}[];\n\tconfig: Config | undefined;\n\toptions: { cwd: AbsolutePath; yes: boolean };\n\tcontinueOnNoPath?: boolean;\n}): Promise<\n\tResult<\n\t\t{\n\t\t\titemPaths: Record<string, { path: string; alias?: string }>;\n\t\t\tresolvedPaths: Config['paths'];\n\t\t\tupdatedPaths: Record<string, string>;\n\t\t},\n\t\tNoPathProvidedError\n\t>\n> {\n\tconst pathsMatcher = getPathsMatcher({ cwd: options.cwd });\n\n\tconst resolvedPaths = resolvePaths(config?.paths ?? {}, {\n\t\tcwd: options.cwd,\n\t\tmatcher: pathsMatcher,\n\t});\n\tconst updatedPaths: Record<string, string> = {};\n\t// get any paths that are not already set\n\tfor (const item of items) {\n\t\tconst uniqueTypes = Array.from(\n\t\t\tnew Set(Array.from(item.files, (file) => normalizeItemTypeForPath(file.type)))\n\t\t);\n\t\tfor (const type of uniqueTypes) {\n\t\t\tconst itemPath = resolvedPaths[`${type}/${item.name}`];\n\t\t\tconst typePath = resolvedPaths[type];\n\t\t\tif (itemPath !== undefined) {\n\t\t\t\tresolvedPaths[`${type}/${item.name}`] = itemPath;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (typePath !== undefined) {\n\t\t\t\tresolvedPaths[type] = typePath;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst result = await getItemLocation(item, {\n\t\t\t\tpaths: resolvedPaths,\n\t\t\t\tnonInteractive: options.yes,\n\t\t\t\toptions,\n\t\t\t\tmatcher: pathsMatcher,\n\t\t\t});\n\t\t\tif (result.isErr()) {\n\t\t\t\tif (result.error instanceof NoPathProvidedError && continueOnNoPath) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\treturn err(result.error);\n\t\t\t}\n\t\t\tconst { path: originalPath, resolvedPath } = result.value;\n\t\t\tresolvedPaths[type] = resolvedPath;\n\t\t\tupdatedPaths[type] = originalPath;\n\t\t\tif (config) {\n\t\t\t\tconfig.paths[type] = originalPath;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst itemPaths = getItemPaths(items, resolvedPaths, { config });\n\n\treturn ok({ itemPaths, resolvedPaths, updatedPaths });\n}\n\nexport function getItemPaths(\n\titems: {\n\t\tname: string;\n\t\ttype: RegistryItemType;\n\t\tfiles: { path: ItemRelativePath; type: RegistryItemType; target?: string }[];\n\t}[],\n\tresolvedPaths: Config['paths'],\n\t{\n\t\tconfig,\n\t}: {\n\t\tconfig: Config | undefined;\n\t}\n): Record<\n\tstring,\n\t{\n\t\tpath: string;\n\t\talias?: string;\n\t}\n> {\n\tconst itemPaths = items.reduce(\n\t\t(acc, item) => {\n\t\t\tfor (const file of item.files) {\n\t\t\t\tconst type = normalizeItemTypeForPath(file.type);\n\n\t\t\t\tconst itemPath = resolvedPaths[`${type}/${item.name}`];\n\t\t\t\tconst itemAlias =\n\t\t\t\t\tconfig?.paths[`${type}/${item.name}`] &&\n\t\t\t\t\tconfig.paths[`${type}/${item.name}`] !== itemPath\n\t\t\t\t\t\t? config.paths[`${type}/${item.name}`]\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tconst typePath = resolvedPaths[type];\n\t\t\t\tconst typeAlias =\n\t\t\t\t\tconfig?.paths[type] && config.paths[type] !== typePath\n\t\t\t\t\t\t? config.paths[type]\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tacc[`${type}/${item.name}`] = itemPath\n\t\t\t\t\t? { path: itemPath, alias: itemAlias }\n\t\t\t\t\t: // at this point we know that the type must be defined\n\t\t\t\t\t\t{ path: typePath!, alias: typeAlias! };\n\t\t\t}\n\n\t\t\treturn acc;\n\t\t},\n\t\t{} as Record<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tpath: string;\n\t\t\t\talias?: string;\n\t\t\t}\n\t\t>\n\t);\n\n\tfor (const [key, value] of Object.entries(resolvedPaths)) {\n\t\t// we only care about type only paths\n\t\tif (key.includes('/')) continue;\n\t\t// just rename for readability\n\t\tconst type = key;\n\n\t\tconst typePath = value;\n\t\tconst typeAlias =\n\t\t\tconfig?.paths[type] && config.paths[type] !== typePath ? config.paths[type] : undefined;\n\n\t\t// let's also make sure we just add the straight type path if it's defined\n\t\tif (typePath) {\n\t\t\titemPaths[type] = { path: typePath, alias: typeAlias };\n\t\t}\n\t}\n\n\treturn itemPaths;\n}\n\nexport type UpdatedFile = {\n\titemName: string;\n\tregistryUrl: string;\n\t_imports_: UnresolvedImport[];\n\tfilePath: AbsolutePath;\n\tnewPath: ItemRelativePath;\n\toriginalPath: ItemRelativePath;\n\tcontent: string;\n\tfileType: RegistryItemType;\n} & (\n\t| {\n\t\t\ttype: 'update';\n\t\t\toldContent: string;\n\t  }\n\t| {\n\t\t\ttype: 'create';\n\t  }\n);\n\n/**\n * Prepares updates for the items without applying them. This will transform the files and collect any dependencies and env vars needed.\n *\n * @param param0\n * @returns\n */\nexport async function prepareUpdates({\n\titems,\n\titemPaths,\n\tconfigResult,\n\toptions,\n}: {\n\tconfigResult: { path: string; config: Config } | null;\n\toptions: {\n\t\tcwd: AbsolutePath;\n\t\tyes: boolean;\n\t\twithRoles?: Set<string>;\n\t};\n\titemPaths: Record<string, { path: string; alias?: string }>;\n\titems: (ItemRepository | ItemDistributed)[];\n}): Promise<\n\tResult<\n\t\t{\n\t\t\tneededDependencies: {\n\t\t\t\tdependencies: RemoteDependency[];\n\t\t\t\tdevDependencies: RemoteDependency[];\n\t\t\t};\n\t\t\tneededEnvVars: Record<string, string> | undefined;\n\t\t\tneededFiles: UpdatedFile[];\n\t\t},\n\t\tInvalidDependencyError\n\t>\n> {\n\tconst neededFiles: UpdatedFile[] = [];\n\tconst neededDependencies = new Map<DependencyKey, RemoteDependency>();\n\tconst neededDevDependencies = new Map<DependencyKey, RemoteDependency>();\n\tlet neededEnvVars: Record<string, string> | undefined;\n\n\tfor (const item of items) {\n\t\tfor (let file of item.files) {\n\t\t\tif (!shouldIncludeRole(file.role, options.withRoles ?? new Set())) continue;\n\n\t\t\tconst type = normalizeItemTypeForPath(file.type);\n\t\t\tconst expectedPath = itemPaths[`${type}/${item.name}`]!;\n\n\t\t\tconst originalPath = file.path;\n\n\t\t\tfile = await transformRemoteContent(file, {\n\t\t\t\titem,\n\t\t\t\tlanguages: configResult?.config.languages ?? DEFAULT_LANGS,\n\t\t\t\toptions,\n\t\t\t\tconfig: configResult?.config,\n\t\t\t\titemPaths,\n\t\t\t});\n\n\t\t\tfor (const dependency of file.dependencies ?? []) {\n\t\t\t\tneededDependencies.set(\n\t\t\t\t\t`${dependency.ecosystem}:${dependency.name}@${dependency.version}`,\n\t\t\t\t\tdependency\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (const dependency of file.devDependencies ?? []) {\n\t\t\t\tneededDevDependencies.set(\n\t\t\t\t\t`${dependency.ecosystem}:${dependency.name}@${dependency.version}`,\n\t\t\t\t\tdependency\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst filePath = getTargetPath(file, { itemPath: expectedPath, options });\n\n\t\t\t// check if the file needs to be updated\n\t\t\tif (existsSync(filePath)) {\n\t\t\t\tconst originalContents = readFileSync(filePath)._unsafeUnwrap();\n\n\t\t\t\tneededFiles.push({\n\t\t\t\t\ttype: 'update',\n\t\t\t\t\tfileType: file.type,\n\t\t\t\t\toldContent: originalContents,\n\t\t\t\t\t_imports_: file._imports_ ?? [],\n\t\t\t\t\tfilePath,\n\t\t\t\t\tnewPath: file.path,\n\t\t\t\t\toriginalPath,\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\titemName: item.name,\n\t\t\t\t\tregistryUrl: item.registry.url,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tneededFiles.push({\n\t\t\t\t\ttype: 'create',\n\t\t\t\t\tfileType: file.type,\n\t\t\t\t\t_imports_: file._imports_ ?? [],\n\t\t\t\t\tfilePath,\n\t\t\t\t\tnewPath: file.path,\n\t\t\t\t\toriginalPath,\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\titemName: item.name,\n\t\t\t\t\tregistryUrl: item.registry.url,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// add any dependencies\n\t\tfor (const remoteDependency of item.dependencies ?? []) {\n\t\t\tif (typeof remoteDependency === 'string') {\n\t\t\t\tconst parsedResult = parsePackageName(remoteDependency);\n\t\t\t\tif (parsedResult.isErr())\n\t\t\t\t\treturn err(\n\t\t\t\t\t\tnew InvalidDependencyError({\n\t\t\t\t\t\t\tdependency: remoteDependency,\n\t\t\t\t\t\t\tregistryName: item.registry.url,\n\t\t\t\t\t\t\titemName: item.name,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\tconst parsed = parsedResult.value;\n\t\t\t\t// assume js ecosystem for string deps\n\t\t\t\tneededDependencies.set(`js:${parsed.name}@${parsed.version}`, {\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\tname: parsed.name,\n\t\t\t\t\tversion: parsed.version,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tneededDependencies.set(\n\t\t\t\t\t`${remoteDependency.ecosystem}:${remoteDependency.name}@${remoteDependency.version}`,\n\t\t\t\t\tremoteDependency\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tfor (const remoteDevDependency of item.devDependencies ?? []) {\n\t\t\tif (typeof remoteDevDependency === 'string') {\n\t\t\t\tconst parsedResult = parsePackageName(remoteDevDependency);\n\t\t\t\tif (parsedResult.isErr())\n\t\t\t\t\treturn err(\n\t\t\t\t\t\tnew InvalidDependencyError({\n\t\t\t\t\t\t\tdependency: remoteDevDependency,\n\t\t\t\t\t\t\tregistryName: item.registry.url,\n\t\t\t\t\t\t\titemName: item.name,\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\tconst parsed = parsedResult.value;\n\t\t\t\t// assume js ecosystem for string deps\n\t\t\t\tneededDevDependencies.set(`js:${parsed.name}@${parsed.version}`, {\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\tname: parsed.name,\n\t\t\t\t\tversion: parsed.version,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tneededDevDependencies.set(\n\t\t\t\t\t`${remoteDevDependency.ecosystem}:${remoteDevDependency.name}@${remoteDevDependency.version}`,\n\t\t\t\t\tremoteDevDependency\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// add any env vars\n\t\tfor (const [name, value] of Object.entries(item.envVars ?? {})) {\n\t\t\tif (neededEnvVars === undefined) {\n\t\t\t\tneededEnvVars = {};\n\t\t\t}\n\t\t\tneededEnvVars[name] = value;\n\t\t}\n\t}\n\n\t// transform the imports for each file\n\tfor (const file of neededFiles) {\n\t\t// update _imports_ so that they reference the transformed file path\n\t\tconst newImports: UnresolvedImport[] = [];\n\t\tfor (const imp of file._imports_ ?? []) {\n\t\t\tconst targetFile = neededFiles.find(\n\t\t\t\t(f) =>\n\t\t\t\t\tf.originalPath === imp.file.path &&\n\t\t\t\t\timp.item === f.itemName &&\n\t\t\t\t\tf.fileType === imp.file.type\n\t\t\t);\n\t\t\tif (!targetFile) {\n\t\t\t\tnewImports.push(imp);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tnewImports.push({\n\t\t\t\timport: imp.import,\n\t\t\t\titem: imp.item,\n\t\t\t\tfile: { type: imp.file.type, path: targetFile.newPath },\n\t\t\t\tmeta: imp.meta,\n\t\t\t});\n\t\t}\n\n\t\tconst lang = configResult?.config.languages.find((lang) =>\n\t\t\tlang.canResolveDependencies(file.newPath)\n\t\t);\n\t\tfile.content =\n\t\t\t(await lang?.transformImports(file.content, newImports, {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\ttargetPath: file.newPath,\n\t\t\t\titem: file.itemName,\n\t\t\t\tfile: { type: file.fileType, path: file.newPath },\n\t\t\t\tgetItemPath: ({ item, file }) => {\n\t\t\t\t\tconst fileType = normalizeItemTypeForPath(file.type);\n\t\t\t\t\t// there are two types of paths\n\t\t\t\t\t// <type> and <type>/<name>\n\t\t\t\t\tfor (const [key, value] of Object.entries(itemPaths)) {\n\t\t\t\t\t\t// <type>/<name>\n\t\t\t\t\t\tif (key === `${fileType}/${item}`) return value;\n\n\t\t\t\t\t\t// <type>\n\t\t\t\t\t\tif (key === fileType) return value;\n\t\t\t\t\t}\n\t\t\t\t\t// by now we should have already got all the necessary paths from the user\n\t\t\t\t\tthrow new Unreachable();\n\t\t\t\t},\n\t\t\t})) ?? file.content;\n\t}\n\n\treturn ok({\n\t\tneededDependencies: {\n\t\t\tdependencies: Array.from(neededDependencies.values()),\n\t\t\tdevDependencies: Array.from(neededDevDependencies.values()),\n\t\t},\n\t\tneededEnvVars,\n\t\tneededFiles: neededFiles.filter((file) => {\n\t\t\tif ('oldContent' in file) {\n\t\t\t\treturn file.oldContent !== file.content;\n\t\t\t}\n\t\t\treturn true;\n\t\t}),\n\t});\n}\n\nexport async function updateFiles({\n\tfiles,\n\toptions,\n}: {\n\tfiles: UpdatedFile[];\n\toptions: {\n\t\tcwd: string;\n\t\toverwrite: boolean;\n\t\texpand: boolean;\n\t\tmaxUnchanged: number;\n\t};\n}): Promise<Result<string[], JsrepoError>> {\n\tconst updatedFiles: string[] = [];\n\tfor (const file of files) {\n\t\tif (file.type === 'create' || options.overwrite) {\n\t\t\tconst writeResult = writeFileSync(file.filePath, file.content);\n\t\t\tif (writeResult.isErr()) return err(writeResult.error);\n\t\t\tupdatedFiles.push(path.relative(options.cwd, file.filePath));\n\t\t} else {\n\t\t\tconst changes = diffLines(file.oldContent, file.content);\n\t\t\tconst relativePath = path.relative(options.cwd, file.filePath);\n\t\t\tconst formattedDiff = formatDiff({\n\t\t\t\tfrom: `${file.registryUrl}/${file.itemName}`,\n\t\t\t\tto: relativePath,\n\t\t\t\tchanges,\n\t\t\t\texpand: options.expand,\n\t\t\t\tmaxUnchanged: options.maxUnchanged,\n\t\t\t\tprefix: () => `${VERTICAL_LINE}  `,\n\t\t\t\tonUnchanged: ({ from, to, prefix }) =>\n\t\t\t\t\t`${prefix?.() ?? ''}${pc.cyan(from)} → ${pc.gray(to)} ${pc.gray('(unchanged)')}\\n`,\n\t\t\t\tintro: ({ from, to, changes, prefix }) => {\n\t\t\t\t\tconst totalChanges = changes.filter((a) => a.added || a.removed).length;\n\n\t\t\t\t\treturn `${prefix?.() ?? ''}${pc.cyan(from)} → ${pc.gray(to)} (${totalChanges} change${\n\t\t\t\t\t\ttotalChanges === 1 ? '' : 's'\n\t\t\t\t\t})\\n${prefix?.() ?? ''}\\n`;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (formattedDiff.type === 'unchanged') continue;\n\t\t\tprocess.stdout.write(`${VERTICAL_LINE}\\n`);\n\t\t\tprocess.stdout.write(formattedDiff.diff);\n\n\t\t\tconst confirmResult = await select({\n\t\t\t\tmessage: 'Would you like to apply these changes?',\n\t\t\t\toptions: [\n\t\t\t\t\t{ label: 'Yes', value: 'yes' },\n\t\t\t\t\t{ label: 'No', value: 'no' },\n\t\t\t\t],\n\t\t\t\tinitialValue: 'yes',\n\t\t\t});\n\n\t\t\tif (isCancel(confirmResult)) {\n\t\t\t\tcancel('Canceled!');\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\n\t\t\tif (confirmResult === 'no') continue;\n\n\t\t\tconst writeResult = writeFileSync(file.filePath, file.content);\n\t\t\tif (writeResult.isErr()) return err(writeResult.error);\n\t\t\tupdatedFiles.push(path.relative(options.cwd, file.filePath));\n\t\t}\n\t}\n\treturn ok(updatedFiles);\n}\n\n/**\n * Normalizes the item type for the path. We strip the `registry:` prefix if it exists.\n * @param type\n * @returns\n */\nexport function normalizeItemTypeForPath(type: RegistryItemType) {\n\tif (type.startsWith('registry:')) return type.slice('registry:'.length);\n\treturn type;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/build.ts",
    "content": "import { isDynamicPattern } from 'fast-glob';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport type {\n\tConfig,\n\tRegistryConfig,\n\tRegistryFileRoles,\n\tRegistryItem,\n\tRegistryItemAdd,\n\tRegistryItemFile,\n\tRegistryItemFolderFile,\n\tRegistryItemType,\n\tRegistryMeta,\n\tRegistryPlugin,\n} from '@/utils/config';\nimport { isOptionalRole } from '@/utils/roles';\nimport type { AbsolutePath, ItemRelativePath, LooseAutocomplete } from '@/utils/types';\nimport {\n\tcreateWarningHandler,\n\tGlobPatternNoMatchWarning,\n\tLanguageNotFoundWarning,\n\ttype WarningHandler,\n} from '@/utils/warnings';\nimport {\n\tBuildError,\n\tDuplicateFileReferenceError,\n\tDuplicateItemNameError,\n\tFileNotFoundError,\n\tIllegalItemNameError,\n\tImportedFileNotResolvedError,\n\tInvalidDependencyError,\n\tInvalidRegistryDependencyError,\n\tNoFilesError,\n\tNoListedItemsError,\n\tSelfReferenceError,\n} from './errors';\nimport { existsSync, readdirSync, readFileSync, statSync } from './fs';\nimport { getGlobBaseDirectory, glob } from './glob';\nimport { parsePackageName } from './parse-package-name';\nimport { joinAbsolute, joinRelative, type NormalizedAbsolutePath, normalizeAbsolute } from './path';\nimport { endsWithOneOf } from './strings';\n\n/**\n * We won't warn about these file types when resolving dependencies.\n */\nconst DO_NOT_RESOLVE_EXTENSIONS = [\n\t// Images\n\t'.png',\n\t'.jpg',\n\t'.jpeg',\n\t'.gif',\n\t'.svg',\n\t'.webp',\n\t'.ico',\n\t'.bmp',\n\t'.tiff',\n\t// 3D models\n\t'.glb',\n\t'.gltf',\n\t'.obj',\n\t'.fbx',\n\t'.dae',\n\t// Fonts\n\t'.woff',\n\t'.woff2',\n\t'.ttf',\n\t'.eot',\n\t'.otf',\n\t// Media\n\t'.mp4',\n\t'.webm',\n\t'.mp3',\n\t'.wav',\n\t'.ogg',\n\t'.avi',\n\t// Archives\n\t'.zip',\n\t'.tar',\n\t'.gz',\n\t// Other binary\n\t'.pdf',\n\t'.exe',\n\t'.dll',\n\t'.so',\n\t'.dylib',\n] as const;\n\nexport const MANIFEST_FILE = 'registry.json';\n\nexport type BuildResult = RegistryMeta & {\n\tplugins?: {\n\t\tlanguages?: RegistryPlugin[];\n\t\tproviders?: RegistryPlugin[];\n\t\ttransforms?: RegistryPlugin[];\n\t};\n\titems: ResolvedItem[];\n\tdefaultPaths?: Record<string, string>;\n};\n\nexport type Ecosystem = LooseAutocomplete<'js'>;\n\nexport type ResolvedItem = {\n\tname: string;\n\ttitle: string | undefined;\n\ttype: RegistryItemType;\n\tdescription: string | undefined;\n\tfiles: RegistryFile[];\n\t/** Dependencies to other items in the registry */\n\tregistryDependencies: string[] | undefined;\n\t/** Dependencies to code located in a remote repository to be installed later with a package manager. */\n\tdependencies: RemoteDependency[] | undefined;\n\tdevDependencies: RemoteDependency[] | undefined;\n\tadd: RegistryItemAdd;\n\tenvVars: Record<string, string> | undefined;\n\tcategories: string[] | undefined;\n\tmeta: Record<string, string> | undefined;\n};\n\nexport type RegistryFile = {\n\t/** The absolute path to the file. This can be immediately used to read the file. */\n\tabsolutePath: AbsolutePath;\n\t/** Path of the file relative to the parent item. */\n\tpath: ItemRelativePath;\n\tcontent: string;\n\ttype: RegistryItemType;\n\trole: RegistryFileRoles;\n\t/** Templates for resolving imports when adding items to users projects. This way users can add their items anywhere and things will just work. */\n\t_imports_: UnresolvedImport[];\n\ttarget?: string;\n\tregistryDependencies: string[] | undefined;\n\tdependencies: RemoteDependency[] | undefined;\n\tdevDependencies: RemoteDependency[] | undefined;\n};\n\nexport const UnresolvedImportSchema = z.object({\n\timport: z.string(),\n\titem: z.string(),\n\tfile: z.object({\n\t\ttype: z.string(),\n\t\tpath: z.string().transform((v) => v as ItemRelativePath),\n\t}),\n\tmeta: z.record(z.string(), z.unknown()),\n});\n\n/** All the information that a client would need about the registry to correct imports in a users project. */\nexport type UnresolvedImport = {\n\t/** The literal import string to be transformed on the client */\n\timport: string;\n\titem: string;\n\tfile: { type: RegistryItemType; path: ItemRelativePath };\n\t/** Additional properties to help resolve the import on the client */\n\tmeta: Record<string, unknown>;\n};\n\nexport type RemoteDependency = {\n\t/** This helps us determine how to install the dependency later */\n\tecosystem: Ecosystem;\n\tname: string;\n\tversion?: string;\n};\n\nexport type LocalDependency = {\n\t/** The file name of the dependency. */\n\tfileName: AbsolutePath;\n\t/** The import string used to reference the dependency in the file. */\n\timport: string;\n\t/** A function that will resolve a template that can be used to transform the import on the client */\n\tcreateTemplate: (\n\t\tresolvedDependency: ResolvedFile\n\t) => Promise<Record<string, unknown>> | Record<string, unknown>;\n};\n\nexport type Dependency = RemoteDependency | LocalDependency;\n\nexport function isLocalDependency(dependency: Dependency): dependency is LocalDependency {\n\treturn 'fileName' in dependency;\n}\n\nexport function isRemoteDependency(dependency: Dependency): dependency is RemoteDependency {\n\treturn 'ecosystem' in dependency;\n}\n\ntype ParentItem = { name: string; type: RegistryItemType; registryName: string };\n\n/**\n * A file that has not been resolved yet. This should not be a folder!\n */\nexport type UnresolvedFile = {\n\t/**\n\t * The absolute path to the file. This can be immediately used to read the file.\n\t */\n\tabsolutePath: AbsolutePath;\n\t/**\n\t * Path of the file relative to the parent item.\n\t */\n\tpath: ItemRelativePath;\n\tparent: ParentItem;\n\ttype: RegistryItemType | undefined;\n\trole: RegistryFileRoles | undefined;\n\tdependencyResolution: 'auto' | 'manual';\n\tregistryDependencies: string[] | undefined;\n\tdependencies: (string | RemoteDependency)[] | undefined;\n\tdevDependencies: (string | RemoteDependency)[] | undefined;\n\ttarget: string | undefined;\n};\n\nexport type ResolvedFile = {\n\t/**\n\t * The absolute path to the file. This can be immediately used to read the file.\n\t */\n\tabsolutePath: AbsolutePath;\n\t/**\n\t * Path of the file relative to the parent item.\n\t */\n\tpath: ItemRelativePath;\n\tparent: ParentItem;\n\ttype: RegistryItemType;\n\trole: RegistryFileRoles;\n\tdependencyResolution: 'auto' | 'manual';\n\tlocalDependencies: LocalDependency[];\n\tdependencies: RemoteDependency[];\n\tdevDependencies: RemoteDependency[];\n\tmanualDependencies: {\n\t\tregistryDependencies: string[] | undefined;\n\t\tdependencies: RemoteDependency[];\n\t\tdevDependencies: RemoteDependency[];\n\t};\n\tcontent: string;\n\ttarget: string | undefined;\n};\n\nexport type ResolvedFiles = Map<NormalizedAbsolutePath, ResolvedFile>;\n\n/**\n * Validates the registry config\n * @param registry\n */\nexport async function validateRegistryConfig(\n\tregistry: RegistryConfig\n): Promise<Result<void, BuildError>> {\n\tconst noListedItems = !registry.items.some((item) => !item.add || item.add === 'when-added');\n\tif (noListedItems) return err(new NoListedItemsError({ registryName: registry.name }));\n\n\tconst items = new Set<string>();\n\tfor (const item of registry.items) {\n\t\t// cannot have duplicate item names\n\t\tif (items.has(item.name))\n\t\t\treturn err(\n\t\t\t\tnew DuplicateItemNameError({ name: item.name, registryName: registry.name })\n\t\t\t);\n\n\t\titems.add(item.name);\n\n\t\t// cannot reference itself\n\t\tif (item.registryDependencies?.includes(item.name)) {\n\t\t\treturn err(new SelfReferenceError({ name: item.name, registryName: registry.name }));\n\t\t}\n\n\t\tif (item.files.length === 0)\n\t\t\treturn err(new NoFilesError({ name: item.name, registryName: registry.name }));\n\n\t\tif (item.name === 'registry')\n\t\t\treturn err(new IllegalItemNameError({ name: item.name, registryName: registry.name }));\n\t}\n\n\t// check to make sure that any registry dependencies are valid\n\tfor (const item of registry.items) {\n\t\tfor (const dependency of item.registryDependencies ?? []) {\n\t\t\tif (!items.has(dependency)) {\n\t\t\t\treturn err(\n\t\t\t\t\tnew InvalidRegistryDependencyError({\n\t\t\t\t\t\tdependency,\n\t\t\t\t\t\titem: item.name,\n\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ok();\n}\n\ntype ExpandedRegistryItem = Omit<RegistryItem, 'files'> & {\n\tfiles: UnresolvedFile[];\n};\n\n/**\n * Resolve any glob patterns or nested files within registry items and return a list of registry items with a flattened array of files.\n *\n * @param registry\n * @param param1\n */\nexport async function expandRegistryItems(\n\tregistry: RegistryConfig,\n\t{ cwd, warn }: { cwd: AbsolutePath; warn: WarningHandler }\n): Promise<Result<ExpandedRegistryItem[], BuildError>> {\n\tconst expandedRegistryItems: ExpandedRegistryItem[] = [];\n\n\tfor (const item of registry.items) {\n\t\tconst expandedFilesResult = await expandItemFiles(item.files, {\n\t\t\tcwd,\n\t\t\titem,\n\t\t\tregistry,\n\t\t\twarn,\n\t\t});\n\t\tif (expandedFilesResult.isErr()) return err(expandedFilesResult.error);\n\t\tconst expandedFiles = expandedFilesResult.value;\n\t\tif (expandedFiles.length === 0) {\n\t\t\treturn err(new NoFilesError({ name: item.name, registryName: registry.name }));\n\t\t}\n\t\texpandedRegistryItems.push({\n\t\t\t...item,\n\t\t\tfiles: expandedFiles,\n\t\t});\n\t}\n\n\treturn ok(expandedRegistryItems);\n}\n\nasync function expandItemFiles(\n\tfiles: RegistryItemFile[],\n\t{\n\t\tcwd,\n\t\titem,\n\t\tregistry,\n\t\twarn,\n\t}: { cwd: AbsolutePath; item: RegistryItem; registry: RegistryConfig; warn: WarningHandler }\n): Promise<Result<UnresolvedFile[], BuildError>> {\n\tconst unresolvedFiles: UnresolvedFile[] = [];\n\n\tfor (const file of files) {\n\t\tconst absolutePath = joinAbsolute(cwd, file.path);\n\t\tif (isDynamicPattern(absolutePath)) {\n\t\t\tif (!file.files) {\n\t\t\t\tconst entriesResult = await glob(absolutePath, {\n\t\t\t\t\tabsolute: true,\n\t\t\t\t\tdot: true,\n\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t});\n\t\t\t\tif (entriesResult.isErr()) return err(entriesResult.error);\n\t\t\t\tconst entries = entriesResult.value;\n\t\t\t\tif (entries.length === 0) {\n\t\t\t\t\twarn(\n\t\t\t\t\t\tnew GlobPatternNoMatchWarning({ itemName: item.name, pattern: file.path })\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// This preserves subdirectory structure when glob patterns match files in subdirectories\n\t\t\t\tconst globBaseDir = joinAbsolute(\n\t\t\t\t\tcwd,\n\t\t\t\t\tgetGlobBaseDirectory(file.path, { cwd, dot: true })\n\t\t\t\t);\n\t\t\t\tconst files = entries.map((e) => {\n\t\t\t\t\tconst relativePath = path.relative(globBaseDir, e);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...file,\n\t\t\t\t\t\tpath: (relativePath || path.basename(e)) as ItemRelativePath,\n\t\t\t\t\t\tabsolutePath: e as AbsolutePath,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\tunresolvedFiles.push(\n\t\t\t\t\t...files.map((f) => ({\n\t\t\t\t\t\tabsolutePath: f.absolutePath,\n\t\t\t\t\t\tpath: f.path,\n\t\t\t\t\t\ttype: f.type ?? item.type,\n\t\t\t\t\t\trole: f.role ?? 'file',\n\t\t\t\t\t\ttarget: f.target,\n\t\t\t\t\t\tdependencyResolution:\n\t\t\t\t\t\t\tf.dependencyResolution ?? item.dependencyResolution ?? 'auto',\n\t\t\t\t\t\tparent: {\n\t\t\t\t\t\t\tname: item.name,\n\t\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tregistryDependencies: f.registryDependencies,\n\t\t\t\t\t\tdependencies: f.dependencies,\n\t\t\t\t\t\tdevDependencies: f.devDependencies,\n\t\t\t\t\t}))\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst subFilesResult = await expandItemFolderFiles(file.files, {\n\t\t\t\tparent: {\n\t\t\t\t\ttype: file.type ?? item.type,\n\t\t\t\t\trole: file.role ?? 'file',\n\t\t\t\t\ttarget: file.target,\n\t\t\t\t\tdependencyResolution: item.dependencyResolution ?? 'auto',\n\t\t\t\t\tparentItem: {\n\t\t\t\t\t\tname: item.name,\n\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t},\n\t\t\t\t\tpath: file.path as ItemRelativePath,\n\t\t\t\t\tabsolutePath: absolutePath,\n\t\t\t\t},\n\t\t\t\tcwd,\n\t\t\t\titem,\n\t\t\t\tregistry,\n\t\t\t\twarn,\n\t\t\t});\n\t\t\tif (subFilesResult.isErr()) return err(subFilesResult.error);\n\t\t\tunresolvedFiles.push(...subFilesResult.value);\n\t\t} else {\n\t\t\tif (!existsSync(absolutePath)) {\n\t\t\t\treturn err(\n\t\t\t\t\tnew FileNotFoundError({\n\t\t\t\t\t\tpath: absolutePath,\n\t\t\t\t\t\tparent: {\n\t\t\t\t\t\t\tname: item.name,\n\t\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst statResult = statSync(absolutePath)._unsafeUnwrap();\n\t\t\tif (statResult.isFile()) {\n\t\t\t\tunresolvedFiles.push({\n\t\t\t\t\tabsolutePath,\n\t\t\t\t\tpath: path.basename(file.path) as ItemRelativePath,\n\t\t\t\t\ttype: file.type ?? item.type,\n\t\t\t\t\trole: file.role ?? 'file',\n\t\t\t\t\ttarget: file.target,\n\t\t\t\t\tdependencyResolution:\n\t\t\t\t\t\tfile.dependencyResolution ?? item.dependencyResolution ?? 'auto',\n\t\t\t\t\tparent: {\n\t\t\t\t\t\tname: item.name,\n\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t},\n\t\t\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\t\t\tdependencies: file.dependencies,\n\t\t\t\t\tdevDependencies: file.devDependencies,\n\t\t\t\t});\n\t\t\t\t// only folders can have sub files\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet files: RegistryItemFolderFile[];\n\t\t\tif (file.files) {\n\t\t\t\tfiles = file.files;\n\t\t\t} else {\n\t\t\t\tconst readdirResult = readdirSync(absolutePath);\n\t\t\t\tif (readdirResult.isErr())\n\t\t\t\t\treturn err(\n\t\t\t\t\t\tnew BuildError(\n\t\t\t\t\t\t\t`Error reading directory: ${pc.bold(absolutePath)} referenced by ${pc.bold(item.name)}`,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t\t\t\tsuggestion: 'Please ensure the directory exists and is readable.',\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\tfiles = readdirResult.value.map((f) => ({\n\t\t\t\t\tpath: f,\n\t\t\t\t}));\n\t\t\t}\n\n\t\t\tconst subFilesResult = await expandItemFolderFiles(files ?? [], {\n\t\t\t\tparent: {\n\t\t\t\t\ttype: file.type ?? item.type,\n\t\t\t\t\trole: file.role ?? 'file',\n\t\t\t\t\ttarget: file.target,\n\t\t\t\t\tdependencyResolution: item.dependencyResolution ?? 'auto',\n\t\t\t\t\tparentItem: {\n\t\t\t\t\t\tname: item.name,\n\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\t},\n\t\t\t\t\t// we use the basename of the folder\n\t\t\t\t\tpath: path.basename(file.path) as ItemRelativePath,\n\t\t\t\t\tabsolutePath: absolutePath,\n\t\t\t\t},\n\t\t\t\tcwd,\n\t\t\t\titem,\n\t\t\t\tregistry,\n\t\t\t\twarn,\n\t\t\t});\n\t\t\tif (subFilesResult.isErr()) return err(subFilesResult.error);\n\t\t\tunresolvedFiles.push(...subFilesResult.value);\n\t\t}\n\t}\n\n\treturn ok(unresolvedFiles);\n}\n\nasync function expandItemFolderFiles(\n\tfiles: RegistryItemFolderFile[],\n\t{\n\t\tcwd,\n\t\titem,\n\t\tregistry,\n\t\tparent,\n\t\twarn,\n\t}: {\n\t\tparent: {\n\t\t\ttype: RegistryItemType;\n\t\t\trole: RegistryFileRoles;\n\t\t\ttarget: string | undefined;\n\t\t\tdependencyResolution: 'auto' | 'manual';\n\t\t\tparentItem: ParentItem;\n\t\t\tpath: ItemRelativePath;\n\t\t\tabsolutePath: AbsolutePath;\n\t\t};\n\t\tcwd: AbsolutePath;\n\t\titem: RegistryItem;\n\t\tregistry: RegistryConfig;\n\t\twarn: WarningHandler;\n\t}\n): Promise<Result<UnresolvedFile[], BuildError>> {\n\tconst unresolvedFiles: UnresolvedFile[] = [];\n\tfor (const f of files) {\n\t\tconst absolutePath = joinAbsolute(parent.absolutePath, f.path);\n\t\tif (isDynamicPattern(absolutePath)) {\n\t\t\tif (!f.files) {\n\t\t\t\tconst entriesResult = await glob(absolutePath, {\n\t\t\t\t\tabsolute: true,\n\t\t\t\t\tdot: true,\n\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t});\n\t\t\t\tif (entriesResult.isErr()) return err(entriesResult.error);\n\t\t\t\tconst entries = entriesResult.value;\n\t\t\t\tif (entries.length === 0) {\n\t\t\t\t\twarn(new GlobPatternNoMatchWarning({ itemName: item.name, pattern: f.path }));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// This preserves subdirectory structure when glob patterns match files in subdirectories\n\t\t\t\tconst globBaseDir = joinAbsolute(\n\t\t\t\t\tparent.absolutePath,\n\t\t\t\t\tgetGlobBaseDirectory(f.path, { cwd: parent.absolutePath, dot: true })\n\t\t\t\t);\n\t\t\t\tconst files = entries.map((e) => {\n\t\t\t\t\tconst relativePath = path.relative(globBaseDir, e);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...f,\n\t\t\t\t\t\tpath: (relativePath || path.basename(e)) as ItemRelativePath,\n\t\t\t\t\t\tabsolutePath: e as AbsolutePath,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t\t\tunresolvedFiles.push(\n\t\t\t\t\t...files.map((f) => ({\n\t\t\t\t\t\tabsolutePath: f.absolutePath,\n\t\t\t\t\t\tpath: path.join(parent.path, f.path) as ItemRelativePath,\n\t\t\t\t\t\ttype: parent.type,\n\t\t\t\t\t\trole: f.role ?? parent.role,\n\t\t\t\t\t\ttarget: parent.target ? path.join(parent.target, f.path) : undefined,\n\t\t\t\t\t\tdependencyResolution: f.dependencyResolution ?? parent.dependencyResolution,\n\t\t\t\t\t\tparent: parent.parentItem,\n\t\t\t\t\t\tregistryDependencies: f.registryDependencies,\n\t\t\t\t\t\tdependencies: f.dependencies,\n\t\t\t\t\t\tdevDependencies: f.devDependencies,\n\t\t\t\t\t}))\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst subFilesResult = await expandItemFolderFiles(f.files, {\n\t\t\t\tparent: {\n\t\t\t\t\tparentItem: parent.parentItem,\n\t\t\t\t\ttarget: parent.target ? path.join(parent.target, f.path) : undefined,\n\t\t\t\t\ttype: parent.type,\n\t\t\t\t\trole: f.role ?? parent.role,\n\t\t\t\t\tdependencyResolution: parent.dependencyResolution,\n\t\t\t\t\tpath: joinRelative(parent.path, f.path),\n\t\t\t\t\tabsolutePath: joinAbsolute(parent.absolutePath, f.path),\n\t\t\t\t},\n\t\t\t\tcwd,\n\t\t\t\titem,\n\t\t\t\tregistry,\n\t\t\t\twarn,\n\t\t\t});\n\t\t\tif (subFilesResult.isErr()) return err(subFilesResult.error);\n\t\t\tunresolvedFiles.push(...subFilesResult.value);\n\t\t} else {\n\t\t\tif (!existsSync(absolutePath)) {\n\t\t\t\treturn err(\n\t\t\t\t\tnew FileNotFoundError({\n\t\t\t\t\t\tpath: absolutePath,\n\t\t\t\t\t\tparent: {\n\t\t\t\t\t\t\tname: parent.parentItem.name,\n\t\t\t\t\t\t\ttype: parent.parentItem.type,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tregistryName: parent.parentItem.registryName,\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst isFile = statSync(absolutePath)._unsafeUnwrap().isFile();\n\t\t\tif (isFile) {\n\t\t\t\tunresolvedFiles.push({\n\t\t\t\t\tabsolutePath,\n\t\t\t\t\tpath: path.join(parent.path, f.path) as ItemRelativePath,\n\t\t\t\t\ttype: parent.type,\n\t\t\t\t\trole: f.role ?? parent.role,\n\t\t\t\t\ttarget: parent.target ? path.join(parent.target, f.path) : undefined,\n\t\t\t\t\tdependencyResolution: f.dependencyResolution ?? parent.dependencyResolution,\n\t\t\t\t\tparent: parent.parentItem,\n\t\t\t\t\tregistryDependencies: f.registryDependencies,\n\t\t\t\t\tdependencies: f.dependencies,\n\t\t\t\t\tdevDependencies: f.devDependencies,\n\t\t\t\t});\n\t\t\t\t// only folders can have sub files\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet files: RegistryItemFolderFile[];\n\t\t\tif (f.files) {\n\t\t\t\tfiles = f.files;\n\t\t\t} else {\n\t\t\t\tconst readdirResult = readdirSync(absolutePath);\n\t\t\t\tif (readdirResult.isErr())\n\t\t\t\t\treturn err(\n\t\t\t\t\t\tnew BuildError(\n\t\t\t\t\t\t\t`Error reading directory: ${pc.bold(absolutePath)} referenced by ${pc.bold(\n\t\t\t\t\t\t\t\tparent.parentItem.name\n\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tregistryName: parent.parentItem.registryName,\n\t\t\t\t\t\t\t\tsuggestion: 'Please ensure the directory exists and is readable.',\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\tfiles = readdirResult.value.map((f) => ({\n\t\t\t\t\tpath: f,\n\t\t\t\t}));\n\t\t\t}\n\n\t\t\tconst subFilesResult = await expandItemFolderFiles(files ?? [], {\n\t\t\t\tparent: {\n\t\t\t\t\tparentItem: parent.parentItem,\n\t\t\t\t\ttarget: parent.target ? path.join(parent.target, f.path) : undefined,\n\t\t\t\t\ttype: parent.type,\n\t\t\t\t\trole: f.role ?? parent.role,\n\t\t\t\t\tdependencyResolution: parent.dependencyResolution,\n\t\t\t\t\tpath: joinRelative(parent.path, f.path),\n\t\t\t\t\tabsolutePath: joinAbsolute(parent.absolutePath, f.path),\n\t\t\t\t},\n\t\t\t\tcwd,\n\t\t\t\titem,\n\t\t\t\tregistry,\n\t\t\t\twarn,\n\t\t\t});\n\t\t\tif (subFilesResult.isErr()) return err(subFilesResult.error);\n\t\t\tunresolvedFiles.push(...subFilesResult.value);\n\t\t}\n\t}\n\treturn ok(unresolvedFiles);\n}\n\nexport async function buildRegistry(\n\tregistry: RegistryConfig,\n\t{ options, config }: { options: { cwd: AbsolutePath }; config: Config }\n): Promise<Result<BuildResult, BuildError>> {\n\tconst result = await validateRegistryConfig(registry);\n\tif (result.isErr()) return err(result.error);\n\n\tconst warn = createWarningHandler(config.build?.onwarn);\n\n\tconst expandedRegistryItemsResult = await expandRegistryItems(registry, {\n\t\tcwd: options.cwd,\n\t\twarn,\n\t});\n\tif (expandedRegistryItemsResult.isErr()) return err(expandedRegistryItemsResult.error);\n\tconst expandedRegistryItems = expandedRegistryItemsResult.value;\n\n\tconst resolvedFilesResult = await resolveFiles(\n\t\texpandedRegistryItems.flatMap((item) => item.files),\n\t\t{\n\t\t\tcwd: options.cwd,\n\t\t\tconfig,\n\t\t\tregistry,\n\t\t\twarn,\n\t\t}\n\t);\n\tif (resolvedFilesResult.isErr()) return err(resolvedFilesResult.error);\n\tconst resolvedFiles = resolvedFilesResult.value;\n\n\tconst resolvedItemsResult = await resolveRegistryItems(expandedRegistryItems, {\n\t\tcwd: options.cwd,\n\t\tconfig,\n\t\tresolvedFiles,\n\t\tregistry,\n\t});\n\tif (resolvedItemsResult.isErr()) return err(resolvedItemsResult.error);\n\tconst resolvedItems = resolvedItemsResult.value;\n\n\treturn ok({\n\t\t...registry,\n\t\titems: Array.from(resolvedItems.values()),\n\t\tdefaultPaths: registry.defaultPaths as Record<string, string> | undefined,\n\t});\n}\n\nexport async function resolveFiles(\n\tfiles: UnresolvedFile[],\n\t{\n\t\tcwd,\n\t\tresolvedFiles = new Map<NormalizedAbsolutePath, ResolvedFile>(),\n\t\tconfig,\n\t\tregistry,\n\t\twarn,\n\t}: {\n\t\tcwd: AbsolutePath;\n\t\tconfig: Config;\n\t\tregistry: RegistryConfig | { name: string; excludeDeps?: string[] };\n\t\tresolvedFiles?: ResolvedFiles;\n\t\twarn: WarningHandler;\n\t}\n): Promise<Result<ResolvedFiles, BuildError>> {\n\tfor (const file of files) {\n\t\tconst normalizedPath = normalizeAbsolute(file.absolutePath);\n\t\tconst previouslyResolvedFile = resolvedFiles.get(normalizedPath);\n\t\tif (previouslyResolvedFile) {\n\t\t\treturn err(\n\t\t\t\tnew DuplicateFileReferenceError({\n\t\t\t\t\tpath: file.path,\n\t\t\t\t\tparent: previouslyResolvedFile.parent,\n\t\t\t\t\tduplicateParent: file.parent,\n\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\tif (!existsSync(file.absolutePath)) {\n\t\t\treturn err(\n\t\t\t\tnew FileNotFoundError({\n\t\t\t\t\tpath: file.absolutePath,\n\t\t\t\t\tparent: file.parent,\n\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\tconst resolveResult = await resolveFile(file, { cwd, config, registry, warn });\n\t\tif (resolveResult.isErr()) return err(resolveResult.error);\n\t\tresolvedFiles.set(normalizedPath, resolveResult.value);\n\t}\n\treturn ok(resolvedFiles);\n}\n\nasync function resolveFile(\n\tfile: UnresolvedFile,\n\t{\n\t\tcwd,\n\t\tconfig,\n\t\tregistry,\n\t\twarn,\n\t}: {\n\t\tcwd: AbsolutePath;\n\t\tconfig: Config;\n\t\tregistry: { name: string; excludeDeps?: string[] };\n\t\twarn: WarningHandler;\n\t}\n): Promise<Result<ResolvedFile, BuildError>> {\n\tconst contentResult = readFileSync(file.absolutePath);\n\tif (contentResult.isErr())\n\t\treturn err(\n\t\t\tnew BuildError(\n\t\t\t\t`Failed to read file ${pc.bold(file.absolutePath)} referenced by ${pc.bold(file.parent.name)}`,\n\t\t\t\t{\n\t\t\t\t\tregistryName: registry.name,\n\t\t\t\t\tsuggestion: 'Please ensure the file exists and is readable.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\n\t// run any prebuild transforms on the content\n\tlet content = contentResult.value;\n\tfor (const transform of config.build.transforms ?? []) {\n\t\tconst { content: newContent } = await transform.transform(content, {\n\t\t\tcwd,\n\t\t\tfile,\n\t\t});\n\t\tcontent = newContent;\n\t}\n\n\tconst manualDependencies = toRemoteDependencies(file.dependencies ?? [], {\n\t\tregistryName: registry.name,\n\t\titemName: file.parent.name,\n\t});\n\tif (manualDependencies.isErr()) return err(manualDependencies.error);\n\n\tconst manualDevDependencies = toRemoteDependencies(file.devDependencies ?? [], {\n\t\tregistryName: registry.name,\n\t\titemName: file.parent.name,\n\t});\n\tif (manualDevDependencies.isErr()) return err(manualDevDependencies.error);\n\n\tlet localDependencies: LocalDependency[] = [];\n\tlet dependencies: RemoteDependency[] = [];\n\tlet devDependencies: RemoteDependency[] = [];\n\tif (file.dependencyResolution === 'auto') {\n\t\tconst language = config.languages.find((language) =>\n\t\t\tlanguage.canResolveDependencies(file.path)\n\t\t);\n\t\tif (language) {\n\t\t\tconst {\n\t\t\t\tlocalDependencies: localDeps,\n\t\t\t\tdependencies: deps,\n\t\t\t\tdevDependencies: devDeps,\n\t\t\t} = await language.resolveDependencies(content, {\n\t\t\t\tcwd,\n\t\t\t\tfileName: file.absolutePath,\n\t\t\t\texcludeDeps: registry.excludeDeps ?? [],\n\t\t\t\twarn,\n\t\t\t});\n\t\t\tlocalDependencies = localDeps;\n\t\t\tdependencies = deps;\n\t\t\tdevDependencies = devDeps;\n\t\t} else {\n\t\t\t// only log a warning if the file is not a binary asset file\n\t\t\tif (!endsWithOneOf(file.path, DO_NOT_RESOLVE_EXTENSIONS)) {\n\t\t\t\twarn(new LanguageNotFoundWarning({ path: file.absolutePath }));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ok({\n\t\tabsolutePath: file.absolutePath,\n\t\tpath: file.path,\n\t\t// inherit the type from the parent item\n\t\ttype: file.type ?? file.parent.type,\n\t\trole: file.role ?? 'file',\n\t\tparent: file.parent,\n\t\ttarget: file.target,\n\t\tdependencyResolution: file.dependencyResolution,\n\t\tlocalDependencies,\n\t\tdependencies,\n\t\tdevDependencies,\n\t\tmanualDependencies: {\n\t\t\tregistryDependencies: file.registryDependencies,\n\t\t\tdependencies: manualDependencies.value,\n\t\t\tdevDependencies: manualDevDependencies.value,\n\t\t},\n\t\tcontent,\n\t});\n}\n\nexport async function resolveRegistryItems(\n\titems: ExpandedRegistryItem[],\n\t{\n\t\tcwd,\n\t\tconfig,\n\t\tresolvedItems = new Map<string, ResolvedItem>(),\n\t\tresolvedFiles,\n\t\tregistry,\n\t}: {\n\t\tcwd: AbsolutePath;\n\t\tconfig: Config;\n\t\tresolvedItems?: Map<string, ResolvedItem>;\n\t\tresolvedFiles: ResolvedFiles;\n\t\tregistry: { name: string };\n\t}\n): Promise<Result<Map<string, ResolvedItem>, BuildError>> {\n\tfor (const item of items) {\n\t\tconst resolvedItem = await resolveRegistryItem(item, {\n\t\t\tcwd,\n\t\t\tconfig,\n\t\t\tresolvedItems,\n\t\t\tresolvedFiles,\n\t\t\tregistry,\n\t\t});\n\t\tif (resolvedItem.isErr()) return err(resolvedItem.error);\n\t\tresolvedItems.set(item.name, resolvedItem.value);\n\t}\n\treturn ok(resolvedItems);\n}\n\nexport type DependencyKey = `${Ecosystem}:${string}@${string}`;\n\nfunction toDependencyKey(dep: RemoteDependency): DependencyKey {\n\treturn `${dep.ecosystem}:${dep.name}@${dep.version}`;\n}\n\nasync function resolveRemoteDependencies(\n\tdeps: RemoteDependency[],\n\t{\n\t\tcwd,\n\t\tconfig,\n\t\tregistryName,\n\t\titemName,\n\t}: { cwd: AbsolutePath; config: Config; registryName: string; itemName: string }\n): Promise<Result<RemoteDependency[], BuildError>> {\n\tconst resolver = config.build?.remoteDependencyResolver;\n\tif (!resolver) return ok(deps);\n\n\tconst resolved: RemoteDependency[] = [];\n\tfor (const dep of deps) {\n\t\ttry {\n\t\t\tresolved.push(await resolver(dep, { cwd }));\n\t\t} catch {\n\t\t\treturn err(\n\t\t\t\tnew BuildError(\n\t\t\t\t\t`Failed to resolve remote dependency ${pc.bold(dep.name)} referenced by ${pc.bold(itemName)}.`,\n\t\t\t\t\t{\n\t\t\t\t\t\tregistryName,\n\t\t\t\t\t\tsuggestion: `Please ensure build.remoteDependencyResolver can resolve ${dep.name}${dep.version ? `@${dep.version}` : ''}.`,\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t}\n\n\treturn ok(resolved);\n}\n\nexport async function resolveRegistryItem(\n\titem: ExpandedRegistryItem,\n\t{\n\t\tcwd,\n\t\tconfig,\n\t\tresolvedItems,\n\t\tresolvedFiles,\n\t\tregistry,\n\t}: {\n\t\tcwd: AbsolutePath;\n\t\tconfig: Config;\n\t\tresolvedItems: Map<string, ResolvedItem>;\n\t\tresolvedFiles: ResolvedFiles;\n\t\tregistry: { name: string };\n\t}\n): Promise<Result<ResolvedItem, BuildError>> {\n\tconst preResolvedItem = resolvedItems.get(item.name);\n\tif (preResolvedItem) return ok(preResolvedItem);\n\n\tconst files: RegistryFile[] = [];\n\tconst registryDependencies = new Set<string>(item.registryDependencies ?? []);\n\n\tconst dependenciesResult = toRemoteDependencies(item.dependencies ?? [], {\n\t\tregistryName: registry.name,\n\t\titemName: item.name,\n\t});\n\tif (dependenciesResult.isErr()) return err(dependenciesResult.error);\n\tconst resolvedDependenciesResult = await resolveRemoteDependencies(dependenciesResult.value, {\n\t\tcwd,\n\t\tconfig,\n\t\tregistryName: registry.name,\n\t\titemName: item.name,\n\t});\n\tif (resolvedDependenciesResult.isErr()) return err(resolvedDependenciesResult.error);\n\tconst dependencies = new Map<DependencyKey, RemoteDependency>(\n\t\tresolvedDependenciesResult.value.map((dep) => [toDependencyKey(dep), dep])\n\t);\n\n\tconst devDependenciesResult = toRemoteDependencies(item.devDependencies ?? [], {\n\t\tregistryName: registry.name,\n\t\titemName: item.name,\n\t});\n\tif (devDependenciesResult.isErr()) return err(devDependenciesResult.error);\n\tconst resolvedDevDependenciesResult = await resolveRemoteDependencies(\n\t\tdevDependenciesResult.value,\n\t\t{\n\t\t\tcwd,\n\t\t\tconfig,\n\t\t\tregistryName: registry.name,\n\t\t\titemName: item.name,\n\t\t}\n\t);\n\tif (resolvedDevDependenciesResult.isErr()) return err(resolvedDevDependenciesResult.error);\n\tconst devDependencies = new Map<DependencyKey, RemoteDependency>(\n\t\tresolvedDevDependenciesResult.value.map((dep) => [toDependencyKey(dep), dep])\n\t);\n\n\tconst itemFiles = Array.from(resolvedFiles.values()).filter(\n\t\t(file) => file.parent.name === item.name\n\t);\n\n\tfor (const resolvedFile of itemFiles) {\n\t\tconst resolvedResult = await resolveFileDependencies(resolvedFile, {\n\t\t\titem,\n\t\t\tconfig,\n\t\t\tresolvedFiles,\n\t\t\tcwd,\n\t\t});\n\t\tif (resolvedResult.isErr()) return err(resolvedResult.error);\n\t\tconst {\n\t\t\tfile,\n\t\t\tdependencies: deps,\n\t\t\tdevDependencies: devDeps,\n\t\t\tregistryDependencies: regDeps,\n\t\t} = resolvedResult.value;\n\n\t\tfiles.push(file);\n\n\t\tfor (const dep of deps) {\n\t\t\tdependencies.set(toDependencyKey(dep), dep);\n\t\t}\n\t\tfor (const dep of devDeps) {\n\t\t\tdevDependencies.set(toDependencyKey(dep), dep);\n\t\t}\n\t\tfor (const dep of regDeps) {\n\t\t\tregistryDependencies.add(dep);\n\t\t}\n\t}\n\n\treturn ok({\n\t\tname: item.name,\n\t\ttitle: item.title,\n\t\ttype: item.type,\n\t\tdescription: item.description,\n\t\tfiles,\n\t\tregistryDependencies: Array.from(registryDependencies),\n\t\tdependencies: Array.from(dependencies.values()),\n\t\tdevDependencies: Array.from(devDependencies.values()),\n\t\tadd: item.add ?? 'when-added',\n\t\tenvVars: item.envVars,\n\t\tcategories: item.categories,\n\t\tmeta: item.meta,\n\t});\n}\n\nasync function resolveFileDependencies(\n\tresolvedFile: ResolvedFile,\n\t{\n\t\tresolvedFiles,\n\t\titem,\n\t\tconfig,\n\t\tcwd,\n\t}: {\n\t\tresolvedFiles: ResolvedFiles;\n\t\titem: ExpandedRegistryItem;\n\t\tconfig: Config;\n\t\tcwd: AbsolutePath;\n\t}\n): Promise<\n\tResult<\n\t\t{\n\t\t\tfile: RegistryFile;\n\t\t\tdependencies: RemoteDependency[];\n\t\t\tdevDependencies: RemoteDependency[];\n\t\t\tregistryDependencies: string[];\n\t\t},\n\t\tBuildError\n\t>\n> {\n\tconst optionalFileType = isOptionalRole(resolvedFile.role);\n\n\tconst _imports_: UnresolvedImport[] = [];\n\n\tconst dependencies = new Map<DependencyKey, RemoteDependency>();\n\tconst devDependencies = new Map<DependencyKey, RemoteDependency>();\n\tconst registryDependencies = new Set<string>();\n\n\tconst resolvedFileDependenciesResult = await resolveRemoteDependencies(\n\t\tresolvedFile.manualDependencies.dependencies,\n\t\t{\n\t\t\tcwd,\n\t\t\tconfig,\n\t\t\tregistryName: resolvedFile.parent.registryName,\n\t\t\titemName: item.name,\n\t\t}\n\t);\n\tif (resolvedFileDependenciesResult.isErr()) return err(resolvedFileDependenciesResult.error);\n\n\tconst fileDependencies = new Map<DependencyKey, RemoteDependency>(\n\t\tresolvedFileDependenciesResult.value.map((dep) => [toDependencyKey(dep), dep])\n\t);\n\n\tconst resolvedFileDevDependenciesResult = await resolveRemoteDependencies(\n\t\tresolvedFile.manualDependencies.devDependencies,\n\t\t{\n\t\t\tcwd,\n\t\t\tconfig,\n\t\t\tregistryName: resolvedFile.parent.registryName,\n\t\t\titemName: item.name,\n\t\t}\n\t);\n\tif (resolvedFileDevDependenciesResult.isErr())\n\t\treturn err(resolvedFileDevDependenciesResult.error);\n\n\tconst fileDevDependencies = new Map<DependencyKey, RemoteDependency>(\n\t\tresolvedFileDevDependenciesResult.value.map((dep) => [toDependencyKey(dep), dep])\n\t);\n\tconst fileRegistryDependencies = new Set<string>(\n\t\tresolvedFile.manualDependencies.registryDependencies\n\t);\n\n\tif (resolvedFile.dependencyResolution === 'auto') {\n\t\tconst resolvedDependenciesResult = await resolveRemoteDependencies(\n\t\t\tresolvedFile.dependencies,\n\t\t\t{\n\t\t\t\tcwd,\n\t\t\t\tconfig,\n\t\t\t\tregistryName: resolvedFile.parent.registryName,\n\t\t\t\titemName: item.name,\n\t\t\t}\n\t\t);\n\t\tif (resolvedDependenciesResult.isErr()) return err(resolvedDependenciesResult.error);\n\n\t\tconst resolvedDevDependenciesResult = await resolveRemoteDependencies(\n\t\t\tresolvedFile.devDependencies,\n\t\t\t{\n\t\t\t\tcwd,\n\t\t\t\tconfig,\n\t\t\t\tregistryName: resolvedFile.parent.registryName,\n\t\t\t\titemName: item.name,\n\t\t\t}\n\t\t);\n\t\tif (resolvedDevDependenciesResult.isErr()) return err(resolvedDevDependenciesResult.error);\n\n\t\tfor (const dependency of resolvedFile.localDependencies) {\n\t\t\tconst localDependency = resolvedFiles.get(normalizeAbsolute(dependency.fileName));\n\t\t\tif (localDependency) {\n\t\t\t\tconst selfReference = localDependency.parent.name === item.name;\n\t\t\t\t// only add to registry dependencies if not a self reference\n\t\t\t\tif (!selfReference) {\n\t\t\t\t\tif (optionalFileType) {\n\t\t\t\t\t\tfileRegistryDependencies.add(localDependency.parent.name);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tregistryDependencies.add(localDependency.parent.name);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_imports_.push({\n\t\t\t\t\timport: dependency.import,\n\t\t\t\t\titem: localDependency.parent.name,\n\t\t\t\t\tfile: {\n\t\t\t\t\t\ttype: localDependency.type,\n\t\t\t\t\t\tpath: localDependency.path,\n\t\t\t\t\t},\n\t\t\t\t\tmeta: await dependency.createTemplate(localDependency),\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// only error if in strict mode\n\t\t\t\tif (item.strict === false) continue;\n\t\t\t\treturn err(\n\t\t\t\t\tnew ImportedFileNotResolvedError({\n\t\t\t\t\t\treferencedFile: dependency.fileName,\n\t\t\t\t\t\tfileName: resolvedFile.absolutePath,\n\t\t\t\t\t\titem: item.name,\n\t\t\t\t\t\tregistryName: resolvedFile.parent.registryName,\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tfor (const dependency of resolvedDependenciesResult.value) {\n\t\t\tif (optionalFileType) {\n\t\t\t\tfileDependencies.set(toDependencyKey(dependency), dependency);\n\t\t\t} else {\n\t\t\t\tdependencies.set(toDependencyKey(dependency), dependency);\n\t\t\t}\n\t\t}\n\n\t\tfor (const dependency of resolvedDevDependenciesResult.value) {\n\t\t\tif (optionalFileType) {\n\t\t\t\tfileDevDependencies.set(toDependencyKey(dependency), dependency);\n\t\t\t} else {\n\t\t\t\tdevDependencies.set(toDependencyKey(dependency), dependency);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ok({\n\t\tfile: {\n\t\t\tabsolutePath: resolvedFile.absolutePath,\n\t\t\ttarget: resolvedFile.target,\n\t\t\tpath: resolvedFile.path,\n\t\t\tcontent: resolvedFile.content,\n\t\t\ttype: resolvedFile.type,\n\t\t\trole: resolvedFile.role,\n\t\t\t_imports_,\n\t\t\tregistryDependencies: Array.from(fileRegistryDependencies),\n\t\t\tdependencies: Array.from(fileDependencies.values()),\n\t\t\tdevDependencies: Array.from(fileDevDependencies.values()),\n\t\t},\n\t\tdependencies: Array.from(dependencies.values()),\n\t\tdevDependencies: Array.from(devDependencies.values()),\n\t\tregistryDependencies: Array.from(registryDependencies),\n\t});\n}\n\n/**\n * Convert a list of string or RemoteDependency objects into an array of RemoteDependency objects.\n *\n * @param dependencies\n * @param param1\n * @returns\n */\nexport function toRemoteDependencies(\n\tdependencies: (string | RemoteDependency)[],\n\t{ registryName, itemName }: { registryName: string; itemName: string }\n): Result<RemoteDependency[], BuildError> {\n\tconst remoteDependencies = new Map<DependencyKey, RemoteDependency>();\n\tfor (const dependency of dependencies) {\n\t\tlet dep: RemoteDependency;\n\t\tif (typeof dependency === 'string') {\n\t\t\tconst depResult = stringToRemoteDependency(dependency, { registryName, itemName });\n\t\t\tif (depResult.isErr()) return err(depResult.error);\n\t\t\tdep = depResult.value;\n\t\t} else {\n\t\t\tdep = dependency;\n\t\t}\n\t\tremoteDependencies.set(toDependencyKey(dep), dep);\n\t}\n\treturn ok(Array.from(remoteDependencies.values()));\n}\n\n/**\n * Convert a string into a RemoteDependency object.\n *\n * @param dependency\n * @param param1\n * @returns\n */\nexport function stringToRemoteDependency(\n\tdependency: string,\n\t{ registryName, itemName }: { registryName: string; itemName: string }\n): Result<RemoteDependency, BuildError> {\n\tconst parsed = parsePackageName(dependency);\n\tif (parsed.isErr())\n\t\treturn err(new InvalidDependencyError({ dependency, registryName, itemName }));\n\treturn ok({\n\t\tecosystem: 'js',\n\t\tname: parsed.value.name,\n\t\tversion: parsed.value.version,\n\t});\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/casing.ts",
    "content": "/** Converts a `kebab-case` string to a `camelCase` string\n *\n *\n * @param str\n * @returns\n *\n * ## Usage\n * ```ts\n * kebabToCamel('hello-world'); // helloWorld\n * ```\n */\nexport function kebabToCamel(str: string): string {\n\tlet newStr = '';\n\n\tfor (let i = 0; i < str.length; i++) {\n\t\t// capitalize first after a -\n\t\tif (str[i] === '-') {\n\t\t\ti++;\n\t\t\tif (i <= str.length - 1) {\n\t\t\t\tnewStr += str[i]!.toUpperCase();\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tnewStr += str[i]!.toLocaleLowerCase();\n\t}\n\n\treturn newStr;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/compat/shadcn.ts",
    "content": "import type { ImportTransform } from '@/api/langs';\nimport { createImportPattern, createReplacement } from '@/langs/js';\nimport type { AbsolutePath } from '@/utils/types';\n\nconst SUPPORTED_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];\n\n/**\n * Ripped from: https://github.com/shadcn-ui/ui/blob/main/packages/shadcn/src/utils/transformers/transform-import.ts\n */\n\ntype Config = {\n\taliases: {\n\t\tcomponents: string | undefined;\n\t\tutils: string | undefined;\n\t\tui: string | undefined;\n\t\tlib: string | undefined;\n\t\thooks: string | undefined;\n\t};\n};\n\nexport async function transformShadcnImports({\n\tcode,\n\timports,\n\tfileName,\n\tconfig,\n}: {\n\tcode: string;\n\timports: string[];\n\tfileName: AbsolutePath;\n\tconfig: Config;\n}): Promise<string> {\n\tif (!endsWithOneOf(fileName, SUPPORTED_EXTENSIONS)) return code;\n\n\tconst transformedImports: ImportTransform[] = [];\n\n\tfor (const specifier of imports) {\n\t\tconst updated = updateImportAliases(specifier, config);\n\n\t\ttransformedImports.push({\n\t\t\tpattern: createImportPattern(specifier),\n\t\t\treplacement: createReplacement(updated),\n\t\t});\n\t}\n\n\tfor (const transformation of transformedImports) {\n\t\tcode = code.replace(transformation.pattern, transformation.replacement);\n\t}\n\n\treturn code;\n}\n\nfunction updateImportAliases(moduleSpecifier: string, config: Config) {\n\t// Not a local import.\n\tif (!moduleSpecifier.startsWith('@/')) {\n\t\treturn moduleSpecifier;\n\t}\n\n\tif (config.aliases.ui || config.aliases.components) {\n\t\tif (moduleSpecifier.match(/^@\\/registry\\/(.+)\\/ui/)) {\n\t\t\treturn moduleSpecifier.replace(\n\t\t\t\t/^@\\/registry\\/(.+)\\/ui/,\n\t\t\t\tconfig.aliases.ui ?? `${config.aliases.components}/ui`\n\t\t\t);\n\t\t}\n\t}\n\n\tif (config.aliases.components && moduleSpecifier.match(/^@\\/registry\\/(.+)\\/components/)) {\n\t\treturn moduleSpecifier.replace(/^@\\/registry\\/(.+)\\/components/, config.aliases.components);\n\t}\n\n\tif (config.aliases.lib && moduleSpecifier.match(/^@\\/registry\\/(.+)\\/lib/)) {\n\t\treturn moduleSpecifier.replace(/^@\\/registry\\/(.+)\\/lib/, config.aliases.lib);\n\t}\n\n\tif (config.aliases.hooks && moduleSpecifier.match(/^@\\/registry\\/(.+)\\/hooks/)) {\n\t\treturn moduleSpecifier.replace(/^@\\/registry\\/(.+)\\/hooks/, config.aliases.hooks);\n\t}\n\n\treturn moduleSpecifier;\n}\n\nfunction endsWithOneOf(fileName: string, extensions: string[]): boolean {\n\treturn extensions.some((extension) => fileName.endsWith(extension));\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/config/index.ts",
    "content": "import { z } from 'zod';\nimport { DEFAULT_LANGS, type Language } from '@/langs';\nimport type { Output } from '@/outputs/types';\nimport { DEFAULT_PROVIDERS, type ProviderFactory } from '@/providers';\nimport type { RemoteDependency, UnresolvedFile } from '@/utils/build';\nimport type { AfterHook, BeforeHook } from '@/utils/hooks';\nimport type {\n\tAbsolutePath,\n\tItemRelativePath,\n\tLooseAutocomplete,\n\tMaybePromise,\n\tPrettify,\n} from '@/utils/types';\nimport { extract, type MaybeGetterAsync } from '@/utils/utils';\nimport type { Warning } from '@/utils/warnings';\n\nexport type RegistryConfigArgs = [{ cwd: string }];\n\nexport type RemoteDependencyResolverOptions = {\n\tcwd: AbsolutePath;\n};\n\nexport type RemoteDependencyResolver = (\n\tdep: RemoteDependency,\n\toptions: RemoteDependencyResolverOptions\n) => MaybePromise<RemoteDependency>;\n\nexport type BuildTransform = {\n\ttransform: (\n\t\tcontent: string,\n\t\topts: { cwd: AbsolutePath; file: UnresolvedFile }\n\t) => MaybePromise<{ content: string }>;\n};\n\nexport type Config = {\n\t/** An array of registries to fetch items from.\n\t * @example\n\t * ```ts\n\t * [\"@ieedan/std\", \"@ieedan/shadcn-svelte-extras\"]\n\t * ```\n\t */\n\tregistries: string[];\n\t/** Define your registry or registries here. */\n\tregistry:\n\t\t| MaybeGetterAsync<RegistryConfig, RegistryConfigArgs>\n\t\t| MaybeGetterAsync<RegistryConfig, RegistryConfigArgs>[]\n\t\t| RegistryConfig\n\t\t| RegistryConfig[];\n\tproviders: ProviderFactory[];\n\t/** Use this to add support for additional languages. */\n\tlanguages: Language[];\n\ttransforms: Transform[];\n\t/**\n\t * Where to put items of a given type in your project. The key is the item type and the value is path where you want it to be added to your project.\n\t * You can use the '*' key to set a default path for all item types. Specify the path for a custom item with `<type>/<name>`.\n\t * @example\n\t * ```ts\n\t * import { defineConfig } from \"jsrepo\";\n\t *\n\t * export default defineConfig({\n\t *  // ...\n\t *  paths: {\n\t *    \"*\": \"./src/items\",\n\t *    ui: \"./src/ui\",\n\t *    \"ui/button\": \"./components/ui\",\n\t *  }\n\t * });\n\t * ```\n\t */\n\tpaths: {\n\t\t[key in RegistryItemType | '*']?: string;\n\t};\n\tbuild: {\n\t\t/**\n\t\t * Custom warning handler. If not provided, warnings will be logged using the default logger.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * import { defineConfig } from \"jsrepo\";\n\t\t * import { InvalidImportWarning } from \"jsrepo/warnings\";\n\t\t *\n\t\t * export default defineConfig({\n\t\t *   // ...\n\t\t *   onwarn: (warning, handler) => {\n\t\t *     if (warning instanceof InvalidImportWarning) {\n\t\t *       // Skip warnings for $app/server and $app/navigation imports\n\t\t *       if (['$app/server', '$app/navigation'].includes(warning.specifier)) {\n\t\t *         return;\n\t\t *       }\n\t\t *     }\n\t\t *     handler(warning);\n\t\t *   }\n\t\t * });\n\t\t * ```\n\t\t */\n\t\tonwarn?: (warning: Warning, handler: (message: Warning) => void) => void;\n\t\ttransforms?: BuildTransform[];\n\t\t/**\n\t\t * Resolve each remote dependency before it is added to the built registry output.\n\t\t * Useful for rewriting protocol-based versions (e.g. `workspace:*`, `catalog:`).\n\t\t */\n\t\tremoteDependencyResolver?: RemoteDependencyResolver;\n\t};\n\t/**\n\t * Custom warning handler. If not provided, warnings will be logged using the default logger.\n\t *\n\t * @deprecated Use `build.onwarn` instead.\n\t *\n\t * @example\n\t * ```ts\n\t * import { defineConfig } from \"jsrepo\";\n\t * import { InvalidImportWarning } from \"jsrepo/warnings\";\n\t *\n\t * export default defineConfig({\n\t *   // ...\n\t *   onwarn: (warning, handler) => {\n\t *     if (warning instanceof InvalidImportWarning) {\n\t *       // Skip warnings for $app/server and $app/navigation imports\n\t *       if (['$app/server', '$app/navigation'].includes(warning.specifier)) {\n\t *         return;\n\t *       }\n\t *     }\n\t *     handler(warning);\n\t *   }\n\t * });\n\t * ```\n\t */\n\tonwarn?: (warning: Warning, handler: (message: Warning) => void) => void;\n\t/**\n\t * Lifecycle hooks run before and after CLI commands.\n\t * @example\n\t * ```ts\n\t * import { defineConfig } from \"jsrepo\";\n\t *\n\t * export default defineConfig({\n\t *   hooks: {\n\t *     before: ({ command }) => console.log(`Running ${command}...`),\n\t *     after: \"echo done\",\n\t *   },\n\t * });\n\t * ```\n\t */\n\thooks?: {\n\t\tafter?: AfterHook | AfterHook[];\n\t\tbefore?: BeforeHook | BeforeHook[];\n\t};\n};\n\nexport const RegistryMetaSchema = z.object({\n\tname: z.string(),\n\tdescription: z.string().optional(),\n\tversion: z.string().optional(),\n\thomepage: z.string().optional(),\n\ttags: z.array(z.string()).optional(),\n\trepository: z.string().optional(),\n\tbugs: z.string().optional(),\n\tauthors: z.array(z.string()).optional(),\n\tmeta: z.record(z.string(), z.string()).optional(),\n\taccess: z.enum(['public', 'private', 'marketplace']).optional(),\n});\n\nexport type RegistryMeta = {\n\t/**\n\t * The name of the registry. When publishing to [jsrepo.com](https://jsrepo.com) the name must follow the format of `@<scope>/<name>`.\n\t */\n\tname: string;\n\tdescription?: string;\n\t/**\n\t * The version of the registry. When publishing to [jsrepo.com](https://jsrepo.com) the version can be provided as `package` to use the version from the `package.json` file, otherwise it should be a valid semver version.\n\t */\n\tversion?: LooseAutocomplete<'package'>;\n\thomepage?: string;\n\ttags?: string[];\n\trepository?: string;\n\tbugs?: string;\n\tauthors?: string[];\n\tmeta?: Record<string, string>;\n\t/** The access level of the registry when published to jsrepo.com with `jsrepo publish`.\n\t *\n\t *  - \"public\" - The registry will be visible to everyone\n\t *  - \"private\" - The registry will be visible to only you\n\t *  - \"marketplace\" - The registry will purchasable on the jsrepo.com marketplace\n\t *\n\t *  @default \"public\"\n\t */\n\taccess?: 'public' | 'private' | 'marketplace';\n};\n\nexport type RegistryConfig = RegistryMeta & {\n\t/** These dependencies will not be installed with registry items even if detected. */\n\texcludeDeps?: string[];\n\titems: RegistryItem[];\n\t/** An array of output strategies. These allow you to customize the way your registry is distributed. */\n\toutputs?: Output[];\n\t/** Plugins that users need to install to use your registry. (Will be installed automatically when initializing your registry)*/\n\tplugins?: {\n\t\tlanguages?: RegistryPlugin[];\n\t\tproviders?: RegistryPlugin[];\n\t\ttransforms?: RegistryPlugin[];\n\t};\n\t/** The default path for each item type. You can also provide the path for a specific item with `<type>/<name>`. */\n\tdefaultPaths?: {\n\t\t[key in RegistryItemType]?: string;\n\t};\n};\n\nexport type RegistryPlugin = {\n\t/** The name of the package */\n\tpackage: string;\n\t/** The version of the package */\n\tversion?: string | undefined;\n\t/** Whether the plugin is optional. @default false */\n\toptional?: boolean;\n};\n\nexport type RegistryItemType = LooseAutocomplete<\n\t'block' | 'component' | 'lib' | 'hook' | 'ui' | 'page'\n>;\n\nexport const RegistryItemAddOptions = [\n\t'optionally-on-init',\n\t'on-init',\n\t'when-needed',\n\t'when-added',\n] as const;\nexport const RegistryItemAddSchema = z.enum(RegistryItemAddOptions);\nexport type RegistryItemAdd = (typeof RegistryItemAddOptions)[number];\n\nexport type RegistryItem = {\n\t/** The name of the item. MUST be unique. Spaces are NOT allowed. */\n\tname: string;\n\t/** Human readable title of the item */\n\ttitle?: string;\n\t/** The type of the item. This can be any string. Users will configure their paths based on this type. */\n\ttype: RegistryItemType;\n\t/** The description of the item not user visible but great for LLMs. */\n\tdescription?: string;\n\t/** Paths to the files that are required for the item to work. */\n\tfiles: RegistryItemFile[];\n\t/**\n\t * Requires that all dependencies of the item must be part of the registry.\n\t *\n\t * @default true\n\t */\n\tstrict?: boolean;\n\t/** Whether the dependency resolution should be automatic or manual. @default \"auto\" */\n\tdependencyResolution?: 'auto' | 'manual';\n\t/**\n\t * Dependencies to other items in the registry. For many languages these can be automatically detected but can also be nice if there is another item you need that cannot be detected. They should be in the format of `<name>`.\n\t * @example\n\t * ```ts\n\t * {\n\t *  // ...\n\t *  registryDependencies: [\"<name>\"]\n\t * }\n\t * ```\n\t */\n\tregistryDependencies?: string[];\n\t/**\n\t * Provide a list of dependencies to be installed with the item. If dependencies are provided as a string they will be assumed to be a js dependency.\n\t */\n\tdependencies?: (RemoteDependency | string)[];\n\t/**\n\t * Provide a list of devDependencies to be installed with the item. If dependencies are provided as a string they will be assumed to be a js dependency.\n\t */\n\tdevDependencies?: (RemoteDependency | string)[];\n\t/**\n\t * Controls when the item will be added to the project.\n\t *\n\t * - \"on-init\" - Added on registry init or when it's needed by another item\n\t * - \"when-needed\" - Not listed and only added when another item is added that depends on it\n\t * - \"when-added\" - Added when the user selects it to be added\n\t *\n\t * @default \"when-added\"\n\t */\n\tadd?: RegistryItemAdd;\n\t/**\n\t * Environment variables that are required for the item to work. These will be added to the users `.env` or `.env.local` file. NEVER ADD SECRETS HERE.\n\t */\n\tenvVars?: Record<string, string>;\n\tcategories?: string[];\n\tmeta?: Record<string, string>;\n};\n\n/** Built-in file roles. Custom roles are allowed. */\nexport const RegistryFileRoles = ['example', 'doc', 'test', 'file'] as const;\n\nexport type BuiltinRegistryFileRole = (typeof RegistryFileRoles)[number];\nexport type RegistryFileRoles = LooseAutocomplete<BuiltinRegistryFileRole>;\n\nexport type RegistryItemFile = {\n\t/** Path of the file/folder relative to registry config. */\n\tpath: string;\n\t/**\n\t * The type of the file. This will determine the path that the file will be added to in the users project.\n\t */\n\ttype?: RegistryItemType;\n\t/**\n\t * The role of the file. Roles are arbitrary strings.\n\t *\n\t * - \"file\" - A regular file (always installed)\n\t * - \"example\" - An example file (optionally installed, great for LLMs)\n\t * - \"test\" - A test file (optionally installed)\n\t * - \"doc\" - A documentation file (optionally installed, great for LLMs)\n\t *\n\t * Any role other than \"file\" is considered optional and is only included when\n\t * the user passes `--with <role>`.\n\t *\n\t * If a parent folder has a role this file will inherit the role from the parent folder.\n\t *\n\t * @default \"file\"\n\t */\n\trole?: RegistryFileRoles;\n\t/**\n\t * Whether the dependency resolution should be automatic or manual.\n\t * @default \"auto\"\n\t *\n\t * @remarks when this option is set on the parent registry item this option will have no effect\n\t */\n\tdependencyResolution?: 'auto' | 'manual';\n\t/**\n\t * The target path for this file in the users project. Overrides all other path settings.\n\t */\n\ttarget?: string;\n\t/**\n\t * Dependencies to other items in the registry. For many languages these can be automatically detected but can also be nice if there is another item you need that cannot be detected. They should be in the format of `<name>`.\n\t * @example\n\t * ```ts\n\t * {\n\t *  // ...\n\t *  registryDependencies: [\"<name>\"]\n\t * }\n\t * ```\n\t */\n\tregistryDependencies?: string[];\n\t/**\n\t * Provide a list of dependencies to be installed with the item. If dependencies are provided as a string they will be assumed to be a js dependency.\n\t */\n\tdependencies?: (RemoteDependency | string)[];\n\t/**\n\t * Provide a list of devDependencies to be installed with the item. If dependencies are provided as a string they will be assumed to be a js dependency.\n\t */\n\tdevDependencies?: (RemoteDependency | string)[];\n\t/**\n\t * Only valid as children of a folder. Allows you to individually configure each file in the folder.\n\t */\n\tfiles?: RegistryItemFolderFile[];\n\t/**\n\t * Include additional metadata on the item. (Available to LLMs when using the `@jsrepo/mcp` server)\n\t */\n\tmeta?: Record<string, string>;\n};\n\nexport type RegistryItemFolderFile = Prettify<\n\tOmit<RegistryItemFile, 'target' | 'type' | 'path'> & {\n\t\t/**\n\t\t * Path to the file/folder relative to the parent folder.\n\t\t */\n\t\tpath: string;\n\t}\n>;\n\nexport type TransformOptions = {\n\tcwd: AbsolutePath;\n\tregistryUrl: string;\n\titem: {\n\t\tname: string;\n\t\ttype: RegistryItemType;\n\t};\n};\n\nexport type Transform = {\n\ttransform: (opts: {\n\t\tcode: string;\n\t\tfileName: ItemRelativePath;\n\t\toptions: TransformOptions;\n\t}) => Promise<{ code?: string; fileName?: ItemRelativePath }>;\n};\n\nexport function defineConfig(config: Partial<Config> | (() => Partial<Config>)): Config {\n\tconst c = extract(config);\n\n\treturn {\n\t\tproviders: c.providers ?? DEFAULT_PROVIDERS,\n\t\tregistries: c.registries ?? [],\n\t\tregistry: c.registry ?? [],\n\t\tlanguages: c.languages ?? DEFAULT_LANGS,\n\t\ttransforms: c.transforms ?? [],\n\t\tpaths: c.paths ?? {},\n\t\thooks: c.hooks,\n\t\tbuild: {\n\t\t\tonwarn: c.build?.onwarn ?? c.onwarn,\n\t\t\t...c.build,\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/config/mods/add-plugins.ts",
    "content": "import MagicString from 'magic-string';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport {\n\ttype AssignmentTargetProperty,\n\ttype BindingProperty,\n\ttype ImportDeclaration,\n\ttype ObjectExpression,\n\ttype ObjectProperty,\n\tparse,\n\tVisitor,\n} from 'oxc-parser';\nimport { getImports } from '@/langs/js';\nimport { kebabToCamel } from '@/utils/casing';\nimport {\n\tConfigObjectNotFoundError,\n\tCouldNotFindJsrepoImportError,\n\tInvalidKeyTypeError,\n\tInvalidPluginError,\n} from '@/utils/errors';\nimport { parsePackageName } from '@/utils/parse-package-name';\nimport type { AbsolutePath } from '@/utils/types';\nimport { noop } from '@/utils/utils';\nimport type { RegistryPlugin } from '..';\n\nclass VisitorState {\n\tIGNORED_PROPERTIES = ['registry', 'registries', 'paths'];\n\timportDeclarations: ImportDeclaration[] = [];\n\n\twithinExportDefaultDeclaration = false;\n\twithinDefineConfig = false;\n\twithinMainObjectExpression = false;\n\twithinIgnoredProperty = false;\n\tlastImportDeclaration: ImportDeclaration | null = null;\n\tconfigKeyPropertyDeclaration:\n\t\t| ObjectProperty\n\t\t| AssignmentTargetProperty\n\t\t| BindingProperty\n\t\t| null = null;\n\tmainObjectExpression: ObjectExpression | null = null;\n}\n\nexport type Plugin = {\n\tname: string;\n\tpackageName: string;\n\tversion: string | undefined;\n};\n\nexport async function addPluginsToConfig({\n\tplugins,\n\tkey,\n\tconfig,\n}: {\n\tconfig: {\n\t\tpath: string;\n\t\tcode: string;\n\t};\n\tplugins: Plugin[];\n\tkey: 'transforms' | 'providers' | 'languages';\n}): Promise<\n\tResult<string, InvalidKeyTypeError | CouldNotFindJsrepoImportError | ConfigObjectNotFoundError>\n> {\n\tif (plugins.length === 0) return ok(config.code);\n\n\tconst parsed = await parse(config.path, config.code);\n\n\tconst s = new MagicString(config.code);\n\n\tconst state = new VisitorState();\n\tstate.withinDefineConfig = true;\n\n\tconst visitor = new Visitor({\n\t\tImportDeclaration(decl) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tstate.lastImportDeclaration = decl;\n\t\t\tstate.importDeclarations.push(decl);\n\t\t},\n\t\tExportDefaultDeclaration() {\n\t\t\tstate.withinExportDefaultDeclaration = true;\n\t\t},\n\t\t'ExportDefaultDeclaration:exit'() {\n\t\t\t// realistically there should only be one export default declaration\n\t\t\tstate.withinExportDefaultDeclaration = false;\n\t\t},\n\t\tObjectExpression(expr) {\n\t\t\tif (!state.withinExportDefaultDeclaration) return;\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (state.withinIgnoredProperty) return;\n\t\t\tif (state.withinMainObjectExpression) return;\n\n\t\t\tstate.mainObjectExpression = expr;\n\t\t\tstate.withinMainObjectExpression = true;\n\t\t\t// we don't use an exit here cause we don't have a way of knowing it's the same one\n\t\t\t// instead we just reset this upon exiting defineConfig\n\t\t},\n\t\tCallExpression(expr) {\n\t\t\tif (!state.withinExportDefaultDeclaration) return;\n\t\t\tif (expr.callee.type === 'Identifier') {\n\t\t\t\tif (expr.callee.name === 'defineConfig') {\n\t\t\t\t\tstate.withinDefineConfig = true;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t'CallExpression:exit'(expr) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (expr.callee.type === 'Identifier') {\n\t\t\t\tif (expr.callee.name === 'defineConfig') {\n\t\t\t\t\tstate.withinDefineConfig = false;\n\t\t\t\t\tstate.withinMainObjectExpression = false;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tProperty(prop) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (!state.withinMainObjectExpression) return;\n\t\t\tif (state.withinIgnoredProperty) return;\n\t\t\tif (\n\t\t\t\tstate.IGNORED_PROPERTIES.includes(\n\t\t\t\t\tprop.key.type === 'Identifier' ? prop.key.name : ''\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tstate.withinIgnoredProperty = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// locate the config key\n\t\t\tif (\n\t\t\t\tprop.key.type === 'Identifier' &&\n\t\t\t\t(prop.kind === 'init' || prop.kind === 'get') &&\n\t\t\t\tprop.key.name === key\n\t\t\t) {\n\t\t\t\tif (prop.value.type !== 'ArrayExpression')\n\t\t\t\t\treturn err(new InvalidKeyTypeError({ key, type: 'array' }));\n\t\t\t\tstate.configKeyPropertyDeclaration = prop;\n\t\t\t}\n\t\t},\n\t\t'Property:exit'(prop) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (\n\t\t\t\tstate.IGNORED_PROPERTIES.includes(\n\t\t\t\t\tprop.key.type === 'Identifier' ? prop.key.name : ''\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tstate.withinIgnoredProperty = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t},\n\t});\n\n\tvisitor.visit(parsed.program);\n\n\t// filter out plugins that are already imported\n\tplugins = plugins.filter(\n\t\t(plugin) =>\n\t\t\t!state.importDeclarations.some(\n\t\t\t\t(importDeclaration) => importDeclaration.source.value === plugin.packageName\n\t\t\t)\n\t);\n\n\tif (plugins.length === 0) {\n\t\treturn ok(config.code);\n\t}\n\n\t// add imports\n\tif (state.lastImportDeclaration !== null) {\n\t\ts.appendRight(\n\t\t\tstate.lastImportDeclaration.end,\n\t\t\tplugins\n\t\t\t\t.map((plugin) => `\\nimport ${plugin.name} from '${plugin.packageName}';`)\n\t\t\t\t.join('')\n\t\t);\n\t}\n\n\tlet defaultsArray: string | null = null;\n\tif (key === 'providers') {\n\t\tdefaultsArray = 'DEFAULT_PROVIDERS';\n\t} else if (key === 'languages') {\n\t\tdefaultsArray = 'DEFAULT_LANGS';\n\t}\n\tconst jsrepoImportDeclaration = state.importDeclarations.find(\n\t\t(decl) => decl.source.value === 'jsrepo'\n\t);\n\tif (jsrepoImportDeclaration === undefined) return err(new CouldNotFindJsrepoImportError());\n\tlet defaultsArrayState: { added: boolean; name: string } | null = defaultsArray\n\t\t? { added: false, name: defaultsArray }\n\t\t: null;\n\tif (defaultsArrayState !== null) {\n\t\tfor (const specifier of jsrepoImportDeclaration.specifiers) {\n\t\t\tif (specifier.type === 'ImportSpecifier') {\n\t\t\t\tif (specifier.imported.type === 'Identifier') {\n\t\t\t\t\tif (specifier.imported.name === defaultsArray) {\n\t\t\t\t\t\tdefaultsArrayState.added = true;\n\t\t\t\t\t\tif (specifier.local.name !== defaultsArray) {\n\t\t\t\t\t\t\tdefaultsArrayState.name = specifier.local.name;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// add call expressions to the config key\n\tif (state.configKeyPropertyDeclaration !== null) {\n\t\t// handle object with the config key\n\t\tif (state.configKeyPropertyDeclaration.value.type !== 'ArrayExpression') {\n\t\t\treturn err(new InvalidKeyTypeError({ key, type: 'array' }));\n\t\t}\n\t\tconst code = s.slice(\n\t\t\tstate.configKeyPropertyDeclaration.value.start,\n\t\t\tstate.configKeyPropertyDeclaration.value.end\n\t\t);\n\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\tconst lastComma =\n\t\t\tlastCommaIndex === -1\n\t\t\t\t? -1\n\t\t\t\t: lastCommaIndex + state.configKeyPropertyDeclaration.value.start + 1;\n\t\tif (state.configKeyPropertyDeclaration.value.elements.length === 0) {\n\t\t\tdefaultsArrayState = null;\n\t\t\ts.appendRight(\n\t\t\t\tstate.configKeyPropertyDeclaration.value.start + 1,\n\t\t\t\t`${plugins.map((plugin) => `${plugin.name}()`).join(', ')}`\n\t\t\t);\n\t\t} else {\n\t\t\tconst lastElement =\n\t\t\t\tstate.configKeyPropertyDeclaration.value.elements[\n\t\t\t\t\tstate.configKeyPropertyDeclaration.value.elements.length - 1\n\t\t\t\t]!;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastElement.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\t// null it out since we don't use it\n\t\t\tdefaultsArrayState = null;\n\t\t\ts.appendRight(\n\t\t\t\tneedsComma ? lastElement.end : lastComma,\n\t\t\t\t`${needsComma ? ',' : ''} ${plugins.map((plugin) => `${plugin.name}()`).join(', ')}`\n\t\t\t);\n\t\t}\n\t} else {\n\t\t// handle object without the config key\n\t\tif (state.mainObjectExpression === null) return err(new ConfigObjectNotFoundError());\n\t\tconst code = s.slice(state.mainObjectExpression.start, state.mainObjectExpression.end);\n\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\tconst lastComma =\n\t\t\tlastCommaIndex === -1 ? -1 : lastCommaIndex + state.mainObjectExpression.start + 1;\n\t\tif (state.mainObjectExpression.properties.length === 0) {\n\t\t\ts.appendRight(\n\t\t\t\tstate.mainObjectExpression.start + 1,\n\t\t\t\t`\\n\\t${key}: [${defaultsArrayState ? `...${defaultsArrayState.name}, ` : ''}${plugins\n\t\t\t\t\t.map((plugin) => `${plugin.name}()`)\n\t\t\t\t\t.join(', ')}]`\n\t\t\t);\n\t\t} else {\n\t\t\tconst lastProperty =\n\t\t\t\tstate.mainObjectExpression.properties[\n\t\t\t\t\tstate.mainObjectExpression.properties.length - 1\n\t\t\t\t]!;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastProperty.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\ts.appendRight(\n\t\t\t\tneedsComma ? lastProperty.end : lastComma,\n\t\t\t\t`${needsComma ? ',' : ''}\\n\\t${key}: [${\n\t\t\t\t\tdefaultsArrayState ? `...${defaultsArrayState.name}, ` : ''\n\t\t\t\t}${plugins.map((plugin) => `${plugin.name}()`).join(', ')}]`\n\t\t\t);\n\t\t}\n\n\t\tif (defaultsArrayState && !defaultsArrayState.added) {\n\t\t\tconst code = s.slice(jsrepoImportDeclaration.start, jsrepoImportDeclaration.end);\n\t\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\t\tconst lastComma =\n\t\t\t\tlastCommaIndex === -1 ? -1 : lastCommaIndex + jsrepoImportDeclaration.start + 1;\n\n\t\t\tconst lastSpecifier =\n\t\t\t\tjsrepoImportDeclaration.specifiers[jsrepoImportDeclaration.specifiers.length - 1]!;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastSpecifier.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\ts.appendRight(\n\t\t\t\tneedsComma ? lastSpecifier.end : lastComma,\n\t\t\t\t`${needsComma ? ',' : ''} ${defaultsArrayState.name}`\n\t\t\t);\n\t\t}\n\t}\n\n\treturn ok(s.toString());\n}\n\nexport async function neededPlugins({\n\tplugins,\n\tconfig,\n}: {\n\tconfig: {\n\t\tpath: AbsolutePath;\n\t\tcode: string;\n\t};\n\tplugins: RegistryPlugin[];\n}): Promise<RegistryPlugin[]> {\n\tconst imports = await getImports(config.code, {\n\t\tfileName: config.path,\n\t\twarn: noop,\n\t});\n\treturn plugins.filter((plugin) => !imports.includes(plugin.package));\n}\n\nexport const OFFICIAL_PLUGINS = [\n\t{\n\t\tshorthand: 'prettier',\n\t\tname: '@jsrepo/transform-prettier',\n\t},\n\t{\n\t\tshorthand: 'biome',\n\t\tname: '@jsrepo/transform-biome',\n\t},\n\t{\n\t\tshorthand: 'javascript',\n\t\tname: '@jsrepo/transform-javascript',\n\t},\n\t{\n\t\tshorthand: 'oxfmt',\n\t\tname: '@jsrepo/transform-oxfmt',\n\t},\n\t{\n\t\tshorthand: 'filecasing',\n\t\tname: '@jsrepo/transform-filecasing',\n\t},\n];\n\nexport function parsePlugins(\n\tplugins: string[],\n\tkey: 'transform' | 'provider' | 'language'\n): Result<Plugin[], InvalidPluginError> {\n\tconst pluginsResult = plugins.map((plugin) => parsePluginName(plugin, key));\n\tconst finalPlugins: Plugin[] = [];\n\tfor (const result of pluginsResult) {\n\t\tif (result.isErr()) return err(result.error);\n\t\tfinalPlugins.push(result.value);\n\t}\n\treturn ok(finalPlugins);\n}\n\nexport function parsePluginName(\n\tplugin: string,\n\tkey: 'transform' | 'provider' | 'language'\n): Result<Plugin, InvalidPluginError> {\n\tconst officialPlugin = OFFICIAL_PLUGINS.find((p) => p.shorthand === plugin);\n\tif (officialPlugin) {\n\t\tconst parsedPlugin = parsePluginName(officialPlugin.name, key);\n\t\tif (parsedPlugin.isErr()) return err(parsedPlugin.error);\n\t\treturn ok({\n\t\t\tname: parsedPlugin.value.name,\n\t\t\tpackageName: officialPlugin.name,\n\t\t\tversion: undefined,\n\t\t});\n\t}\n\n\tconst parsedPackage = parsePackageName(plugin);\n\tif (parsedPackage.isErr()) return err(new InvalidPluginError(plugin));\n\n\tlet name = parsedPackage.value.name;\n\tif (parsedPackage.value.name.startsWith('@')) {\n\t\tif (parsedPackage.value.name.startsWith('@jsrepo/')) {\n\t\t\tif (!parsedPackage.value.name.split('/')[1]?.includes(key)) {\n\t\t\t\t// hack around names like @jsrepo/shadcn\n\t\t\t\tname = `jsrepo-${key}-${parsedPackage.value.name.split('/')[1]!}`;\n\t\t\t} else {\n\t\t\t\t// add jsrepo- prefix back to official plugins\n\t\t\t\t// instead of @jsrepo/transform-prettier\n\t\t\t\t// we want jsrepo-transform-prettier\n\t\t\t\tname = `jsrepo-${parsedPackage.value.name.split('/')[1]!}`;\n\t\t\t}\n\t\t} else {\n\t\t\t// use the second portion of the name\n\t\t\t// instead of @example/jsrepo-transform-prettier\n\t\t\t// we want jsrepo-transform-prettier\n\t\t\tname = parsedPackage.value.name.split('/')[1]!;\n\t\t}\n\t}\n\n\treturn ok({\n\t\tname: kebabToCamel(name.replace(`jsrepo-${key}-`, '')),\n\t\tpackageName: parsedPackage.value.name,\n\t\tversion: parsedPackage.value.version,\n\t});\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/config/mods/add-registries.ts",
    "content": "import MagicString from 'magic-string';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport {\n\ttype AssignmentTargetProperty,\n\ttype BindingProperty,\n\ttype ObjectExpression,\n\ttype ObjectProperty,\n\tparse,\n\tVisitor,\n} from 'oxc-parser';\nimport { ConfigObjectNotFoundError, InvalidKeyTypeError } from '@/utils/errors';\n\nclass VisitorState {\n\twithinExportDefaultDeclaration = false;\n\twithinDefineConfig = false;\n\twithinMainObjectExpression = false;\n\twithinIgnoredProperty = false;\n\tconfigKeyPropertyDeclaration:\n\t\t| ObjectProperty\n\t\t| AssignmentTargetProperty\n\t\t| BindingProperty\n\t\t| null = null;\n\tmainObjectExpression: ObjectExpression | null = null;\n}\n\nexport async function addRegistriesToConfig(\n\tregistries: string[],\n\t{ config }: { config: { path: string; code: string } }\n): Promise<Result<string, InvalidKeyTypeError | ConfigObjectNotFoundError>> {\n\tif (registries.length === 0) return ok(config.code);\n\n\tconst parsed = await parse(config.path, config.code);\n\n\tconst s = new MagicString(config.code);\n\n\tconst state = new VisitorState();\n\tstate.withinDefineConfig = true;\n\n\tconst visitor = new Visitor({\n\t\tExportDefaultDeclaration() {\n\t\t\tstate.withinExportDefaultDeclaration = true;\n\t\t},\n\t\t'ExportDefaultDeclaration:exit'() {\n\t\t\t// realistically there should only be one export default declaration\n\t\t\tstate.withinExportDefaultDeclaration = false;\n\t\t},\n\t\tObjectExpression(expr) {\n\t\t\tif (!state.withinExportDefaultDeclaration) return;\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (state.withinIgnoredProperty) return;\n\t\t\tif (state.withinMainObjectExpression) return;\n\n\t\t\tstate.mainObjectExpression = expr;\n\t\t\tstate.withinMainObjectExpression = true;\n\t\t\t// we don't use an exit here cause we don't have a way of knowing it's the same one\n\t\t\t// instead we just reset this upon exiting defineConfig\n\t\t},\n\t\tCallExpression(expr) {\n\t\t\tif (!state.withinExportDefaultDeclaration) return;\n\t\t\tif (expr.callee.type === 'Identifier') {\n\t\t\t\tif (expr.callee.name === 'defineConfig') {\n\t\t\t\t\tstate.withinDefineConfig = true;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t'CallExpression:exit'(expr) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (expr.callee.type === 'Identifier') {\n\t\t\t\tif (expr.callee.name === 'defineConfig') {\n\t\t\t\t\tstate.withinDefineConfig = false;\n\t\t\t\t\tstate.withinMainObjectExpression = false;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tProperty(prop) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (!state.withinMainObjectExpression) return;\n\t\t\tif (state.withinIgnoredProperty) return;\n\t\t\tif (prop.key.type === 'Identifier' && prop.key.name !== 'registries') {\n\t\t\t\tstate.withinIgnoredProperty = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// locate the config key\n\t\t\tif (\n\t\t\t\tprop.key.type === 'Identifier' &&\n\t\t\t\t(prop.kind === 'init' || prop.kind === 'get') &&\n\t\t\t\tprop.key.name === 'registries'\n\t\t\t) {\n\t\t\t\tif (prop.value.type !== 'ArrayExpression')\n\t\t\t\t\treturn err(new InvalidKeyTypeError({ key: 'registries', type: 'array' }));\n\t\t\t\tstate.configKeyPropertyDeclaration = prop;\n\t\t\t}\n\t\t},\n\t\t'Property:exit'(prop) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (prop.key.type === 'Identifier' && prop.key.name !== 'registries') {\n\t\t\t\tstate.withinIgnoredProperty = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t},\n\t});\n\n\tvisitor.visit(parsed.program);\n\n\t// add call expressions to the config key\n\tif (state.configKeyPropertyDeclaration !== null) {\n\t\t// handle object with the config key\n\t\tif (state.configKeyPropertyDeclaration.value.type !== 'ArrayExpression') {\n\t\t\treturn err(new InvalidKeyTypeError({ key: 'registries', type: 'array' }));\n\t\t}\n\n\t\tfor (const value of state.configKeyPropertyDeclaration.value.elements) {\n\t\t\tif (!value || value.type !== 'Literal') continue;\n\t\t\tif (typeof value.value !== 'string') continue;\n\n\t\t\tregistries = registries.filter((r) => r !== value.value);\n\t\t}\n\n\t\tif (registries.length === 0) return ok(config.code);\n\n\t\tconst code = s.slice(\n\t\t\tstate.configKeyPropertyDeclaration.value.start,\n\t\t\tstate.configKeyPropertyDeclaration.value.end\n\t\t);\n\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\tconst lastComma =\n\t\t\tlastCommaIndex === -1\n\t\t\t\t? -1\n\t\t\t\t: lastCommaIndex + state.configKeyPropertyDeclaration.value.start + 1;\n\t\tif (state.configKeyPropertyDeclaration.value.elements.length === 0) {\n\t\t\ts.appendRight(\n\t\t\t\tstate.configKeyPropertyDeclaration.value.start + 1,\n\t\t\t\t`${registries.map((registry) => `'${registry}'`).join(', ')}`\n\t\t\t);\n\t\t} else {\n\t\t\tconst lastElement =\n\t\t\t\tstate.configKeyPropertyDeclaration.value.elements[\n\t\t\t\t\tstate.configKeyPropertyDeclaration.value.elements.length - 1\n\t\t\t\t]!;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastElement.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\ts.appendRight(\n\t\t\t\tneedsComma ? lastElement.end : lastComma,\n\t\t\t\t`${needsComma ? ',' : ''} ${registries.map((registry) => `'${registry}'`).join(', ')}`\n\t\t\t);\n\t\t}\n\t} else {\n\t\t// handle object without the config key\n\t\tif (state.mainObjectExpression === null) return err(new ConfigObjectNotFoundError());\n\t\tconst code = s.slice(state.mainObjectExpression.start, state.mainObjectExpression.end);\n\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\tconst lastComma =\n\t\t\tlastCommaIndex === -1 ? -1 : lastCommaIndex + state.mainObjectExpression.start + 1;\n\t\tif (state.mainObjectExpression.properties.length === 0) {\n\t\t\ts.appendRight(\n\t\t\t\tstate.mainObjectExpression.start + 1,\n\t\t\t\t`\\n\\tregistries: [${registries.map((registry) => `'${registry}'`).join(', ')}]`\n\t\t\t);\n\t\t} else {\n\t\t\tconst lastProperty =\n\t\t\t\tstate.mainObjectExpression.properties[\n\t\t\t\t\tstate.mainObjectExpression.properties.length - 1\n\t\t\t\t]!;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastProperty.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\ts.appendRight(\n\t\t\t\tneedsComma ? lastProperty.end : lastComma,\n\t\t\t\t`${needsComma ? ',' : ''}\\n\\tregistries: [${registries.map((registry) => `'${registry}'`).join(', ')}]`\n\t\t\t);\n\t\t}\n\t}\n\n\treturn ok(s.toString());\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/config/mods/update-paths.ts",
    "content": "import MagicString from 'magic-string';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport {\n\ttype AssignmentTargetProperty,\n\ttype BindingProperty,\n\ttype ObjectExpression,\n\ttype ObjectProperty,\n\tparse,\n\tVisitor,\n} from 'oxc-parser';\nimport type { Config } from '@/utils/config';\nimport { ConfigObjectNotFoundError, InvalidKeyTypeError } from '@/utils/errors';\nimport { VALID_VARIABLE_NAME_REGEX } from '../utils';\n\nclass VisitorState {\n\twithinExportDefaultDeclaration = false;\n\twithinDefineConfig = false;\n\twithinMainObjectExpression = false;\n\twithinIgnoredProperty = false;\n\tconfigKeyPropertyDeclaration:\n\t\t| ObjectProperty\n\t\t| AssignmentTargetProperty\n\t\t| BindingProperty\n\t\t| null = null;\n\tmainObjectExpression: ObjectExpression | null = null;\n}\n\nexport async function updateConfigPaths(\n\tpaths: Config['paths'],\n\t{ config }: { config: { path: string; code: string } }\n): Promise<Result<string, InvalidKeyTypeError | ConfigObjectNotFoundError>> {\n\tif (Object.keys(paths).length === 0) return ok(config.code);\n\n\tconst parsed = await parse(config.path, config.code);\n\n\tconst s = new MagicString(config.code);\n\n\tconst state = new VisitorState();\n\tstate.withinDefineConfig = true;\n\n\tconst visitor = new Visitor({\n\t\tExportDefaultDeclaration() {\n\t\t\tstate.withinExportDefaultDeclaration = true;\n\t\t},\n\t\t'ExportDefaultDeclaration:exit'() {\n\t\t\t// realistically there should only be one export default declaration\n\t\t\tstate.withinExportDefaultDeclaration = false;\n\t\t},\n\t\tObjectExpression(expr) {\n\t\t\tif (!state.withinExportDefaultDeclaration) return;\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (state.withinIgnoredProperty) return;\n\t\t\tif (state.withinMainObjectExpression) return;\n\n\t\t\tstate.mainObjectExpression = expr;\n\t\t\tstate.withinMainObjectExpression = true;\n\t\t\t// we don't use an exit here cause we don't have a way of knowing it's the same one\n\t\t\t// instead we just reset this upon exiting defineConfig\n\t\t},\n\t\tCallExpression(expr) {\n\t\t\tif (!state.withinExportDefaultDeclaration) return;\n\t\t\tif (expr.callee.type === 'Identifier') {\n\t\t\t\tif (expr.callee.name === 'defineConfig') {\n\t\t\t\t\tstate.withinDefineConfig = true;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t'CallExpression:exit'(expr) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (expr.callee.type === 'Identifier') {\n\t\t\t\tif (expr.callee.name === 'defineConfig') {\n\t\t\t\t\tstate.withinDefineConfig = false;\n\t\t\t\t\tstate.withinMainObjectExpression = false;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tProperty(prop) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (!state.withinMainObjectExpression) return;\n\t\t\tif (state.withinIgnoredProperty) return;\n\t\t\tif (prop.key.type === 'Identifier' && prop.key.name !== 'paths') {\n\t\t\t\tstate.withinIgnoredProperty = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// locate the config key\n\t\t\tif (\n\t\t\t\tprop.key.type === 'Identifier' &&\n\t\t\t\t(prop.kind === 'init' || prop.kind === 'get') &&\n\t\t\t\tprop.key.name === 'paths'\n\t\t\t) {\n\t\t\t\tif (prop.value.type !== 'ObjectExpression')\n\t\t\t\t\treturn err(new InvalidKeyTypeError({ key: 'paths', type: 'object' }));\n\t\t\t\tstate.configKeyPropertyDeclaration = prop;\n\t\t\t}\n\t\t},\n\t\t'Property:exit'(prop) {\n\t\t\tif (!state.withinDefineConfig) return;\n\t\t\tif (prop.key.type === 'Identifier' && prop.key.name !== 'paths') {\n\t\t\t\tstate.withinIgnoredProperty = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t},\n\t});\n\n\tvisitor.visit(parsed.program);\n\n\t// add call expressions to the config key\n\tif (state.configKeyPropertyDeclaration !== null) {\n\t\t// handle object with the config key\n\t\tif (state.configKeyPropertyDeclaration.value.type !== 'ObjectExpression') {\n\t\t\treturn err(new InvalidKeyTypeError({ key: 'paths', type: 'object' }));\n\t\t}\n\n\t\tconst propsToOverwrite = new Map<string, ObjectProperty>();\n\n\t\tfor (const property of state.configKeyPropertyDeclaration.value.properties) {\n\t\t\tif (\n\t\t\t\tproperty.type === 'Property' &&\n\t\t\t\t(property.key.type === 'Literal' || property.key.type === 'Identifier')\n\t\t\t) {\n\t\t\t\tconst key = s.slice(property.key.start, property.key.end).replaceAll(/['\"]/g, '');\n\t\t\t\tif (paths[key] !== undefined) {\n\t\t\t\t\tpropsToOverwrite.set(key, property);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst code = s.slice(\n\t\t\tstate.configKeyPropertyDeclaration.value.start,\n\t\t\tstate.configKeyPropertyDeclaration.value.end\n\t\t);\n\n\t\tif (state.configKeyPropertyDeclaration.value.properties.length === 0) {\n\t\t\t// easy path for no properties\n\t\t\ts.overwrite(\n\t\t\t\tstate.configKeyPropertyDeclaration.value.start,\n\t\t\t\tstate.configKeyPropertyDeclaration.value.end,\n\t\t\t\t`{\\n${Object.entries(paths)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]) =>\n\t\t\t\t\t\t\t`\\t\\t${VALID_VARIABLE_NAME_REGEX.test(key) ? key : `'${key}'`}: '${value}'`\n\t\t\t\t\t)\n\t\t\t\t\t.join(',\\n')}\\n\\t}`\n\t\t\t);\n\t\t} else {\n\t\t\tconst lastProperty =\n\t\t\t\tstate.configKeyPropertyDeclaration.value.properties[\n\t\t\t\t\tstate.configKeyPropertyDeclaration.value.properties.length - 1\n\t\t\t\t]!;\n\t\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\t\tconst lastComma =\n\t\t\t\tlastCommaIndex === -1\n\t\t\t\t\t? -1\n\t\t\t\t\t: lastCommaIndex + state.configKeyPropertyDeclaration.value.start + 1;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastProperty.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\tfor (const [type, path] of Object.entries(paths)) {\n\t\t\t\tconst overwrittenProperty = propsToOverwrite.get(type);\n\t\t\t\tif (overwrittenProperty !== undefined) {\n\t\t\t\t\t// overwrite property value only\n\t\t\t\t\ts.overwrite(\n\t\t\t\t\t\toverwrittenProperty.value.start,\n\t\t\t\t\t\toverwrittenProperty.value.end,\n\t\t\t\t\t\t`'${path}'`\n\t\t\t\t\t);\n\n\t\t\t\t\tdelete paths[type];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Object.keys(paths).length > 0) {\n\t\t\t\ts.appendRight(\n\t\t\t\t\tneedsComma ? lastProperty.end : lastComma,\n\t\t\t\t\t`${needsComma ? ',' : ''}\\n${Object.entries(paths)\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t([key, value]) =>\n\t\t\t\t\t\t\t\t`\\t\\t${VALID_VARIABLE_NAME_REGEX.test(key) ? key : `'${key}'`}: '${value}'`\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(',\\n')}`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// handle object without the config key\n\t\tif (state.mainObjectExpression === null) return err(new ConfigObjectNotFoundError());\n\t\tconst code = s.slice(state.mainObjectExpression.start, state.mainObjectExpression.end);\n\t\tconst lastCommaIndex = code.lastIndexOf(',');\n\t\tconst lastComma =\n\t\t\tlastCommaIndex === -1 ? -1 : lastCommaIndex + state.mainObjectExpression.start + 1;\n\t\tif (state.mainObjectExpression.properties.length === 0) {\n\t\t\ts.appendRight(\n\t\t\t\tstate.mainObjectExpression.start + 1,\n\t\t\t\t`\\n\\tpaths: {\\n${Object.entries(paths)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]) =>\n\t\t\t\t\t\t\t`\\t\\t${VALID_VARIABLE_NAME_REGEX.test(key) ? key : `'${key}'`}: '${value}'`\n\t\t\t\t\t)\n\t\t\t\t\t.join(',\\n')}\\n\\t}`\n\t\t\t);\n\t\t} else {\n\t\t\tconst lastProperty =\n\t\t\t\tstate.mainObjectExpression.properties[\n\t\t\t\t\tstate.mainObjectExpression.properties.length - 1\n\t\t\t\t]!;\n\n\t\t\tlet needsComma = false;\n\t\t\tif (lastComma !== -1 && lastComma > lastProperty.end) {\n\t\t\t\tneedsComma = false;\n\t\t\t} else {\n\t\t\t\tneedsComma = true;\n\t\t\t}\n\n\t\t\ts.appendRight(\n\t\t\t\tneedsComma ? lastProperty.end : lastComma,\n\t\t\t\t`${needsComma ? ',' : ''}\\n\\tpaths: {\\n${Object.entries(paths)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t([key, value]) =>\n\t\t\t\t\t\t\t`\\t\\t${VALID_VARIABLE_NAME_REGEX.test(key) ? key : `'${key}'`}: '${value}'`\n\t\t\t\t\t)\n\t\t\t\t\t.join(',\\n')}\\n\\t}`\n\t\t\t);\n\t\t}\n\t}\n\n\treturn ok(s.toString());\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/config/utils.ts",
    "content": "import { cancel, confirm, isCancel } from '@clack/prompts';\nimport { err, ok, ResultAsync } from 'nevereverthrow';\nimport path from 'pathe';\nimport { createConfigLoader } from 'unconfig';\nimport type { AbsolutePath } from '@/api/utils';\nimport type { Config } from '@/utils/config';\nimport { ConfigNotFoundError, FailedToLoadConfigError } from '@/utils/errors';\nimport { createPathsMatcher, type PathsMatcher, tryGetTsconfig } from '@/utils/tsconfig';\n\n/**\n * Regex to match valid JS variable names\n */\nexport const VALID_VARIABLE_NAME_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n\nfunction _createConfigLoader({ cwd }: { cwd: string }) {\n\treturn createConfigLoader<Config>({\n\t\tsources: [\n\t\t\t{\n\t\t\t\tfiles: ['jsrepo.config'],\n\t\t\t\textensions: ['ts', 'js', 'mts', 'mjs'],\n\t\t\t},\n\t\t],\n\t\tmerge: false,\n\t\tcwd,\n\t});\n}\n\n/**\n * Attempts to load the config. Returns an error if the config is not found.\n * @returns\n */\nexport function loadConfig({\n\tcwd,\n}: {\n\tcwd: string;\n}): ResultAsync<Config, ConfigNotFoundError | FailedToLoadConfigError> {\n\treturn loadConfigOptional({ cwd }).andThen((v) => {\n\t\tif (v === null) return err(new ConfigNotFoundError(cwd));\n\t\treturn ok(v);\n\t});\n}\n\n/**\n * Attempts to load the config. Will return null if the config is not found.\n * @returns\n */\nexport function loadConfigOptional({\n\tcwd,\n}: {\n\tcwd: string;\n}): ResultAsync<Config | null, FailedToLoadConfigError> {\n\treturn ResultAsync.fromPromise(\n\t\t(async () => {\n\t\t\tconst loadResult = await _createConfigLoader({ cwd }).load();\n\t\t\tif (loadResult.sources.length === 0) return null;\n\n\t\t\treturn loadResult.config;\n\t\t})(),\n\t\t(err) => new FailedToLoadConfigError(err)\n\t);\n}\n\n/**\n * Searches for a a config file in the current directory (`jsrepo.config.(ts|js|mts|mjs)`)  it will search directories above the cwd until it finds a config file or reaches the user's home directory or root.\n *\n * @param cwd - The current working directory.\n * @param promptForContinueIfNull - Whether to prompt the user to continue if no config file is found.\n * @returns\n */\nexport async function loadConfigSearch({\n\tcwd,\n\tpromptForContinueIfNull,\n}: {\n\tcwd: AbsolutePath;\n\tpromptForContinueIfNull: boolean;\n}): Promise<{ config: Config; path: AbsolutePath } | null> {\n\t// search all supported config names and return the first one that exists\n\tconst loadResult = await _createConfigLoader({ cwd }).load();\n\n\tif (loadResult.sources.length > 0) {\n\t\treturn { config: loadResult.config, path: loadResult.sources[0]! as AbsolutePath };\n\t}\n\n\tlet shouldContinue = !promptForContinueIfNull;\n\n\tif (!shouldContinue) {\n\t\tconst response = await confirm({\n\t\t\tmessage: `You don't have jsrepo initialized in your project. Do you want to continue?`,\n\t\t\tinitialValue: false,\n\t\t});\n\n\t\tif (isCancel(response)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tshouldContinue = response;\n\t}\n\n\tif (!shouldContinue) {\n\t\tcancel('Canceled!');\n\t\tprocess.exit(0);\n\t}\n\n\treturn null;\n}\n\nexport function getPathsMatcher({ cwd }: { cwd: AbsolutePath }): PathsMatcher {\n\tconst tsConfig = tryGetTsconfig(cwd).unwrapOr(null);\n\treturn tsConfig ? createPathsMatcher(tsConfig, { cwd }) : null;\n}\n\n/** Resolves the paths relative to the cwd */\nexport function resolvePaths(\n\tpaths: Config['paths'],\n\t{ cwd, matcher }: { cwd: string; matcher: PathsMatcher }\n): Config['paths'] {\n\tconst newPaths: Config['paths'] = {};\n\n\tfor (const [type, p] of Object.entries(paths)) {\n\t\tif (!p || p.trim() === '') continue;\n\t\tnewPaths[type] = resolvePath(p, { cwd, matcher });\n\t}\n\n\treturn newPaths;\n}\n\nexport function resolvePath(\n\tp: string,\n\t{ cwd, matcher }: { cwd: string; matcher: PathsMatcher }\n): string {\n\t// don't resolve relative paths\n\tif (p.startsWith('./')) return p;\n\n\tif (matcher === null) {\n\t\treturn path.relative(cwd, path.join(path.resolve(cwd), p));\n\t}\n\n\tconst matchedPaths = matcher(p);\n\tconst resolved = matchedPaths.length > 0 ? path.relative(cwd, matchedPaths[0]!) : undefined;\n\n\tif (!resolved) {\n\t\treturn path.relative(cwd, path.join(path.resolve(cwd), p));\n\t}\n\n\treturn resolved;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/diff.ts",
    "content": "import { type Change, diffChars } from 'diff';\nimport pc from 'picocolors';\nimport * as lines from '@/utils/lines';\nimport { leftPadMin } from '@/utils/pad';\n\ntype Options = {\n\t/** The source file */\n\tfrom: string;\n\t/** The destination file */\n\tto: string;\n\t/** The changes to the file */\n\tchanges: Change[];\n\t/** Expands all lines to show the entire file */\n\texpand: boolean;\n\t/** Maximum lines to show before collapsing */\n\tmaxUnchanged: number;\n\t/** Color the removed lines */\n\tcolorRemoved?: (line: string) => string;\n\t/** Color the added lines */\n\tcolorAdded?: (line: string) => string;\n\t/** Color the removed chars */\n\tcolorCharsRemoved?: (line: string) => string;\n\t/** Color the added chars */\n\tcolorCharsAdded?: (line: string) => string;\n\t/** Prefixes each line with the string returned from this function. */\n\tprefix: () => string;\n\tintro: (options: Options) => string;\n\tonUnchanged: (options: Options) => string;\n};\n\n/** Check if a character is whitespace\n *\n * @param str\n * @returns\n */\nfunction isWhitespace(str: string) {\n\treturn /^\\s+$/g.test(str);\n}\n\n/** We need to add a newline at the end of each change to make sure\n * the next change can start correctly. So we take off just 1.\n *\n * @param str\n * @returns\n */\nfunction trimSingleNewLine(str: string): string {\n\tif (str.length === 0) return str;\n\tlet i = str.length - 1;\n\twhile (str[i] !== undefined && isWhitespace(str[i]!) && i >= 0) {\n\t\tif (str[i] === '\\n') {\n\t\t\tif (str[i - 1] === '\\r') {\n\t\t\t\treturn str.slice(0, i - 1);\n\t\t\t}\n\n\t\t\treturn str.slice(0, i);\n\t\t}\n\n\t\ti--;\n\t}\n\n\treturn str;\n}\n\nexport type DiffResult =\n\t| {\n\t\t\ttype: 'unchanged';\n\t  }\n\t| {\n\t\t\ttype: 'changed';\n\t\t\tdiff: string;\n\t  };\n\nfunction formatDiff({\n\tfrom,\n\tto,\n\tchanges,\n\texpand = false,\n\tmaxUnchanged = 5,\n\tcolorRemoved = pc.redBright,\n\tcolorAdded = pc.greenBright,\n\tcolorCharsRemoved = pc.bgRedBright,\n\tcolorCharsAdded = pc.bgGreenBright,\n\tprefix,\n\tonUnchanged,\n\tintro,\n}: Options): DiffResult {\n\tlet result = '';\n\n\tconst length =\n\t\tchanges.reduce((acc, change) => acc + (change.count ?? 0), 0).toString().length + 1;\n\n\tlet lineOffset = 0;\n\n\tif (changes.length === 1 && !changes[0]!.added && !changes[0]!.removed) {\n\t\treturn {\n\t\t\ttype: 'unchanged',\n\t\t};\n\t}\n\n\tresult += intro({\n\t\tfrom,\n\t\tto,\n\t\tchanges,\n\t\texpand,\n\t\tmaxUnchanged,\n\t\tcolorAdded,\n\t\tcolorRemoved,\n\t\tprefix,\n\t\tonUnchanged,\n\t\tintro,\n\t});\n\n\t/** Provides the line number prefix */\n\tconst linePrefix = (line: number): string =>\n\t\tpc.gray(`${prefix?.() ?? ''}${leftPadMin(`${line + 1 + lineOffset} `, length)} `);\n\n\tfor (let i = 0; i < changes.length; i++) {\n\t\tconst change = changes[i]!;\n\n\t\tconst hasPreviousChange = changes[i - 1]?.added || changes[i - 1]?.removed;\n\t\tconst hasNextChange = changes[i + 1]?.added || changes[i + 1]?.removed;\n\n\t\tif (!change.added && !change.removed) {\n\t\t\t// show collapsed\n\t\t\tif (!expand && change.count !== undefined && change.count > maxUnchanged) {\n\t\t\t\tconst prevLineOffset = lineOffset;\n\t\t\t\tconst ls = lines.get(trimSingleNewLine(change.value));\n\n\t\t\t\tlet shownLines = 0;\n\n\t\t\t\tif (hasNextChange) shownLines += maxUnchanged;\n\t\t\t\tif (hasPreviousChange) shownLines += maxUnchanged;\n\n\t\t\t\t// just show all if we are going to show more than we have\n\t\t\t\tif (shownLines >= ls.length) {\n\t\t\t\t\tresult += `${lines.join(ls, {\n\t\t\t\t\t\tprefix: linePrefix,\n\t\t\t\t\t})}\\n`;\n\t\t\t\t\tlineOffset += ls.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// this writes the top few lines\n\t\t\t\tif (hasPreviousChange) {\n\t\t\t\t\tresult += `${lines.join(ls.slice(0, maxUnchanged), {\n\t\t\t\t\t\tprefix: linePrefix,\n\t\t\t\t\t})}\\n`;\n\t\t\t\t}\n\n\t\t\t\tif (ls.length > shownLines) {\n\t\t\t\t\tconst count = ls.length - shownLines;\n\t\t\t\t\tresult += `${lines.join(\n\t\t\t\t\t\tlines.get(\n\t\t\t\t\t\t\tpc.gray(`+ ${count} more unchanged (${pc.italic('-E to expand')})`)\n\t\t\t\t\t\t),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprefix: () => `${prefix?.() ?? ''}${leftPadMin(' ', length)} `,\n\t\t\t\t\t\t}\n\t\t\t\t\t)}\\n`;\n\t\t\t\t}\n\n\t\t\t\tif (hasNextChange) {\n\t\t\t\t\tlineOffset = lineOffset + ls.length - maxUnchanged;\n\t\t\t\t\tresult += `${lines.join(ls.slice(ls.length - maxUnchanged), {\n\t\t\t\t\t\tprefix: linePrefix,\n\t\t\t\t\t})}\\n`;\n\t\t\t\t}\n\n\t\t\t\t// resets the line offset\n\t\t\t\tlineOffset = prevLineOffset + change.count;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// show expanded\n\t\t\tresult += `${lines.join(lines.get(trimSingleNewLine(change.value)), {\n\t\t\t\tprefix: linePrefix,\n\t\t\t})}\\n`;\n\t\t\tlineOffset += change.count ?? 0;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst colorLineChange = (change: Change) => {\n\t\t\tif (change.added) {\n\t\t\t\treturn colorAdded(trimSingleNewLine(change.value));\n\t\t\t}\n\n\t\t\tif (change.removed) {\n\t\t\t\treturn colorRemoved(trimSingleNewLine(change.value));\n\t\t\t}\n\n\t\t\treturn change.value;\n\t\t};\n\n\t\tconst colorCharChange = (change: Change) => {\n\t\t\tif (change.added) {\n\t\t\t\treturn colorCharsAdded(trimSingleNewLine(change.value));\n\t\t\t}\n\n\t\t\tif (change.removed) {\n\t\t\t\treturn colorCharsRemoved(trimSingleNewLine(change.value));\n\t\t\t}\n\n\t\t\treturn change.value;\n\t\t};\n\n\t\tif (\n\t\t\tchange.removed &&\n\t\t\tchange.count === 1 &&\n\t\t\tchanges[i + 1]?.added &&\n\t\t\tchanges[i + 1]?.count === 1\n\t\t) {\n\t\t\t// single line change\n\t\t\tconst diffedChars = diffChars(change.value, changes[i + 1]!.value);\n\n\t\t\tconst sentence = diffedChars.map((chg) => colorCharChange(chg)).join('');\n\n\t\t\tresult += `${linePrefix(0)}${sentence}`;\n\n\t\t\tlineOffset += 1;\n\n\t\t\ti++;\n\t\t} else {\n\t\t\tif (isWhitespace(change.value)) {\n\t\t\t\t// adds some spaces to make sure that you can see the change\n\t\t\t\tresult += `${lines.join(lines.get(colorCharChange(change)), {\n\t\t\t\t\tprefix: (line) =>\n\t\t\t\t\t\t`${linePrefix(line)}${colorCharChange({\n\t\t\t\t\t\t\tremoved: true,\n\t\t\t\t\t\t\tvalue: '   ',\n\t\t\t\t\t\t\tadded: false,\n\t\t\t\t\t\t\tcount: 1,\n\t\t\t\t\t\t})}`,\n\t\t\t\t})}\\n`;\n\n\t\t\t\tif (!change.removed) {\n\t\t\t\t\tlineOffset += change.count ?? 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult += `${lines.join(lines.get(colorLineChange(change)), {\n\t\t\t\t\tprefix: linePrefix,\n\t\t\t\t})}\\n`;\n\n\t\t\t\tif (!change.removed) {\n\t\t\t\t\tlineOffset += change.count ?? 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!result.endsWith('\\n')) {\n\t\tresult = result += '\\n';\n\t}\n\n\treturn {\n\t\ttype: 'changed',\n\t\tdiff: result,\n\t};\n}\n\nexport { formatDiff };\n"
  },
  {
    "path": "packages/jsrepo/src/utils/env.ts",
    "content": "import { existsSync, readFileSync } from './fs';\nimport { dirname, joinAbsolute } from './path';\nimport type { AbsolutePath } from './types';\n\nexport const ENV_FILE_NAMES = [\n\t'.env.local',\n\t'.env',\n\t'.env.development.local',\n\t'.env.development',\n] as const;\n\nexport function searchForEnvFile(\n\tcwd: AbsolutePath\n): { path: AbsolutePath; envVars: Record<string, string>; contents: string } | null {\n\tfor (const envFileName of ENV_FILE_NAMES) {\n\t\tconst envFilePath = joinAbsolute(cwd, envFileName);\n\t\tif (!existsSync(envFilePath)) continue;\n\n\t\tconst contentsResult = readFileSync(envFilePath);\n\t\tif (contentsResult.isErr()) continue;\n\t\tconst contents = contentsResult.value;\n\n\t\tconst envVars = parseEnvVariables(contents);\n\t\treturn { path: envFilePath, envVars, contents };\n\t}\n\tconst newCwd = dirname(cwd);\n\tif (newCwd === cwd) return null;\n\treturn searchForEnvFile(newCwd);\n}\n\nexport function parseEnvVariables(contents: string): Record<string, string> {\n\tconst lines = contents.split('\\n');\n\tconst result: Record<string, string> = {};\n\tlet i = 0;\n\n\twhile (i < lines.length) {\n\t\tconst line = lines[i]!;\n\t\tconst trimmedLine = line.trim();\n\n\t\t// Skip empty lines\n\t\tif (trimmedLine.length === 0) {\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip comment lines (lines starting with #)\n\t\tif (trimmedLine.startsWith('#')) {\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst firstEqualIndex = trimmedLine.indexOf('=');\n\t\tif (firstEqualIndex === -1) {\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst name = trimmedLine.slice(0, firstEqualIndex).trim();\n\t\tlet value = trimmedLine.slice(firstEqualIndex + 1);\n\n\t\t// Check if this is a multi-line value (starts with quote but doesn't end with quote on same line)\n\t\tif (value.startsWith('\"') && !value.endsWith('\"')) {\n\t\t\t// Multi-line value - accumulate lines until we find the closing quote\n\t\t\tconst valueLines: string[] = [value];\n\t\t\ti++;\n\n\t\t\twhile (i < lines.length) {\n\t\t\t\tconst nextLine = lines[i]!;\n\t\t\t\tvalueLines.push(nextLine);\n\n\t\t\t\t// Check if this line ends with a closing quote\n\t\t\t\tif (nextLine.trim().endsWith('\"')) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t}\n\n\t\t\t// Join all lines, preserve newlines, and strip quotes\n\t\t\tconst joined = valueLines.join('\\n');\n\t\t\t// Remove opening quote from first line and closing quote from last line\n\t\t\tif (joined.startsWith('\"') && joined.endsWith('\"')) {\n\t\t\t\tvalue = `${joined.slice(1, -1)}\\n`;\n\t\t\t} else {\n\t\t\t\tvalue = joined;\n\t\t\t}\n\t\t} else {\n\t\t\t// Single-line value - handle comments\n\t\t\t// If value is quoted, find the closing quote first, then strip comments after it\n\t\t\tif (value.startsWith('\"') && value.endsWith('\"')) {\n\t\t\t\t// Quoted single-line value - comments can appear after the closing quote\n\t\t\t\tconst closingQuoteIndex = value.lastIndexOf('\"');\n\t\t\t\tif (closingQuoteIndex !== -1 && closingQuoteIndex < value.length - 1) {\n\t\t\t\t\t// There's content after the closing quote - check for comment\n\t\t\t\t\tconst afterQuote = value.slice(closingQuoteIndex + 1);\n\t\t\t\t\tconst commentIndex = afterQuote.indexOf('#');\n\t\t\t\t\tif (commentIndex !== -1) {\n\t\t\t\t\t\t// Strip comment and any whitespace before it\n\t\t\t\t\t\tvalue = value.slice(0, closingQuoteIndex + 1 + commentIndex).trimEnd();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Unquoted value - find first # and strip everything after it (and whitespace before it)\n\t\t\t\tconst commentIndex = value.indexOf('#');\n\t\t\t\tif (commentIndex !== -1) {\n\t\t\t\t\tvalue = value.slice(0, commentIndex).trimEnd();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tresult[name] = value ?? '';\n\t\ti++;\n\t}\n\n\treturn result;\n}\n\nexport function updateEnvFile(contents: string, envVars: Record<string, string>): string {\n\tconst lines = contents.split('\\n');\n\tconst parsedEnvVars = parseEnvVariables(contents);\n\tlet totalEnvVars = Object.keys(parsedEnvVars).length;\n\n\tfor (const [name, value] of Object.entries(envVars)) {\n\t\tif (parsedEnvVars[name] === undefined) {\n\t\t\tcontents += `${totalEnvVars === 0 ? '' : '\\n'}${name}=${value}`;\n\t\t\ttotalEnvVars++;\n\t\t} else {\n\t\t\tif (value === '') continue;\n\n\t\t\t// Find the variable in the original content\n\t\t\tlet found = false;\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\tconst line = lines[i]!;\n\t\t\t\tconst lineTrimmed = line.trim();\n\t\t\t\tif (lineTrimmed.startsWith(`${name}=`)) {\n\t\t\t\t\t// Found the start of the variable\n\t\t\t\t\tconst startIndex = contents.indexOf(line);\n\n\t\t\t\t\t// Check if it's a multi-line value\n\t\t\t\t\tconst valuePart = lineTrimmed.slice(name.length + 1);\n\t\t\t\t\tlet endIndex = startIndex + line.length;\n\n\t\t\t\t\tif (valuePart.startsWith('\"') && !valuePart.endsWith('\"')) {\n\t\t\t\t\t\t// Multi-line value - find the end\n\t\t\t\t\t\tlet lineIndex = i + 1;\n\t\t\t\t\t\twhile (lineIndex < lines.length) {\n\t\t\t\t\t\t\tendIndex += lines[lineIndex]!.length + 1; // +1 for newline\n\t\t\t\t\t\t\tif (lines[lineIndex]!.trim().endsWith('\"')) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlineIndex++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Replace the entire variable definition\n\t\t\t\t\tconst before = contents.slice(0, startIndex);\n\t\t\t\t\tconst after = contents.slice(endIndex);\n\n\t\t\t\t\t// Determine if we should use quotes (preserve original format if it was quoted)\n\t\t\t\t\tconst wasQuoted = valuePart.startsWith('\"');\n\t\t\t\t\tconst newValue = wasQuoted ? `\"${value}\"` : value;\n\n\t\t\t\t\tcontents = `${before}${name}=${newValue}${after}`;\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!found) {\n\t\t\t\t// Fallback to simple replace if we couldn't find it\n\t\t\t\tcontents = contents.replace(`${name}=${parsedEnvVars[name]}`, `${name}=${value}`);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn contents;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/errors.ts",
    "content": "import type { Pattern } from 'fast-glob';\nimport pc from 'picocolors';\nimport type { z } from 'zod';\nimport type { RegistryItemType } from './config';\n\nexport type CLIError =\n\t| JsrepoError\n\t| NoPackageJsonFoundError\n\t| ConfigNotFoundError\n\t| InvalidRegistryError\n\t| RegistryItemNotFoundError\n\t| ManifestFetchError\n\t| RegistryItemFetchError\n\t| RegistryFileFetchError\n\t| RegistryNotProvidedError\n\t| MultipleRegistriesError\n\t| AlreadyInitializedError\n\t| FailedToLoadConfigError\n\t| ConfigNotFoundError\n\t| InvalidOptionsError\n\t| NoRegistriesError\n\t| NoOutputsError\n\t| NoPathProvidedError\n\t| NoListedItemsError\n\t| DuplicateItemNameError\n\t| SelfReferenceError\n\t| NoFilesError\n\t| IllegalItemNameError\n\t| InvalidRegistryDependencyError\n\t| DuplicateFileReferenceError\n\t| FileNotFoundError\n\t| ImportedFileNotResolvedError\n\t| InvalidPluginError\n\t| InvalidKeyTypeError\n\t| ConfigObjectNotFoundError\n\t| CouldNotFindJsrepoImportError\n\t| ZodError\n\t| InvalidJSONError\n\t| NoProviderFoundError\n\t| MissingPeerDependencyError\n\t| InvalidRegistryNameError\n\t| InvalidRegistryVersionError\n\t| Unreachable;\n\nexport class JsrepoError extends Error {\n\tprivate readonly suggestion: string;\n\tprivate readonly docsLink?: string;\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tsuggestion: string;\n\t\t\tdocsLink?: string;\n\t\t}\n\t) {\n\t\tsuper(message);\n\n\t\tthis.suggestion = options.suggestion;\n\t\tthis.docsLink = options.docsLink;\n\t}\n\n\ttoString() {\n\t\treturn `${this.message} ${this.suggestion}${pc.gray(this.docsLink ? `\\n   See: ${this.docsLink}` : '')}`;\n\t}\n}\n\nexport class NoPackageJsonFoundError extends JsrepoError {\n\tconstructor() {\n\t\tsuper('No package.json found.', {\n\t\t\tsuggestion:\n\t\t\t\t'Please run create a package.json first before initializing a jsrepo project.',\n\t\t});\n\t}\n}\n\nexport class InvalidRegistryError extends JsrepoError {\n\tconstructor(registry: string) {\n\t\tsuper(\n\t\t\t`Invalid registry: ${pc.bold(registry)} A provider for this registry was not found.`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Maybe you need to add a provider for this registry?',\n\t\t\t\tdocsLink: 'See: https://jsrepo.dev/docs/providers',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class RegistryItemNotFoundError extends JsrepoError {\n\tconstructor(itemName: string, registry?: string) {\n\t\tsuper(\n\t\t\t`${pc.bold(itemName)} not found in ${pc.bold(registry ? `${registry}` : 'any registry')}.`,\n\t\t\t{\n\t\t\t\tsuggestion: registry\n\t\t\t\t\t? `Run ${pc.bold(`\\`jsrepo add --registry ${registry}\\``)} to list all items in this registry`\n\t\t\t\t\t: `Run ${pc.bold(`jsrepo add`)} to list all available registry items.`,\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class ProviderFetchError extends JsrepoError {\n\treadonly resourcePath: string;\n\treadonly originalMessage: string;\n\tconstructor(message: string, resourcePath: string) {\n\t\tsuper(`Error fetching ${resourcePath}: ${message}`, {\n\t\t\tsuggestion: 'Please try again.',\n\t\t});\n\t\tthis.resourcePath = resourcePath;\n\t\tthis.originalMessage = message;\n\t}\n}\n\nexport class ManifestFetchError extends JsrepoError {\n\tconstructor(error: unknown) {\n\t\tsuper(\n\t\t\terror instanceof ProviderFetchError\n\t\t\t\t? `Error fetching manifest file from ${pc.bold(error.resourcePath)}: ${error.originalMessage}`\n\t\t\t\t: error instanceof ManifestFetchError\n\t\t\t\t\t? error.message\n\t\t\t\t\t: `Error fetching manifest file: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class RegistryItemFetchError extends JsrepoError {\n\tconstructor(error: unknown, options: { registry: string; item: string }) {\n\t\tsuper(\n\t\t\terror instanceof ProviderFetchError\n\t\t\t\t? `Error fetching ${pc.bold(`${options.registry}/${options.item}`)} from ${pc.bold(\n\t\t\t\t\t\terror.resourcePath\n\t\t\t\t\t)}: ${error.originalMessage}`\n\t\t\t\t: `Error fetching ${options.registry}/${options.item}: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class RegistryFileFetchError extends JsrepoError {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: { registry: string; item: string; resourcePath: string | undefined }\n\t) {\n\t\tsuper(\n\t\t\t`Error fetching ${pc.bold(`${options.registry}/${options.item}`)}${\n\t\t\t\toptions.resourcePath ? ` from ${pc.bold(options.resourcePath)}` : ''\n\t\t\t}: ${message}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class RegistryNotProvidedError extends JsrepoError {\n\tconstructor() {\n\t\tsuper('No registry was provided.', {\n\t\t\tsuggestion:\n\t\t\t\t'Fully qualify blocks ex: (github/ieedan/std/math) or supply them in your config with the `registries` key.',\n\t\t});\n\t}\n}\n\nexport class MultipleRegistriesError extends JsrepoError {\n\tconstructor(itemName: string, registries: string[]) {\n\t\tsuper(\n\t\t\t`${registries\n\t\t\t\t.map((r, i) => `${i === registries.length - 1 ? 'and ' : ''}${r}`)\n\t\t\t\t.join(', ')} contain ${pc.bold(itemName)}.`,\n\t\t\t{\n\t\t\t\tsuggestion: `You will have to be more specific by providing the registry url like: ${pc.bold(\n\t\t\t\t\t`\\`${registries[0]}/${itemName}\\``\n\t\t\t\t)}.`,\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class AlreadyInitializedError extends JsrepoError {\n\tconstructor() {\n\t\tsuper('Config already initialized.', {\n\t\t\tsuggestion: `To configure a new registry run ${pc.bold(`\\`jsrepo init <registry>\\``)}.`,\n\t\t});\n\t}\n}\n\nexport class FailedToLoadConfigError extends JsrepoError {\n\tconstructor(cause: unknown) {\n\t\tsuper(`Failed to load config: ${cause instanceof Error ? cause.message : String(cause)}`, {\n\t\t\tsuggestion: 'Please try again.',\n\t\t});\n\t}\n}\n\nexport class ConfigNotFoundError extends JsrepoError {\n\tconstructor(path: string) {\n\t\tsuper(`Config not found at ${pc.bold(path)}.`, {\n\t\t\tsuggestion: 'Please run `jsrepo init` to create a config file.',\n\t\t\tdocsLink: 'https://jsrepo.dev/docs/jsrepo-config',\n\t\t});\n\t}\n}\n\nexport class InvalidOptionsError extends JsrepoError {\n\tconstructor(error: z.ZodError) {\n\t\tsuper(\n\t\t\t`Invalid options: ${error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join(', ')}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please check the options and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class NoRegistriesError extends JsrepoError {\n\tconstructor() {\n\t\tsuper('No registries were found in the config.', {\n\t\t\tsuggestion: 'Please define at least one registry using the `registry` key.',\n\t\t});\n\t}\n}\n\nexport class NoPathProvidedError extends JsrepoError {\n\tconstructor({ item, type }: { item: string; type: string }) {\n\t\tsuper(`No path was provided for ${pc.bold(item)} of type ${pc.bold(type)}.`, {\n\t\t\tsuggestion: 'Please configure a path with the `paths` key.',\n\t\t\tdocsLink: 'https://jsrepo.dev/docs/jsrepo-config#paths',\n\t\t});\n\t}\n}\n\nexport class BuildError extends JsrepoError {\n\treadonly registryName: string;\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: { registryName: string; suggestion: string; docsLink?: string }\n\t) {\n\t\tsuper(message, {\n\t\t\tsuggestion: options.suggestion,\n\t\t\tdocsLink: options.docsLink,\n\t\t});\n\t\tthis.registryName = options.registryName;\n\t}\n}\n\nexport class ModuleNotFoundError extends JsrepoError {\n\tconstructor(mod: string, { fileName }: { fileName: string }) {\n\t\tsuper(`Module referenced by ${pc.bold(fileName)}: ${pc.bold(mod)} not found.`, {\n\t\t\tsuggestion: 'Please ensure the module exists.',\n\t\t});\n\t}\n}\n\nexport class NoOutputsError extends BuildError {\n\tconstructor({ registryName }: { registryName: string }) {\n\t\tsuper(`No outputs were defined in the registry ${pc.bold(registryName)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please define at least one output using the `registry.outputs` key.',\n\t\t\tdocsLink: 'https://jsrepo.dev/docs/outputs',\n\t\t});\n\t}\n}\n\nexport class NoListedItemsError extends BuildError {\n\tconstructor({ registryName }: { registryName: string }) {\n\t\tsuper(`No items were listed in the registry ${pc.bold(registryName)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please list at least one item using the `items` key.',\n\t\t});\n\t}\n}\n\nexport class DuplicateItemNameError extends BuildError {\n\tconstructor({ name, registryName }: { name: string; registryName: string }) {\n\t\tsuper(`Duplicate item name: ${pc.bold(name)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please ensure each item has a unique name.',\n\t\t});\n\t}\n}\n\nexport class SelfReferenceError extends BuildError {\n\tconstructor({ name, registryName }: { name: string; registryName: string }) {\n\t\tsuper(`Self reference: ${pc.bold(name)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please ensure each item does not reference itself.',\n\t\t});\n\t}\n}\n\nexport class NoFilesError extends BuildError {\n\tconstructor({ name, registryName }: { name: string; registryName: string }) {\n\t\tsuper(`No files were listed in the item ${pc.bold(name)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please list at least one file using the `files` key.',\n\t\t});\n\t}\n}\n\nexport class IllegalItemNameError extends BuildError {\n\tconstructor({ name, registryName }: { name: string; registryName: string }) {\n\t\tsuper(`Illegal item name: ${pc.bold(name)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please update the name of your item.',\n\t\t});\n\t}\n}\n\nexport class InvalidRegistryDependencyError extends BuildError {\n\tconstructor({\n\t\tdependency,\n\t\titem,\n\t\tregistryName,\n\t}: { dependency: string; item: string; registryName: string }) {\n\t\tsuper(`Invalid registry dependency: ${pc.bold(dependency)} for item ${pc.bold(item)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please ensure the dependency is a valid item in the registry.',\n\t\t});\n\t}\n}\n\nexport class InvalidDependencyError extends BuildError {\n\tconstructor({\n\t\tdependency,\n\t\tregistryName,\n\t\titemName,\n\t}: {\n\t\tdependency: string;\n\t\tregistryName: string;\n\t\titemName: string;\n\t}) {\n\t\tsuper(`Invalid dependency: ${pc.bold(dependency)} for ${pc.bold(itemName)}.`, {\n\t\t\tregistryName,\n\t\t\tsuggestion: 'Please ensure the dependency is a valid npm package name.',\n\t\t});\n\t}\n}\n\nexport class DuplicateFileReferenceError extends BuildError {\n\tconstructor({\n\t\tpath,\n\t\tparent,\n\t\tduplicateParent,\n\t\tregistryName,\n\t}: {\n\t\tpath: string;\n\t\tparent: { name: string; type: RegistryItemType };\n\t\tduplicateParent: { name: string; type: RegistryItemType };\n\t\tregistryName: string;\n\t}) {\n\t\tsuper(\n\t\t\t`Duplicate file reference: ${pc.bold(path)} is in both ${pc.bold(\n\t\t\t\t`${parent.type}/${parent.name}`\n\t\t\t)} and ${pc.bold(`${duplicateParent.type}/${duplicateParent.name}`)}.`,\n\t\t\t{\n\t\t\t\tregistryName,\n\t\t\t\tsuggestion: 'Please ensure the file is not referenced multiple times.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class FileNotFoundError extends BuildError {\n\tconstructor({\n\t\tpath,\n\t\tparent,\n\t\tregistryName,\n\t}: {\n\t\tpath: string;\n\t\tparent: { name: string; type: RegistryItemType };\n\t\tregistryName: string;\n\t}) {\n\t\tsuper(\n\t\t\t`File not found: ${pc.bold(path)} in ${pc.bold(parent.name)} of type ${pc.bold(parent.type)}.`,\n\t\t\t{\n\t\t\t\tregistryName,\n\t\t\t\tsuggestion: 'Please ensure the file exists.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class ImportedFileNotResolvedError extends BuildError {\n\tconstructor({\n\t\treferencedFile,\n\t\tfileName,\n\t\titem,\n\t\tregistryName,\n\t}: {\n\t\treferencedFile: string;\n\t\tfileName: string;\n\t\titem: string;\n\t\tregistryName: string;\n\t}) {\n\t\tsuper(\n\t\t\t`Imported file not resolved: ${pc.bold(referencedFile)} in ${pc.bold(fileName)} of item ${pc.bold(item)}.`,\n\t\t\t{\n\t\t\t\tregistryName,\n\t\t\t\tsuggestion: 'Make sure the file exists and is part of the registry.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class InvalidPluginError extends JsrepoError {\n\tconstructor(plugin: string) {\n\t\tsuper(`Invalid plugin name: ${pc.bold(plugin)} is not a valid npm package name.`, {\n\t\t\tsuggestion: 'Double check the plugin name and try again.',\n\t\t});\n\t}\n}\n\nexport class InvalidKeyTypeError extends JsrepoError {\n\tconstructor({ key, type }: { key: string; type: 'object' | 'array' }) {\n\t\tsuper(`The ${pc.bold(key)} key in your config must be an ${pc.bold(type)}.`, {\n\t\t\tsuggestion: 'Please check your config and try again.',\n\t\t});\n\t}\n}\n\nexport class ConfigObjectNotFoundError extends JsrepoError {\n\tconstructor() {\n\t\tsuper(\"We couldn't locate the config object in your config.\", {\n\t\t\tsuggestion: 'Please check your config and try again.',\n\t\t});\n\t}\n}\n\nexport class CouldNotFindJsrepoImportError extends JsrepoError {\n\tconstructor() {\n\t\tsuper(\"We couldn't find the jsrepo import in your config.\", {\n\t\t\tsuggestion: 'Please check your config and try again.',\n\t\t});\n\t}\n}\n\nexport class ZodError extends JsrepoError {\n\treadonly zodError: z.ZodError;\n\tconstructor(error: z.ZodError) {\n\t\tsuper(`Zod error: ${error.message}`, {\n\t\t\tsuggestion: 'Check the input schema and try again.',\n\t\t});\n\t\tthis.zodError = error;\n\t}\n}\n\nexport class InvalidJSONError extends JsrepoError {\n\tconstructor(error: unknown) {\n\t\tsuper(\n\t\t\t`Invalid JSON: ${error instanceof Error ? (error.stack ?? error.message) : `${error}`}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Check the input JSON and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class GlobError extends BuildError {\n\tconstructor(error: unknown, pattern: Pattern, registryName: string) {\n\t\tsuper(\n\t\t\t`Error while parsing glob pattern ${pc.bold(pattern)}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{\n\t\t\t\tregistryName,\n\t\t\t\tsuggestion: 'Please check the glob pattern and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class NoProviderFoundError extends JsrepoError {\n\tconstructor(provider: string) {\n\t\tsuper(\n\t\t\t`No provider found for ${pc.bold(provider)}. Maybe you need to add it to your config?`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please check the provider name and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class NoItemsToUpdateError extends JsrepoError {\n\tconstructor() {\n\t\tsuper(\"We couldn't find any items to update.\", {\n\t\t\tsuggestion:\n\t\t\t\t'Add items to your project using `jsrepo add` and then run `jsrepo update` to update them.',\n\t\t});\n\t}\n}\n\nexport class MissingPeerDependencyError extends JsrepoError {\n\tconstructor(packageName: string, feature: string) {\n\t\tsuper(`${pc.bold(packageName)} is required for ${feature} to work.`, {\n\t\t\tsuggestion: `Please install it.`,\n\t\t});\n\t}\n}\n\nexport class InvalidRegistryNameError extends JsrepoError {\n\tconstructor(registryName: string) {\n\t\tsuper(\n\t\t\t`Invalid registry name: ${pc.bold(\n\t\t\t\tregistryName\n\t\t\t)} is not a valid name. The name should be provided as ${pc.bold(`\\`@<scope>/<registry>\\``)}.`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please check the registry name and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class InvalidRegistryVersionError extends JsrepoError {\n\tconstructor(registryVersion: string | undefined, registryName: string) {\n\t\tif (registryVersion === undefined) {\n\t\t\tsuper(`No version was provided for ${pc.bold(registryName)}.`, {\n\t\t\t\tsuggestion: `Please provide a version using the ${pc.bold(`\\`version\\``)} key.`,\n\t\t\t});\n\t\t} else {\n\t\t\tsuper(\n\t\t\t\t`Invalid version for ${pc.bold(registryName)}: ${pc.bold(\n\t\t\t\t\tregistryVersion\n\t\t\t\t)} is not a valid version. The version should be provided as ${pc.bold(\n\t\t\t\t\t`\\`<major>.<minor>.<patch>\\``\n\t\t\t\t)}.`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please check the registry version and try again.',\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * This error is thrown when a code path should be unreachable.\n */\nexport class Unreachable extends JsrepoError {\n\tconstructor() {\n\t\tsuper('This code path should be unreachable.', {\n\t\t\tsuggestion: 'This is a bug, please report it.',\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/fs.ts",
    "content": "import fs from 'node:fs';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport { JsrepoError } from './errors';\nimport { dirname } from './path';\nimport type { AbsolutePath } from './types';\n\nexport function readFileSync(p: AbsolutePath): Result<string, JsrepoError> {\n\ttry {\n\t\treturn ok(fs.readFileSync(p, 'utf-8'));\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to read file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\n/**\n * Write to a file. Automatically creates the directory recursively if it doesn't exist.\n *\n * @param p\n * @param data\n * @returns\n */\nexport function writeFileSync(p: AbsolutePath, data: string): Result<void, JsrepoError> {\n\ttry {\n\t\tconst res = mkdirSync(dirname(p) as AbsolutePath);\n\t\tif (res.isErr()) return err(res.error);\n\t\tfs.writeFileSync(p, data);\n\t\treturn ok();\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to write file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nexport function readdirSync(p: AbsolutePath): Result<string[], JsrepoError> {\n\ttry {\n\t\treturn ok(fs.readdirSync(p));\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to read directory ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nexport function mkdirSync(p: AbsolutePath): Result<void, JsrepoError> {\n\ttry {\n\t\tfs.mkdirSync(p, { recursive: true });\n\t\treturn ok();\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to create directory ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\n/**\n * Removes a file if it exists.\n *\n * @param p\n * @returns\n */\nexport function rmSync(p: AbsolutePath): Result<void, JsrepoError> {\n\ttry {\n\t\tif (!existsSync(p)) return ok();\n\t\tfs.rmSync(p);\n\t\treturn ok();\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to remove file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nexport function existsSync(p: AbsolutePath): boolean {\n\treturn fs.existsSync(p);\n}\n\nexport function statSync(p: AbsolutePath): Result<fs.Stats, JsrepoError> {\n\ttry {\n\t\treturn ok(fs.statSync(p));\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to stat file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/glob.ts",
    "content": "import fg, { type Pattern } from 'fast-glob';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport { GlobError } from '@/utils/errors';\n\n/**\n * A safe wrapper around the fast-glob function.\n *\n * @param source\n * @param options\n * @returns\n */\nexport async function glob(\n\tsource: Pattern,\n\toptions: fg.Options & { registryName: string }\n): Promise<Result<string[], GlobError>> {\n\ttry {\n\t\treturn ok(await fg(source, options));\n\t} catch (e) {\n\t\treturn err(new GlobError(e, source, options.registryName));\n\t}\n}\n\n/**\n * Gets the base directory for a glob pattern using fast-glob's generateTasks.\n * This is more reliable than manually parsing the pattern.\n *\n * @param pattern The glob pattern\n * @param options Options passed to fast-glob\n * @returns The base directory path (relative to cwd if cwd is provided)\n */\nexport function getGlobBaseDirectory(pattern: Pattern, options?: fg.Options): string {\n\ttry {\n\t\tconst tasks = fg.generateTasks(pattern, options);\n\t\t// generateTasks returns an array of tasks, each with a base property\n\t\t// For a single pattern, we'll get one task with the base directory\n\t\tif (tasks.length > 0 && tasks[0]) {\n\t\t\treturn tasks[0].base;\n\t\t}\n\t\t// Fallback to current directory if no tasks\n\t\treturn '.';\n\t} catch {\n\t\t// If generateTasks fails, fallback to current directory\n\t\treturn '.';\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/hooks.ts",
    "content": "import { confirm, intro, isCancel, log, outro } from '@clack/prompts';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport { x } from 'tinyexec';\nimport type { AddCommandResult, AddOptions } from '@/commands/add';\nimport type { AuthCommandResult, AuthOptions } from '@/commands/auth';\nimport type { BuildCommandResult, BuildOptions } from '@/commands/build';\nimport type {\n\tConfigAddLanguageCommandResult,\n\tConfigAddLanguageOptions,\n} from '@/commands/config/language';\nimport type { ConfigMcpCommandResult, ConfigMcpOptions } from '@/commands/config/mcp';\nimport type {\n\tConfigAddProviderCommandResult,\n\tConfigAddProviderOptions,\n} from '@/commands/config/provider';\nimport type {\n\tConfigAddTransformCommandResult,\n\tConfigAddTransformOptions,\n} from '@/commands/config/transform';\nimport type { InitCommandResult, InitOptions } from '@/commands/init';\nimport type { PublishCommandResult, PublishOptions } from '@/commands/publish';\nimport type { UpdateCommandResult, UpdateOptions } from '@/commands/update';\nimport type { Config } from '@/utils/config';\n\nexport type BeforeArgs =\n\t| { command: 'config.language'; options: ConfigAddLanguageOptions }\n\t| { command: 'config.mcp'; options: ConfigMcpOptions }\n\t| { command: 'config.provider'; options: ConfigAddProviderOptions }\n\t| { command: 'config.transform'; options: ConfigAddTransformOptions }\n\t| { command: 'add'; options: AddOptions }\n\t| { command: 'auth'; options: AuthOptions }\n\t| { command: 'build'; options: BuildOptions }\n\t| { command: 'init'; options: InitOptions }\n\t| { command: 'publish'; options: PublishOptions }\n\t| { command: 'update'; options: UpdateOptions };\n\nexport type AfterArgs =\n\t| {\n\t\t\tcommand: 'config.language';\n\t\t\toptions: ConfigAddLanguageOptions;\n\t\t\tresult: ConfigAddLanguageCommandResult;\n\t  }\n\t| { command: 'config.mcp'; options: ConfigMcpOptions; result: ConfigMcpCommandResult }\n\t| {\n\t\t\tcommand: 'config.provider';\n\t\t\toptions: ConfigAddProviderOptions;\n\t\t\tresult: ConfigAddProviderCommandResult;\n\t  }\n\t| {\n\t\t\tcommand: 'config.transform';\n\t\t\toptions: ConfigAddTransformOptions;\n\t\t\tresult: ConfigAddTransformCommandResult;\n\t  }\n\t| { command: 'add'; options: AddOptions; result: AddCommandResult }\n\t| { command: 'auth'; options: AuthOptions; result: AuthCommandResult }\n\t| { command: 'build'; options: BuildOptions; result: BuildCommandResult }\n\t| { command: 'init'; options: InitOptions; result: InitCommandResult }\n\t| { command: 'publish'; options: PublishOptions; result: PublishCommandResult }\n\t| { command: 'update'; options: UpdateOptions; result: UpdateCommandResult };\n\nexport type HookFn<Args> = (args: Args) => Promise<void>;\nexport type Hook<Args> = HookFn<Args> | string;\n\nexport type InferHookArgs<H> =\n\tH extends HookFn<infer Args> ? Args : H extends HookFn<infer Args>[] ? Args : never;\n\nexport type BeforeHook = Hook<BeforeArgs>;\nexport type AfterHook = Hook<AfterArgs>;\n\nasync function runCommand(\n\tcommand: string,\n\tcwd: string = process.cwd()\n): Promise<Result<void, Error>> {\n\ttry {\n\t\tconst isWindows = process.platform === 'win32';\n\t\tconst [shell, shellArgs] = isWindows\n\t\t\t? (['cmd', ['/c', command]] as [string, string[]])\n\t\t\t: (['sh', ['-c', command]] as [string, string[]]);\n\n\t\tconst proc = x(shell, [...shellArgs], {\n\t\t\tnodeOptions: { stdio: 'inherit', cwd },\n\t\t});\n\t\tawait proc;\n\t\tif (proc.exitCode !== 0) {\n\t\t\treturn err(new Error(`Command \"${command}\" failed with exit code ${proc.exitCode}`));\n\t\t}\n\t\treturn ok(undefined);\n\t} catch (e) {\n\t\treturn err(e instanceof Error ? e : new Error(String(e)));\n\t}\n}\n\nexport async function runHooks<HookKey extends keyof NonNullable<Config['hooks']>>(\n\tconfig: Config,\n\tkey: HookKey,\n\targs: InferHookArgs<NonNullable<NonNullable<Config['hooks']>[HookKey]>>,\n\tcwd?: string\n): Promise<Result<void, Error>> {\n\tconst hooks = config.hooks?.[key] ?? [];\n\tconst hooksArr = (Array.isArray(hooks) ? hooks : [hooks]) as (\n\t\t| HookFn<BeforeArgs | AfterArgs>\n\t\t| string\n\t)[];\n\n\tconst runCwd = cwd ?? process.cwd();\n\n\tfor (const hook of hooksArr) {\n\t\tif (typeof hook === 'function') {\n\t\t\ttry {\n\t\t\t\tawait (hook as HookFn<unknown>)(args);\n\t\t\t} catch (e) {\n\t\t\t\treturn err(e instanceof Error ? e : new Error(String(e)));\n\t\t\t}\n\t\t} else if (typeof hook === 'string') {\n\t\t\tconst result = await runCommand(hook, runCwd);\n\t\t\tif (result.isErr()) return result;\n\t\t}\n\t}\n\n\treturn ok(undefined);\n}\n\nexport async function runBeforeHooks(\n\tconfig: Config,\n\targs: BeforeArgs,\n\topts: { cwd?: string; yes?: boolean }\n): Promise<void> {\n\tconst hooks = config.hooks?.before ?? [];\n\tconst hooksArr = Array.isArray(hooks) ? hooks : [hooks];\n\tif (hooksArr.length === 0) return;\n\n\tconsole.clear();\n\tintro('Running before hooks');\n\tconst result = await runHooks(config, 'before', args, opts.cwd);\n\tif (result.isErr()) {\n\t\tif (opts.yes) {\n\t\t\toutro('');\n\t\t\treturn;\n\t\t}\n\t\tconst shouldContinue = await confirm({\n\t\t\tmessage: 'Before hooks failed. Would you like to continue?',\n\t\t\tinitialValue: false,\n\t\t});\n\t\tif (isCancel(shouldContinue) || !shouldContinue) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\toutro('');\n}\n\nexport async function runAfterHooks(\n\tconfig: Config,\n\targs: AfterArgs,\n\topts?: { cwd?: string }\n): Promise<void> {\n\tconst hooks = config.hooks?.after ?? [];\n\tconst hooksArr = Array.isArray(hooks) ? hooks : [hooks];\n\tif (hooksArr.length === 0) return;\n\n\tintro('Running after hooks');\n\tconst result = await runHooks(config, 'after', args, opts?.cwd);\n\tif (result.isErr()) {\n\t\tlog.warn(`After hooks failed: ${result.error.message}`);\n\t}\n\toutro('');\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/json.ts",
    "content": "export function stringify(data: unknown, options: { format?: boolean } = {}): string {\n\treturn JSON.stringify(data, null, options.format ? '\\t' : undefined);\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/lines.ts",
    "content": "import os from 'node:os';\nimport { leftPadMin } from '@/utils/pad';\n\n/** Regex used to split on new lines\n *\n * ```\n * /\\n|\\r\\n/g\n * ```\n */\nexport const NEW_LINE_REGEX = /\\n|\\r\\n/g;\n\n/** Splits str into an array of lines.\n *\n * @param str\n * @returns\n *\n * ## Usage\n *\n * ```ts\n * lines.split(\"hello\\\\nhello\\nhello\"); // [\"hello\\\\nhello\", \"hello\"]\n * ```\n */\nexport function get(str: string): string[] {\n\treturn str.split(NEW_LINE_REGEX);\n}\n\nexport type Options = {\n\tlineNumbers: boolean;\n\tprefix: (line: number, lineCount: number) => string;\n};\n\n/** Joins the array of lines back into a string using the platform specific EOL.\n *\n * @param lines\n * @returns\n *\n * ## Usage\n *\n * ```ts\n * lines.join([\"1\", \"2\", \"3\"]); // \"1\\n2\\n3\" or on windows \"1\\r\\n2\\r\\n3\"\n *\n * // add line numbers\n * lines.join([\"import { } from '.'\", \"console.log('test')\"], { lineNumbers: true });\n * // 1 import {  } from '.'\n * // 2 console.log('test')\n *\n * // add a custom prefix\n * lines.join([\"import { } from '.'\", \"console.log('test')\"], { prefix: () => \" + \" });\n * // + import {  } from '.'\n * // + console.log('test')\n * ```\n */\nexport function join(\n\tlines: string[],\n\t{ lineNumbers = false, prefix }: Partial<Options> = {}\n): string {\n\tlet transformed = lines;\n\n\tif (lineNumbers) {\n\t\tconst length = lines.length.toString().length + 1;\n\n\t\ttransformed = transformed.map((line, i) => `${leftPadMin(`${i + 1}`, length)} ${line}`);\n\t}\n\n\tif (prefix !== undefined) {\n\t\ttransformed = transformed.map((line, i) => `${prefix(i, lines.length)}${line}`);\n\t}\n\n\treturn transformed.join(os.EOL);\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/package.ts",
    "content": "import os from 'node:os';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport semver from 'semver';\nimport type { AbsolutePath } from '@/api/utils';\nimport type { RemoteDependency } from '@/utils/build';\nimport { existsSync, readFileSync } from './fs';\nimport { dirname, joinAbsolute } from './path';\n\nexport function findNearestPackageJson(\n\tcwd: AbsolutePath\n): { path: AbsolutePath; package: Partial<PackageJson> } | undefined {\n\tif (cwd === os.homedir() || cwd === path.parse(cwd).root) return undefined;\n\n\tconst packagePath = joinAbsolute(cwd, 'package.json');\n\tif (existsSync(packagePath))\n\t\treturn {\n\t\t\tpath: packagePath,\n\t\t\tpackage: getPackage(packagePath),\n\t\t};\n\n\treturn findNearestPackageJson(dirname(cwd));\n}\n\nexport function tryGetPackage(path: AbsolutePath): Result<Partial<PackageJson>, string> {\n\ttry {\n\t\treturn ok(getPackage(path));\n\t} catch (error) {\n\t\treturn err(`Error while trying to get package.json at ${path}: ${error}`);\n\t}\n}\n\nfunction getPackage(path: AbsolutePath): Partial<PackageJson> {\n\treturn JSON.parse(readFileSync(path)._unsafeUnwrap().toString());\n}\n\nexport type PackageJson = {\n\tname: string;\n\tversion: string;\n\tdescription: string;\n\tscripts: Record<string, string>;\n\tdependencies: Record<string, string>;\n\tdevDependencies: Record<string, string>;\n\ttype: string;\n\t// rest props\n\t[key: string]: unknown;\n};\n\nexport function cleanVersion(version: string) {\n\tif (version[0] === '^') {\n\t\treturn version.slice(1);\n\t}\n\n\treturn version;\n}\n\n/** Returns only the dependencies that should be installed based on what is already in the package.json */\nexport function shouldInstall<T extends Omit<RemoteDependency, 'ecosystem'>>(\n\tdependencies: { dependencies: T[]; devDependencies: T[] },\n\t{ pkg }: { pkg: Partial<PackageJson> }\n): { dependencies: T[]; devDependencies: T[] } {\n\tfunction shouldShouldInstallDependency(\n\t\tdep: T,\n\t\tpkgDeps: Record<string, string> | undefined\n\t): boolean {\n\t\tconst foundDep = pkgDeps?.[dep.name];\n\n\t\t// if version isn't pinned and dep exists delete\n\t\tif (dep.version === undefined && foundDep) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the version installed satisfies the requested version remove the dep\n\t\tif (\n\t\t\tfoundDep &&\n\t\t\t(dep.version === undefined || semver.satisfies(cleanVersion(foundDep), dep.version))\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tconst deps = new Map<string, T>();\n\tfor (const dep of dependencies.dependencies) {\n\t\tif (!shouldShouldInstallDependency(dep, pkg.dependencies)) continue;\n\n\t\tdeps.set(dep.name, dep);\n\t}\n\tconst devDeps = new Map<string, T>();\n\tfor (const dep of dependencies.devDependencies) {\n\t\tif (!shouldShouldInstallDependency(dep, pkg.devDependencies)) continue;\n\n\t\tdevDeps.set(dep.name, dep);\n\t}\n\treturn {\n\t\tdependencies: Array.from(deps.values()),\n\t\tdevDependencies: Array.from(devDeps.values()),\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/pad.ts",
    "content": "import { stripVTControlCharacters as stripAsni } from 'node:util';\n\n/** Adds the `padWith` (default `' '`) to the string the amount of times specified by the `space` argument\n *\n * @param str String to add padding to\n * @param space Whitespace to add\n * @param padWith Character to use to pad the string\n * @returns\n *\n * ## Usage\n * ```ts\n * const padded = leftPad(\"Hello\", 3, \".\");\n *\n * console.log(padded); // '...Hello'\n * ```\n */\nexport function leftPad(str: string, space: number, padWith = ' '): string {\n\treturn padWith.repeat(space) + str;\n}\n\n/** Adds the `padWith` until the string length matches the `length`\n *\n * @param str\n * @param length\n * @param padWith\n *\n * ## Usage\n * ```ts\n * const padded = leftPadMin(\"1\", 3, \".\");\n *\n * console.log(padded); // '..1'\n * ```\n */\nexport function leftPadMin(str: string, length: number, padWith = ' '): string {\n\tconst strippedLength = stripAsni(str).length;\n\n\tif (strippedLength > length)\n\t\tthrow new Error('String length is greater than the length provided.');\n\n\treturn padWith.repeat(length - strippedLength) + str;\n}\n\n/** Adds the `padWith` (default `' '`) to the string the amount of times specified by the `space` argument\n *\n * @param str String to add padding to\n * @param space Whitespace to add\n * @param padWith Character to use to pad the string\n * @returns\n *\n * ## Usage\n * ```ts\n * const padded = rightPad(\"Hello\", 3, \".\");\n *\n * console.log(padded); // 'Hello...'\n * ```\n */\nexport function rightPad(str: string, space: number, padWith = ' '): string {\n\treturn str + padWith.repeat(space);\n}\n\n/** Adds the `padWith` until the string length matches the `length`\n *\n * @param str\n * @param length\n * @param padWith\n *\n * ## Usage\n * ```ts\n * const padded = rightPadMin(\"1\", 3, \".\");\n *\n * console.log(padded); // '1..'\n * ```\n */\nexport function rightPadMin(str: string, length: number, padWith = ' '): string {\n\tconst strippedLength = stripAsni(str).length;\n\n\tif (strippedLength > length)\n\t\tthrow new Error('String length is greater than the length provided.');\n\n\treturn str + padWith.repeat(length - strippedLength);\n}\n\n/** Pads the string with the `padWith` so that it appears in the center of a new string with the provided length.\n *\n * @param str\n * @param length\n * @param padWith\n * @returns\n *\n * ## Usage\n * ```ts\n * const str = \"Hello, World!\";\n *\n * const padded = centerPad(str, str.length + 4);\n *\n * console.log(padded); // '  Hello, World!  '\n * ```\n */\nexport function centerPad(str: string, length: number, padWith = ' '): string {\n\tconst strippedLength = stripAsni(str).length;\n\n\tif (strippedLength > length) {\n\t\tthrow new Error('String length is greater than the length provided.');\n\t}\n\n\tconst overflow = length - strippedLength;\n\n\tconst paddingLeft = Math.floor(overflow / 2);\n\n\tconst paddingRight = Math.ceil(overflow / 2);\n\n\treturn padWith.repeat(paddingLeft) + str + padWith.repeat(paddingRight);\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/parse-package-name.ts",
    "content": "/**\n * Adapted from https://github.com/egoist/parse-package-name/blob/main/src/index.ts\n * @module\n */\n\nimport { err, ok } from 'nevereverthrow';\n\n// Parsed a scoped package name into name, version, and path.\nconst RE_SCOPED = /^(@[^/]+\\/[^@/]+)(?:@([^/]+))?(\\/.*)?$/;\n// Parsed a non-scoped package name into name, version, path\nconst RE_NON_SCOPED = /^([^@/]+)(?:@([^/]+))?(\\/.*)?$/;\n\nexport type Package = {\n\t/** Name of the package as it would be installed from npm */\n\tname: string;\n\t/** Version of the package */\n\tversion: string;\n\tpath: string;\n};\n\nexport function parsePackageName(input: string) {\n\tconst m = RE_SCOPED.exec(input) || RE_NON_SCOPED.exec(input);\n\n\tif (!m) return err(`invalid package name: ${input}`);\n\n\treturn ok({\n\t\tname: m[1] || '',\n\t\tversion: m[2] || undefined,\n\t\tpath: m[3] || '',\n\t});\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/path.ts",
    "content": "import path from 'pathe';\nimport type { AbsolutePath, Branded, ItemRelativePath } from './types';\n\n/**\n * Join all arguments together and normalize the resulting path.\n *\n * @param p\n * @param paths\n * @returns\n */\nexport function joinAbsolute(p: AbsolutePath, ...paths: string[]): AbsolutePath {\n\treturn path.join(p, ...paths) as AbsolutePath;\n}\n\nexport type NormalizedAbsolutePath = Branded<AbsolutePath, 'normalizedAbsolutePath'>;\n\nexport function normalizeAbsolute(p: AbsolutePath): NormalizedAbsolutePath {\n\treturn path.normalize(p) as NormalizedAbsolutePath;\n}\n\nexport function joinRelative(p: ItemRelativePath, ...paths: string[]): ItemRelativePath {\n\treturn path.join(p, ...paths) as ItemRelativePath;\n}\n\n/**\n * Return the directory name of a path. Similar to the Unix dirname command.\n *\n * @param path the path to evaluate.\n * @throws {TypeError} if path is not a string.\n * @returns\n */\nexport function dirname(p: AbsolutePath): AbsolutePath {\n\treturn path.dirname(p) as AbsolutePath;\n}\n\nexport type RelativeToCwdPath = Branded<ItemRelativePath, 'relativeToCwdPath'>;\n\nexport function relativeToCwd(cwd: AbsolutePath, p: AbsolutePath): RelativeToCwdPath {\n\treturn path.relative(cwd, p) as RelativeToCwdPath;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/persisted.ts",
    "content": "import Conf from 'conf';\n\nfunction get() {\n\treturn new Conf({ projectName: 'jsrepo-v2' });\n}\n\nexport { get };\n"
  },
  {
    "path": "packages/jsrepo/src/utils/prompts.ts",
    "content": "import {\n\tintro as _intro,\n\tspinner as _spinner,\n\tcancel,\n\tconfirm,\n\tisCancel,\n\tlog,\n\ttaskLog,\n\ttext,\n} from '@clack/prompts';\nimport isUnicodeSupported from 'is-unicode-supported';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport {\n\tAGENTS,\n\ttype Agent,\n\tdetect,\n\tgetUserAgent,\n\ttype ResolvedCommand,\n} from 'package-manager-detector';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { x } from 'tinyexec';\nimport pkg from '@/../package.json';\nimport { type AbsolutePath, type Config, DEFAULT_LANGS } from '@/api';\nimport { installDependencies } from '@/langs/js';\nimport type { RemoteDependency } from '@/utils/build';\nimport { searchForEnvFile, updateEnvFile } from '@/utils/env';\nimport { findNearestPackageJson, shouldInstall } from '@/utils/package';\nimport type { JsrepoError } from './errors';\nimport { writeFileSync } from './fs';\nimport { dirname, joinAbsolute } from './path';\n\nexport const isTTY = process.stdout.isTTY;\n\nexport function intro() {\n\tconsole.clear();\n\n\t_intro(`${pc.bgYellow(pc.black(` ${pkg.name} `))}${pc.gray(` v${pkg.version} `)}`);\n}\n\nfunction createVerboseLogger({\n\toptions,\n}: {\n\toptions: { verbose: boolean };\n}): (msg: string) => void {\n\treturn (msg: string) => {\n\t\tif (!options.verbose) return;\n\t\tlog.info(msg);\n\t};\n}\n\nexport type Spinner = ReturnType<typeof spinner>;\n\n/**\n * Creates a verbose logger and a spinner. We don't want to use a spinner in verbose mode because we often want to log within spinners and maintain the logs.\n *\n * @param param0\n * @returns\n */\nexport function initLogging({ options }: { options: { verbose: boolean } }) {\n\tconst verbose = createVerboseLogger({ options });\n\treturn {\n\t\tverbose,\n\t\tspinner: spinner({ verbose: options.verbose ? verbose : undefined }),\n\t};\n}\n\n/** A spinner compatible with verbose logging\n *\n * @param param0\n * @returns\n */\nfunction spinner({\n\tverbose,\n}: {\n\tverbose?: (msg: string) => void;\n} = {}): ReturnType<typeof _spinner> {\n\tconst loading = _spinner();\n\n\treturn {\n\t\tmessage: (msg) => {\n\t\t\tif (verbose) {\n\t\t\t\tverbose(msg ?? '');\n\t\t\t} else {\n\t\t\t\tloading.message(msg);\n\t\t\t}\n\t\t},\n\t\tstop: (msg) => {\n\t\t\tif (verbose) {\n\t\t\t\tverbose(msg ?? '');\n\t\t\t} else {\n\t\t\t\tloading.stop(msg);\n\t\t\t}\n\t\t},\n\t\tstart: (msg) => {\n\t\t\tif (verbose) {\n\t\t\t\tverbose(msg ?? '');\n\t\t\t} else {\n\t\t\t\tloading.start(msg);\n\t\t\t}\n\t\t},\n\t\terror: (msg) => {\n\t\t\tif (verbose) {\n\t\t\t\tverbose(msg ?? '');\n\t\t\t} else {\n\t\t\t\tloading.start(msg);\n\t\t\t}\n\t\t},\n\t\tcancel: () => loading.cancel(),\n\t\tclear: () => loading.clear(),\n\t\tget isCancelled() {\n\t\t\treturn loading.isCancelled;\n\t\t},\n\t};\n}\n\nexport { outro } from '@clack/prompts';\n\nexport async function promptInstallDependencies(\n\tdependencies: {\n\t\tdependencies: Omit<RemoteDependency, 'ecosystem'>[];\n\t\tdevDependencies: Omit<RemoteDependency, 'ecosystem'>[];\n\t},\n\t{ options, configPath }: { options: { yes: boolean }; configPath: string }\n) {\n\tlet install = options.yes;\n\tif (!options.yes) {\n\t\tconst result = await confirm({\n\t\t\tmessage: 'Would you like to install dependencies?',\n\t\t\tinitialValue: true,\n\t\t});\n\n\t\tif (isCancel(result)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tinstall = result;\n\t}\n\n\tif (install) {\n\t\tawait installDependencies(\n\t\t\t{\n\t\t\t\tdependencies: dependencies.dependencies.map((dependency) => ({\n\t\t\t\t\t...dependency,\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t})),\n\t\t\t\tdevDependencies: dependencies.devDependencies.map((dependency) => ({\n\t\t\t\t\t...dependency,\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t})),\n\t\t\t},\n\t\t\t{\n\t\t\t\tcwd: dirname(configPath as AbsolutePath),\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport async function promptAddEnvVars(\n\tneededEnvVars: Record<string, string>,\n\t{ options }: { options: { yes: boolean; cwd: AbsolutePath } }\n): Promise<Result<Record<string, string> | undefined, JsrepoError>> {\n\tconst envFile = searchForEnvFile(options.cwd);\n\n\tconst updatedEnvVars: Record<string, string> = {};\n\n\t// we never overwrite existing values with blank values\n\t// we overwrite existing values with new values\n\n\tfor (const [name, value] of Object.entries(neededEnvVars)) {\n\t\t// if the value is not blank and not the same as the old value then we overwrite the old value\n\t\tif (value !== '' && value !== envFile?.envVars[name]) {\n\t\t\tupdatedEnvVars[name] = value;\n\t\t\tcontinue;\n\t\t}\n\t\t// value is blank\n\t\t// if the current value doesn't exist or is blank then we prompt the user for a value\n\t\tif (envFile?.envVars[name] === undefined || envFile?.envVars[name] === '') {\n\t\t\tif (!options.yes) {\n\t\t\t\tconst newValue = await text({\n\t\t\t\t\tmessage: `Add a value for ${name}?`,\n\t\t\t\t\tinitialValue: '',\n\t\t\t\t\tdefaultValue: '',\n\t\t\t\t});\n\n\t\t\t\tif (isCancel(newValue)) {\n\t\t\t\t\tcancel('Canceled!');\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\n\t\t\t\tupdatedEnvVars[name] = newValue;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (Object.keys(updatedEnvVars).length === 0) return ok(undefined);\n\n\tconst newContents = updateEnvFile(envFile?.contents ?? '', updatedEnvVars);\n\tconst envFilePath = envFile?.path ?? joinAbsolute(options.cwd, '.env.local');\n\tconst writeResult = writeFileSync(envFilePath, newContents);\n\tif (writeResult.isErr()) return err(writeResult.error);\n\n\tlog.success(\n\t\t`Added environment variables to ${pc.cyan(path.relative(options.cwd, envFilePath))}`\n\t);\n\n\treturn ok(updatedEnvVars);\n}\n\nexport async function promptInstallDependenciesByEcosystem(\n\tneededDependencies: { dependencies: RemoteDependency[]; devDependencies: RemoteDependency[] },\n\t{\n\t\toptions,\n\t\tconfig,\n\t}: { options: { cwd: AbsolutePath; yes: boolean }; config: Config | undefined }\n): Promise<{\n\tinstalled: boolean;\n\tdependencies: RemoteDependency[];\n}> {\n\t// we can fast path the js dependencies since we support js out of the box\n\tconst deps = shouldInstall(\n\t\t{\n\t\t\tdependencies: neededDependencies.dependencies.filter((dep) => dep.ecosystem === 'js'),\n\t\t\tdevDependencies: neededDependencies.devDependencies.filter(\n\t\t\t\t(dep) => dep.ecosystem === 'js'\n\t\t\t),\n\t\t},\n\t\t{ pkg: findNearestPackageJson(options.cwd)?.package ?? {} }\n\t);\n\tconst dependenciesByEcosystem = new Map<\n\t\tstring,\n\t\t{ dependencies: RemoteDependency[]; devDependencies: RemoteDependency[] }\n\t>();\n\tfor (const dep of deps.dependencies) {\n\t\tconst ecosystem = dependenciesByEcosystem.get(dep.ecosystem);\n\t\tif (ecosystem) {\n\t\t\tecosystem.dependencies.push(dep);\n\t\t\tdependenciesByEcosystem.set(dep.ecosystem, ecosystem);\n\t\t} else {\n\t\t\tdependenciesByEcosystem.set(dep.ecosystem, {\n\t\t\t\tdependencies: [dep],\n\t\t\t\tdevDependencies: [],\n\t\t\t});\n\t\t}\n\t}\n\tfor (const dep of deps.devDependencies) {\n\t\tconst ecosystem = dependenciesByEcosystem.get(dep.ecosystem);\n\t\tif (ecosystem) {\n\t\t\tecosystem.devDependencies.push(dep);\n\t\t\tdependenciesByEcosystem.set(dep.ecosystem, ecosystem);\n\t\t} else {\n\t\t\tdependenciesByEcosystem.set(dep.ecosystem, {\n\t\t\t\tdependencies: [],\n\t\t\t\tdevDependencies: [dep],\n\t\t\t});\n\t\t}\n\t}\n\n\tconst languages = config?.languages ?? DEFAULT_LANGS;\n\n\tif (dependenciesByEcosystem.size === 0) return { installed: false, dependencies: [] };\n\n\tlet install = options.yes;\n\tif (!options.yes) {\n\t\tconst result = await confirm({\n\t\t\tmessage: 'Would you like to install dependencies?',\n\t\t\tinitialValue: true,\n\t\t});\n\n\t\tif (isCancel(result)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tinstall = result;\n\t}\n\n\tconst allDeps = Array.from(dependenciesByEcosystem.values()).flatMap((v) => [\n\t\t...v.dependencies,\n\t\t...v.devDependencies,\n\t]);\n\n\tif (!install) return { installed: false, dependencies: allDeps };\n\n\tfor (const [ecosystem, dependencies] of dependenciesByEcosystem) {\n\t\tawait languages\n\t\t\t.find((lang) => lang.canInstallDependencies(ecosystem))\n\t\t\t?.installDependencies(dependencies, {\n\t\t\t\tcwd: options.cwd,\n\t\t\t});\n\t}\n\n\treturn { installed: true, dependencies: allDeps };\n}\n\nexport async function runCommands({\n\ttitle,\n\tcommands,\n\tcwd,\n\tmessages,\n}: {\n\ttitle: string;\n\tcommands: ResolvedCommand[];\n\tcwd: string;\n\tmessages: {\n\t\tsuccess: () => string;\n\t\terror: (err: unknown) => string;\n\t};\n}) {\n\tconst task = taskLog({\n\t\ttitle,\n\t\tlimit: Math.ceil(process.stdout.rows / 2),\n\t\tspacing: 0,\n\t\tretainLog: true,\n\t});\n\n\tconst runCmd = async (cmd: ResolvedCommand) => {\n\t\tconst proc = x(cmd.command, [...cmd.args], { nodeOptions: { cwd } });\n\n\t\tfor await (const line of proc) {\n\t\t\ttask.message(line);\n\t\t}\n\t};\n\n\ttry {\n\t\tfor (const command of commands) {\n\t\t\tawait runCmd(command);\n\t\t}\n\n\t\ttask.success(messages.success());\n\t} catch (err) {\n\t\ttask.error(messages.error(err));\n\t\tprocess.exit(1);\n\t}\n}\n\nexport async function detectPackageManager(cwd: AbsolutePath): Promise<Agent> {\n\tconst detected = (await detect({ cwd }))?.agent;\n\tif (detected) return detected;\n\treturn AGENTS.find((agent) => agent === getUserAgent()) ?? 'npm';\n}\n\nconst unicode = isUnicodeSupported();\n\nconst s = (c: string, fallback: string) => (unicode ? c : fallback);\n\nexport const VERTICAL_LINE = pc.gray(s('│', '|'));\n"
  },
  {
    "path": "packages/jsrepo/src/utils/roles.ts",
    "content": "export function resolveWithRoles(options: {\n\twith?: string[];\n\t/** @deprecated kept for backward compatibility */\n\twithExamples?: boolean;\n\t/** @deprecated kept for backward compatibility */\n\twithDocs?: boolean;\n\t/** @deprecated kept for backward compatibility */\n\twithTests?: boolean;\n}): Set<string> {\n\tconst withRoles = new Set(options.with ?? []);\n\tif (options.withExamples) withRoles.add('example');\n\tif (options.withDocs) withRoles.add('doc');\n\tif (options.withTests) withRoles.add('test');\n\treturn withRoles;\n}\n\nexport function shouldIncludeRole(role: string | undefined, withRoles: Set<string>): boolean {\n\tif (!role || role === 'file') return true;\n\treturn withRoles.has(role);\n}\n\nexport function isOptionalRole(role: string | undefined): boolean {\n\treturn !!role && role !== 'file';\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/strings.ts",
    "content": "/** Returns the matched suffix for the string (if it exists). Great for matching string union types.\n *\n * @param str\n * @param strings\n * @returns\n *\n * ## Usage\n * ```ts\n * endsWithOneOf('cb', 'a', 'b'); // 'b'\n * endsWithOneOf('cc', 'a', 'b'); // undefined\n * ```\n */\nexport function endsWithOneOf<T extends string>(str: string, strings: readonly T[]): T | undefined {\n\tfor (const s of strings) {\n\t\tif (str.endsWith(s)) return s;\n\t}\n\n\treturn undefined;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/token-manager.ts",
    "content": "import type Conf from 'conf';\nimport type { ProviderFactory } from '@/providers';\nimport * as persisted from '@/utils/persisted';\nimport { JsrepoError } from './errors';\n\nexport class TokenManager {\n\t#storage: Conf;\n\n\tconstructor(storage?: Conf) {\n\t\tthis.#storage = storage ?? persisted.get();\n\t}\n\n\tget(provider: ProviderFactory, registry: string | undefined): string | undefined {\n\t\tif (provider.auth?.tokenStoredFor === 'registry') {\n\t\t\tconst tokens = this.getProviderRegistryTokens(provider);\n\n\t\t\tif (!registry) {\n\t\t\t\tthrow new JsrepoError('No registry was provided to get when one was expected', {\n\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t'This is a bug in jsrepo. Please report it here https://github.com/jsrepo/jsrepo/issues',\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn tokens[registry];\n\t\t} else {\n\t\t\tconst token = this.#storage.get(provider.name, undefined) as string | undefined;\n\n\t\t\t// fallback on environment variable\n\t\t\tif (provider.auth?.envVar) {\n\t\t\t\treturn token ?? process.env[provider.auth.envVar];\n\t\t\t}\n\n\t\t\treturn token;\n\t\t}\n\t}\n\n\tset(provider: ProviderFactory, registry: string | undefined, secret: string) {\n\t\tif (provider.auth?.tokenStoredFor === 'registry') {\n\t\t\tif (!registry) {\n\t\t\t\tthrow new JsrepoError('No registry was provided to set when one was expected', {\n\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t'This is a bug in jsrepo. Please report it here https://github.com/jsrepo/jsrepo/issues',\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.setProviderRegistryToken(provider, registry, secret);\n\t\t} else {\n\t\t\tthis.#storage.set(provider.name, secret);\n\t\t}\n\t}\n\n\tdelete(provider: ProviderFactory, registry: string | undefined) {\n\t\tif (provider.auth?.tokenStoredFor === 'registry') {\n\t\t\tconst tokens = this.getProviderRegistryTokens(provider);\n\t\t\tif (!registry) {\n\t\t\t\tthrow new JsrepoError('No registry was provided to delete when one was expected', {\n\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t'This is a bug in jsrepo. Please report it here https://github.com/jsrepo/jsrepo/issues',\n\t\t\t\t});\n\t\t\t}\n\t\t\tdelete tokens[registry];\n\t\t\tthis.#storage.set(provider.name, tokens);\n\t\t} else {\n\t\t\tthis.#storage.delete(provider.name);\n\t\t}\n\t}\n\n\tgetProviderRegistryTokens(provider: ProviderFactory): Record<string, string> {\n\t\tconst tokens = this.#storage.get(provider.name) as Record<string, string> | undefined;\n\n\t\treturn tokens ?? {};\n\t}\n\n\tsetProviderRegistryToken(provider: ProviderFactory, registry: string, token: string) {\n\t\tconst tokens = this.getProviderRegistryTokens(provider);\n\t\ttokens[registry] = token;\n\t\tthis.#storage.set(provider.name, tokens);\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/tsconfig.ts",
    "content": "import { createPathsMatcher, getTsconfig, type TsConfigResult } from 'get-tsconfig';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport type { AbsolutePath } from '@/utils/types';\nimport { existsSync, statSync } from './fs';\nimport { dirname, joinAbsolute } from './path';\n\n/** Attempts to get the js/tsconfig file for the searched path\n *\n * @param searchPath\n * @returns\n */\nexport function tryGetTsconfig(\n\tsearchPath?: string,\n\tfileName?: string\n): Result<TsConfigResult | null, string> {\n\tlet config: TsConfigResult | null;\n\n\ttry {\n\t\tconfig = getTsconfig(searchPath, fileName);\n\n\t\tif (!config) {\n\t\t\t// if we don't find the config at first check for a jsconfig\n\t\t\tconfig = getTsconfig(searchPath, fileName);\n\n\t\t\tif (!config) {\n\t\t\t\treturn ok(null);\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\treturn err(`Error while trying to get ${pc.bold(fileName || 'tsconfig.json')}: ${e}`);\n\t}\n\n\treturn ok(config);\n}\n\nexport type PathsMatcher = ((specifier: string) => string[]) | null;\n\nexport function _createPathsMatcher(\n\tconfigResult: TsConfigResult,\n\t{ cwd }: { cwd: AbsolutePath }\n): PathsMatcher {\n\tconst matcher = createPathsMatcher(configResult);\n\n\tif (!configResult.config.references) return matcher;\n\n\tconst matchers: ((specifier: string) => string[])[] = matcher ? [matcher] : [];\n\n\t// resolve tsconfig references\n\tfor (const configPath of configResult.config.references) {\n\t\tconst configPathOrDir = joinAbsolute(cwd, configPath.path);\n\n\t\tif (!existsSync(configPathOrDir)) continue;\n\n\t\tlet directory: AbsolutePath;\n\t\tlet fileName = 'tsconfig.json';\n\n\t\t// references can be a file or a directory https://www.typescriptlang.org/docs/handbook/project-references.html\n\t\tif (statSync(configPathOrDir)._unsafeUnwrap().isFile()) {\n\t\t\tdirectory = dirname(configPathOrDir);\n\t\t\tfileName = path.basename(configPathOrDir);\n\t\t} else {\n\t\t\tdirectory = configPathOrDir;\n\t\t}\n\n\t\tconst config = tryGetTsconfig(directory, fileName).unwrapOr(null);\n\n\t\tif (config === null) continue;\n\n\t\tconst matcher = _createPathsMatcher(config, { cwd: directory });\n\n\t\tif (matcher) matchers.push(matcher);\n\t}\n\n\tif (matchers.length === 0) return null;\n\n\t// get all possible paths from all matchers\n\treturn (specifier: string) => matchers.flatMap((matcher) => matcher(specifier));\n}\n\nexport { _createPathsMatcher as createPathsMatcher };\n"
  },
  {
    "path": "packages/jsrepo/src/utils/types.ts",
    "content": "export type LooseAutocomplete<T> = T | (string & {});\n\nexport type Prettify<T> = {\n\t[K in keyof T]: T[K];\n} & {};\n\ndeclare const brand: unique symbol;\n\ntype Brand<B extends string> = { [brand]: B };\n\n/** Allows you to create a branded type.\n *\n * ## Usage\n * ```ts\n * type Milliseconds = Brand<number, 'milliseconds'>;\n * ```\n */\nexport type Branded<T, B extends string> = T & Brand<B>;\n\n/**\n * An absolute path to a file. Can be used to immediately read the file.\n */\nexport type AbsolutePath = Branded<string, 'absolutePath'>;\n\n/**\n * A path relative to the parent item.\n */\nexport type ItemRelativePath = Branded<string, 'itemRelativePath'>;\n\nexport type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;\n"
  },
  {
    "path": "packages/jsrepo/src/utils/url.ts",
    "content": "/** Joins the segments into a single url correctly handling leading and trailing slashes in each segment.\n *\n * @param segments\n * @returns\n *\n * ## Usage\n * ```ts\n * const url = join('https://example.com', '', 'api/', '/examples/');\n *\n * console.log(url); // https://example.com/api/examples\n * ```\n */\nexport function join(...segments: string[]): string {\n\treturn segments\n\t\t.map((s) => removeLeadingAndTrailingSlash(s))\n\t\t.filter(Boolean)\n\t\t.join('/');\n}\n\n/** Removes the leading and trailing slash from the segment (if they exist)\n *\n * @param segment\n * @returns\n *\n * ## Usage\n * ```ts\n * const segment = removeLeadingAndTrailingSlash('/example/');\n *\n * console.log(segment); // 'example'\n * ```\n */\nexport function removeLeadingAndTrailingSlash(segment: string): string {\n\tconst newSegment = removeLeadingSlash(segment);\n\treturn removeTrailingSlash(newSegment);\n}\n\n/** Adds a leading and trailing to the beginning and end of the segment (if it doesn't already exist)\n *\n * @param segment\n * @returns\n *\n * ## Usage\n * ```ts\n * const segment = addLeadingAndTrailingSlash('example');\n *\n * console.log(segment); // '/example/'\n * ```\n */\nexport function addLeadingAndTrailingSlash(segment: string): string {\n\t// this is a weird case so feel free to handle it however you think it makes the most sense\n\tif (segment === '') return '//';\n\n\tconst newSegment = addLeadingSlash(segment);\n\treturn addTrailingSlash(newSegment);\n}\n\n/** Removes the leading slash from the beginning of the segment (if it exists)\n *\n * @param segment\n * @returns\n *\n * ## Usage\n * ```ts\n * const segment = removeLeadingSlash('/example');\n *\n * console.log(segment); // 'example'\n * ```\n */\nexport function removeLeadingSlash(segment: string): string {\n\tlet newSegment = segment;\n\tif (newSegment.startsWith('/')) {\n\t\tnewSegment = newSegment.slice(1);\n\t}\n\n\treturn newSegment;\n}\n\n/** Adds a leading slash to the beginning of the segment (if it doesn't already exist)\n *\n * @param segment\n * @returns\n *\n * ## Usage\n * ```ts\n * const segment = addLeadingSlash('example');\n *\n * console.log(segment); // '/example'\n * ```\n */\nexport function addLeadingSlash(segment: string): string {\n\tlet newSegment = segment;\n\tif (!newSegment.startsWith('/')) {\n\t\tnewSegment = `/${newSegment}`;\n\t}\n\n\treturn newSegment;\n}\n\n/** Removes the trailing slash from the end of the segment (if it exists)\n *\n * @param segment\n * @returns\n *\n * ## Usage\n * ```ts\n * const segment = removeTrailingSlash('example/');\n *\n * console.log(segment); // 'example'\n * ```\n */\nexport function removeTrailingSlash(segment: string): string {\n\tlet newSegment = segment;\n\tif (newSegment.endsWith('/')) {\n\t\tnewSegment = newSegment.slice(0, newSegment.length - 1);\n\t}\n\n\treturn newSegment;\n}\n\n/** Adds a trailing slash to the end of the segment (if it doesn't already exist)\n *\n * @param segment\n * @returns\n *\n * ## Usage\n * ```ts\n * const segment = addTrailingSlash('example');\n *\n * console.log(segment); // 'example/'\n * ```\n */\nexport function addTrailingSlash(segment: string): string {\n\tlet newSegment = segment;\n\tif (!newSegment.endsWith('/')) {\n\t\tnewSegment = `${newSegment}/`;\n\t}\n\n\treturn newSegment;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/utils.ts",
    "content": "export type MaybeGetter<T, Args extends unknown[] = unknown[]> = T | ((...args: Args) => T);\n\nexport function extract<T, Args extends unknown[] = unknown[]>(\n\tgetter: MaybeGetter<T, Args>,\n\t...args: Args\n): T {\n\tif (isFunction<T, Args>(getter)) return getter(...args);\n\treturn getter;\n}\n\nexport type MaybeGetterAsync<T, Args extends unknown[] = unknown[]> =\n\t| MaybeGetter<T, Args>\n\t| ((...args: Args) => Promise<T>);\n\nexport async function extractAsync<T, Args extends unknown[] = unknown[]>(\n\tgetter: MaybeGetterAsync<T, Args>,\n\t...args: Args\n): Promise<T> {\n\tif (isFunctionAsync<T, Args>(getter)) return await getter(...args);\n\treturn getter;\n}\n\nexport function isFunction<T, Args extends unknown[] = unknown[]>(\n\tvalue: unknown\n): value is (...args: Args) => T {\n\treturn typeof value === 'function';\n}\n\nexport function isFunctionAsync<T, Args extends unknown[] = unknown[]>(\n\tvalue: unknown\n): value is ((...args: Args) => T) | ((...args: Args) => Promise<T>) {\n\treturn typeof value === 'function';\n}\n\nexport function debounced<Args extends unknown[] = unknown[]>(\n\tfn: (...args: Args) => void,\n\tdelay: number\n): (...args: Args) => void {\n\tlet timeout: NodeJS.Timeout | null = null;\n\treturn (...args: Args) => {\n\t\tif (timeout) clearTimeout(timeout);\n\t\ttimeout = setTimeout(() => fn(...args), delay);\n\t};\n}\n\n/** Await this to pause execution until the duration has passed.\n *\n * @param durationMs The duration in ms until the sleep in over\n * @returns\n *\n * ## Usage\n * ```ts\n * console.log(Date.now()) // 1725739228744\n *\n * await sleep(1000);\n *\n * console.log(Date.now()) // 1725739229744\n * ```\n */\nexport function sleep(durationMs: number): Promise<void> {\n\treturn new Promise((res) => setTimeout(res, durationMs));\n}\n\nexport function noop() {\n\treturn;\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/validate-npm-package-name.ts",
    "content": "import { builtinModules } from 'node:module';\n\ninterface ValidationResult {\n\tvalidForNewPackages: boolean;\n\tvalidForOldPackages: boolean;\n\twarnings?: string[];\n\terrors?: string[];\n}\n\nconst scopedPackagePattern = /^(?:@([^/]+?)[/])?([^/]+?)$/;\nconst exclusionList = ['node_modules', 'favicon.ico'];\n\nexport function validateNpmPackageName(name: string | null | undefined): ValidationResult {\n\tconst warnings: string[] = [];\n\tconst errors: string[] = [];\n\n\tif (name === null) {\n\t\terrors.push('name cannot be null');\n\t\treturn done(warnings, errors);\n\t}\n\n\tif (name === undefined) {\n\t\terrors.push('name cannot be undefined');\n\t\treturn done(warnings, errors);\n\t}\n\n\tif (typeof name !== 'string') {\n\t\terrors.push('name must be a string');\n\t\treturn done(warnings, errors);\n\t}\n\n\tif (!name.length) {\n\t\terrors.push('name length must be greater than zero');\n\t}\n\n\tif (name.startsWith('.')) {\n\t\terrors.push('name cannot start with a period');\n\t}\n\n\tif (name.match(/^_/)) {\n\t\terrors.push('name cannot start with an underscore');\n\t}\n\n\tif (name.trim() !== name) {\n\t\terrors.push('name cannot contain leading or trailing spaces');\n\t}\n\n\t// No funny business\n\texclusionList.forEach((excludedName) => {\n\t\tif (name.toLowerCase() === excludedName) {\n\t\t\terrors.push(`${excludedName} is not a valid package name`);\n\t\t}\n\t});\n\n\t// Generate warnings for stuff that used to be allowed\n\n\t// core module names like http, events, util, etc\n\tif (builtinModules.includes(name.toLowerCase())) {\n\t\twarnings.push(`${name} is a core module name`);\n\t}\n\n\tif (name.length > 214) {\n\t\twarnings.push('name can no longer contain more than 214 characters');\n\t}\n\n\t// mIxeD CaSe nAMEs\n\tif (name.toLowerCase() !== name) {\n\t\twarnings.push('name can no longer contain capital letters');\n\t}\n\n\tconst lastSegment = name.split('/').slice(-1)[0];\n\tif (lastSegment && /[~'!()*]/.test(lastSegment)) {\n\t\twarnings.push('name can no longer contain special characters (\"~\\'!()*\")');\n\t}\n\n\tif (encodeURIComponent(name) !== name) {\n\t\t// Maybe it's a scoped package name, like @user/package\n\t\tconst nameMatch = name.match(scopedPackagePattern);\n\t\tif (nameMatch) {\n\t\t\tconst user = nameMatch[1];\n\t\t\tconst pkg = nameMatch[2];\n\n\t\t\tif (pkg?.startsWith('.')) {\n\t\t\t\terrors.push('name cannot start with a period');\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tuser &&\n\t\t\t\tpkg &&\n\t\t\t\tencodeURIComponent(user) === user &&\n\t\t\t\tencodeURIComponent(pkg) === pkg\n\t\t\t) {\n\t\t\t\treturn done(warnings, errors);\n\t\t\t}\n\t\t}\n\n\t\terrors.push('name can only contain URL-friendly characters');\n\t}\n\n\treturn done(warnings, errors);\n}\n\nconst done = (warnings: string[], errors: string[]): ValidationResult => {\n\tconst result: ValidationResult = {\n\t\tvalidForNewPackages: errors.length === 0 && warnings.length === 0,\n\t\tvalidForOldPackages: errors.length === 0,\n\t\twarnings: warnings,\n\t\terrors: errors,\n\t};\n\tif (result.warnings && !result.warnings.length) {\n\t\tdelete result.warnings;\n\t}\n\tif (result.errors && !result.errors.length) {\n\t\tdelete result.errors;\n\t}\n\treturn result;\n};\n"
  },
  {
    "path": "packages/jsrepo/src/utils/warnings.ts",
    "content": "import { log } from '@clack/prompts';\nimport pc from 'picocolors';\nimport type { Config } from '@/utils/config';\n\n/**\n * Base warning class. All warnings should extend this class.\n */\nexport class Warning {\n\tconstructor(public readonly message: string) {}\n}\n\n/**\n * Type for a warning handler function.\n */\nexport type WarningHandler = (warning: Warning) => void;\n\n/**\n * Warning when a language cannot be found to resolve dependencies for a file.\n */\nexport class LanguageNotFoundWarning extends Warning {\n\tpublic readonly path: string;\n\tconstructor(options: { path: string }) {\n\t\tsuper(`Couldn't find a language to resolve dependencies for ${options.path}.`);\n\t\tthis.path = options.path;\n\t}\n}\n\n/**\n * Warning when an import is skipped because it's not a valid package name or path alias.\n */\nexport class InvalidImportWarning extends Warning {\n\tpublic readonly specifier: string;\n\tpublic readonly fileName: string;\n\tconstructor(options: { specifier: string; fileName: string }) {\n\t\tsuper(\n\t\t\t`Skipped adding import \\`${pc.cyan(options.specifier)}\\` from ${options.fileName}. Reason: Not a valid package name or path alias.`\n\t\t);\n\t\tthis.specifier = options.specifier;\n\t\tthis.fileName = options.fileName;\n\t}\n}\n\n/**\n * Warning when a dynamic import cannot be resolved due to unresolvable syntax.\n */\nexport class UnresolvableDynamicImportWarning extends Warning {\n\tpublic readonly specifier: string;\n\tpublic readonly fileName: string;\n\tconstructor(options: { specifier: string; fileName: string }) {\n\t\tsuper(\n\t\t\t`Skipping ${pc.cyan(options.specifier)} from ${pc.bold(options.fileName)}. Reason: Unresolvable syntax. 💡 consider manually including the modules expected to be resolved by this import in your registry dependencies.`\n\t\t);\n\t\tthis.specifier = options.specifier;\n\t\tthis.fileName = options.fileName;\n\t}\n}\n\n/**\n * Warning when a glob pattern doesn't match any files.\n */\nexport class GlobPatternNoMatchWarning extends Warning {\n\tpublic readonly itemName: string;\n\tpublic readonly pattern: string;\n\tconstructor(options: { itemName: string; pattern: string }) {\n\t\tsuper(\n\t\t\t`The glob pattern defined in ${pc.bold(options.itemName)}: ${pc.bold(options.pattern)} didn't match any files.`\n\t\t);\n\t\tthis.itemName = options.itemName;\n\t\tthis.pattern = options.pattern;\n\t}\n}\n\n/**\n * Creates a warning handler that uses the onwarn callback if provided, otherwise uses the default logger.\n */\nexport function createWarningHandler(onwarn?: Config['build']['onwarn']): WarningHandler {\n\treturn (warning: Warning) => {\n\t\tif (!onwarn) {\n\t\t\tlog.warn(warning.message);\n\t\t} else {\n\t\t\tonwarn(warning, (w) => log.warn(w.message));\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "packages/jsrepo/src/utils/zod.ts",
    "content": "import { err, ok, type Result } from 'nevereverthrow';\nimport type { z } from 'zod';\nimport { InvalidJSONError, ZodError } from '@/utils/errors';\n\nexport function safeValidate<T>(schema: z.ZodSchema<T>, data: unknown): Result<T, ZodError> {\n\tconst parsed = schema.safeParse(data);\n\tif (parsed.success) {\n\t\treturn ok(parsed.data);\n\t}\n\treturn err(new ZodError(parsed.error));\n}\n\nexport function safeParseFromJSON<T>(\n\tschema: z.ZodSchema<T>,\n\tdata: string\n): Result<T, InvalidJSONError | ZodError> {\n\ttry {\n\t\tconst parsed = schema.safeParse(JSON.parse(data));\n\t\tif (parsed.success) {\n\t\t\treturn ok(parsed.data);\n\t\t}\n\t\treturn err(new ZodError(parsed.error));\n\t} catch (error) {\n\t\treturn err(new InvalidJSONError(error));\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/__mocks__/fs/promises.cjs",
    "content": "// we can also use `import`, but then\n// every export should be explicitly defined\n\nconst { fs } = require('memfs');\nmodule.exports = fs.promises;\n"
  },
  {
    "path": "packages/jsrepo/tests/__mocks__/fs.cjs",
    "content": "// we can also use `import`, but then\n// every export should be explicitly defined\n\nconst { fs } = require('memfs');\nmodule.exports = fs;\n"
  },
  {
    "path": "packages/jsrepo/tests/build.test.ts",
    "content": "import { log } from '@clack/prompts';\nimport path from 'pathe';\nimport { assert, beforeAll, describe, expect, it, type MockInstance, vi } from 'vitest';\nimport { loadConfigSearch } from '@/api';\nimport { forEachRegistry } from '@/commands/utils';\nimport {\n\ttype BuildResult,\n\tbuildRegistry,\n\ttype RemoteDependency,\n\ttype ResolvedItem,\n} from '@/utils/build';\nimport type { AbsolutePath, ItemRelativePath } from '@/utils/types';\nimport { LanguageNotFoundWarning } from '@/utils/warnings';\n\nconst cwd = path.join(__dirname, './fixtures/build') as AbsolutePath;\n\ndescribe('buildRegistry', () => {\n\tlet firstRegistry: BuildResult;\n\tlet warnSpy: MockInstance;\n\n\tbeforeAll(async () => {\n\t\twarnSpy = vi.spyOn(log, 'warn').mockImplementation(() => undefined);\n\n\t\tconst config = await loadConfigSearch({ cwd, promptForContinueIfNull: false });\n\t\tif (config === null) throw new Error('Config not found');\n\n\t\tconst results = await forEachRegistry(\n\t\t\tconfig.config,\n\t\t\tasync (registry) => {\n\t\t\t\treturn await buildRegistry(registry, { options: { cwd }, config: config.config });\n\t\t\t},\n\t\t\t{ cwd }\n\t\t);\n\n\t\tconst firstRegistryResult = results[0];\n\t\tassert(firstRegistryResult !== undefined);\n\t\tfirstRegistry = firstRegistryResult.match(\n\t\t\t(v) => v,\n\t\t\t(e) => {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t);\n\t});\n\n\tit('should have correct registry metadata', () => {\n\t\texpect(firstRegistry.name).toBe('@jsrepo/test');\n\t\texpect(firstRegistry.authors).toStrictEqual(['Aidan Bleser']);\n\t\texpect(firstRegistry.bugs).toBe('https://github.com/jsrepojs/jsrepo/issues');\n\t\texpect(firstRegistry.description).toBe('A test registry');\n\t\texpect(firstRegistry.homepage).toBe('https://github.com/jsrepojs/jsrepo');\n\t\texpect(firstRegistry.repository).toBe('https://github.com/jsrepojs/jsrepo');\n\t\texpect(firstRegistry.tags).toStrictEqual(['test', 'registry']);\n\t\texpect(firstRegistry.version).toBe('0.0.1');\n\t\texpect(firstRegistry.defaultPaths).toStrictEqual({\n\t\t\tutils: './src/utils',\n\t\t});\n\t});\n\n\tit('should have the correct number of items', () => {\n\t\texpect(firstRegistry.items).toHaveLength(9);\n\t});\n\n\tdescribe('math item', () => {\n\t\tlet mathItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tmathItem = firstRegistry.items.find((item) => item.name === 'math')!;\n\t\t\tassert(mathItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(mathItem.name).toBe('math');\n\t\t\texpect(mathItem.title).toBeUndefined();\n\t\t\texpect(mathItem.description).toBeUndefined();\n\t\t\texpect(mathItem.type).toBe('utils');\n\t\t\texpect(mathItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have correct metadata', () => {\n\t\t\texpect(mathItem.meta).toStrictEqual({\n\t\t\t\textendedDescription: 'Use this for basic math operations',\n\t\t\t});\n\t\t\texpect(mathItem.categories).toStrictEqual(['math', 'utils']);\n\t\t});\n\n\t\tit('should have correct dependencies', () => {\n\t\t\texpect(mathItem.registryDependencies).toStrictEqual(['stdout']);\n\t\t\texpect(mathItem.dependencies).toStrictEqual([\n\t\t\t\t{ ecosystem: 'js', name: 'chalk', version: '^5.6.2' },\n\t\t\t]);\n\t\t\texpect(mathItem.devDependencies).toStrictEqual([]);\n\t\t});\n\n\t\tit('should have correct files', () => {\n\t\t\texpect(mathItem.files).toHaveLength(4);\n\t\t\texpect(mathItem.files.map((f) => f.path)).toEqual([\n\t\t\t\t'math/add.ts',\n\t\t\t\t'math/answer-format.ts',\n\t\t\t\t'math/add.test.ts',\n\t\t\t\t'math/answer-format.test.ts',\n\t\t\t]);\n\t\t});\n\n\t\tit('should detect registry imports in files', () => {\n\t\t\tconst answerFormatFile = mathItem.files.find((f) => f.path === 'math/answer-format.ts');\n\t\t\texpect(answerFormatFile).toBeDefined();\n\t\t\texpect(answerFormatFile!._imports_).toHaveLength(1);\n\t\t\texpect(answerFormatFile!._imports_[0]).toMatchObject({\n\t\t\t\timport: '../stdout',\n\t\t\t\titem: 'stdout',\n\t\t\t\tfile: { type: 'utils', path: 'stdout.ts' as ItemRelativePath },\n\t\t\t\tmeta: {},\n\t\t\t});\n\t\t});\n\n\t\tit('should detect test file type', () => {\n\t\t\tconst addTestFile = mathItem.files.find((f) => f.path === 'math/add.test.ts');\n\t\t\texpect(addTestFile).toBeDefined();\n\t\t\texpect(addTestFile!.role).toBe('test');\n\t\t\tconst answerFormatTestFile = mathItem.files.find(\n\t\t\t\t(f) => f.path === 'math/answer-format.test.ts'\n\t\t\t);\n\t\t\texpect(answerFormatTestFile).toBeDefined();\n\t\t\texpect(answerFormatTestFile!.role).toBe('test');\n\t\t});\n\n\t\tit('should detect dependencies in test files and separate them from the main item', () => {\n\t\t\tconst testFile = mathItem.files.find((f) => f.path === 'math/add.test.ts');\n\t\t\texpect(testFile).toBeDefined();\n\t\t\texpect(testFile!.dependencies).toStrictEqual([\n\t\t\t\t{ ecosystem: 'js', name: 'vitest', version: undefined },\n\t\t\t]);\n\t\t});\n\t});\n\n\tdescribe('stdout item', () => {\n\t\tlet stdoutItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tstdoutItem = firstRegistry.items.find((item) => item.name === 'stdout')!;\n\t\t\tassert(stdoutItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(stdoutItem.name).toBe('stdout');\n\t\t\texpect(stdoutItem.title).toBeUndefined();\n\t\t\texpect(stdoutItem.description).toBeUndefined();\n\t\t\texpect(stdoutItem.type).toBe('utils');\n\t\t\texpect(stdoutItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have the correct dependencies', () => {\n\t\t\t// this also implicitly test that the utils item is properly detected\n\t\t\t// since utils has the same name as the utils directory it can be tricky to resolve when importing it without an extension\n\t\t\texpect(stdoutItem.registryDependencies).toStrictEqual(['utils']);\n\t\t\texpect(stdoutItem.dependencies).toStrictEqual([]);\n\t\t\texpect(stdoutItem.devDependencies).toStrictEqual([]);\n\t\t});\n\n\t\tit('should have a single file', () => {\n\t\t\texpect(stdoutItem.files).toHaveLength(1);\n\t\t\tconst stdoutFile = stdoutItem.files[0];\n\t\t\tassert(stdoutFile !== undefined);\n\t\t\texpect(stdoutFile.path).toBe('stdout.ts');\n\t\t});\n\t});\n\n\tdescribe('shiki item', () => {\n\t\tlet shikiItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tshikiItem = firstRegistry.items.find((item) => item.name === 'shiki')!;\n\t\t\tassert(shikiItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(shikiItem.name).toBe('shiki');\n\t\t\texpect(shikiItem.title).toBeUndefined();\n\t\t\texpect(shikiItem.description).toBeUndefined();\n\t\t\texpect(shikiItem.type).toBe('utils');\n\t\t\texpect(shikiItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have correct dependencies', () => {\n\t\t\texpect(shikiItem.registryDependencies).toStrictEqual([]);\n\t\t\texpect(shikiItem.dependencies).toStrictEqual([\n\t\t\t\t{ ecosystem: 'js', name: 'shiki', version: undefined },\n\t\t\t\t// detected from dynamic imports\n\t\t\t\t{ ecosystem: 'js', name: '@shikijs/themes', version: undefined },\n\t\t\t\t{ ecosystem: 'js', name: '@shikijs/langs', version: undefined },\n\t\t\t]);\n\t\t\texpect(shikiItem.devDependencies).toStrictEqual([]);\n\t\t});\n\n\t\tit('should have a single file', () => {\n\t\t\texpect(shikiItem.files).toHaveLength(1);\n\t\t\tconst shikiFile = shikiItem.files[0];\n\t\t\tassert(shikiFile !== undefined);\n\t\t\texpect(shikiFile.path).toBe('shiki.ts');\n\t\t});\n\t});\n\n\tdescribe('button item', () => {\n\t\tlet buttonItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tbuttonItem = firstRegistry.items.find((item) => item.name === 'button')!;\n\t\t\tassert(buttonItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(buttonItem.name).toBe('button');\n\t\t\texpect(buttonItem.title).toBe('Button');\n\t\t\texpect(buttonItem.description).toBe('An awesome button component');\n\t\t\texpect(buttonItem.type).toBe('ui');\n\t\t\texpect(buttonItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have no dependencies', () => {\n\t\t\texpect(buttonItem.registryDependencies).toStrictEqual([]);\n\t\t\texpect(buttonItem.dependencies).toStrictEqual([]);\n\t\t\texpect(buttonItem.devDependencies).toStrictEqual([]);\n\t\t});\n\n\t\tit('should have files including glob pattern matches', () => {\n\t\t\t// The button item uses glob patterns, so it should have more than just the main file\n\t\t\texpect(buttonItem.files.length).toBeGreaterThanOrEqual(3);\n\t\t\tconst buttonFile = buttonItem.files[0];\n\t\t\tassert(buttonFile !== undefined);\n\t\t\texpect(buttonFile.path).toBe('button.tsx');\n\t\t});\n\t});\n\n\tdescribe('counter item', () => {\n\t\tlet counterItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tcounterItem = firstRegistry.items.find((item) => item.name === 'counter')!;\n\t\t\tassert(counterItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(counterItem.name).toBe('counter');\n\t\t\texpect(counterItem.title).toBeUndefined();\n\t\t\texpect(counterItem.description).toBeUndefined();\n\t\t\texpect(counterItem.type).toBe('ui');\n\t\t\texpect(counterItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have correct registry dependencies', () => {\n\t\t\texpect(counterItem.registryDependencies).toStrictEqual(['math']);\n\t\t\texpect(counterItem.dependencies).toStrictEqual([]);\n\t\t\texpect(counterItem.devDependencies).toStrictEqual([]);\n\t\t});\n\n\t\tit('should have a single file', () => {\n\t\t\texpect(counterItem.files).toHaveLength(1);\n\t\t\tconst counterFile = counterItem.files[0];\n\t\t\tassert(counterFile !== undefined);\n\t\t\texpect(counterFile.path).toBe('counter.svelte');\n\t\t});\n\n\t\tit('should detect registry imports in files', () => {\n\t\t\tconst counterFile = counterItem.files[0];\n\t\t\tassert(counterFile !== undefined);\n\t\t\texpect(counterFile._imports_).toHaveLength(1);\n\t\t\texpect(counterFile._imports_[0]).toMatchObject({\n\t\t\t\timport: '../../utils/math/add',\n\t\t\t\titem: 'math',\n\t\t\t\tfile: { type: 'utils', path: 'math/add.ts' as ItemRelativePath },\n\t\t\t\tmeta: {},\n\t\t\t});\n\t\t});\n\t});\n\n\tdescribe('demo-page item', () => {\n\t\tlet demoPageItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tdemoPageItem = firstRegistry.items.find((item) => item.name === 'demo-page')!;\n\t\t\tassert(demoPageItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(demoPageItem.name).toBe('demo-page');\n\t\t\texpect(demoPageItem.title).toBeUndefined();\n\t\t\texpect(demoPageItem.description).toBeUndefined();\n\t\t\texpect(demoPageItem.type).toBe('page');\n\t\t\texpect(demoPageItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have the page files', () => {\n\t\t\tconst serverFile = demoPageItem.files.find((f) => f.path === 'demo/+page.server.ts');\n\t\t\texpect(serverFile).toBeDefined();\n\t\t\texpect(serverFile!.target).toBe('src/routes/demo/+page.server.ts');\n\t\t\tconst clientFile = demoPageItem.files.find((f) => f.path === 'demo/+page.svelte');\n\t\t\texpect(clientFile).toBeDefined();\n\t\t\texpect(clientFile!.target).toBe('src/routes/demo/+page.svelte');\n\t\t});\n\t});\n\n\tdescribe('empty item', () => {\n\t\tlet emptyItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\temptyItem = firstRegistry.items.find((item) => item.name === 'empty')!;\n\t\t\tassert(emptyItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(emptyItem.name).toBe('empty');\n\t\t\texpect(emptyItem.title).toBeUndefined();\n\t\t\texpect(emptyItem.description).toBeUndefined();\n\t\t\texpect(emptyItem.type).toBe('ui');\n\t\t\texpect(emptyItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should have the correct files', () => {\n\t\t\texpect(emptyItem.files).toHaveLength(3);\n\t\t\tconst emptyContentFile = emptyItem.files.find(\n\t\t\t\t(f) => f.path === 'empty/empty-content.svelte'\n\t\t\t);\n\t\t\texpect(emptyContentFile).toBeDefined();\n\t\t\texpect(emptyContentFile!.role).toBe('file');\n\t\t\texpect(emptyContentFile!.type).toBe('ui');\n\t\t\tconst emptyFile = emptyItem.files.find((f) => f.path === 'empty/empty.svelte');\n\t\t\texpect(emptyFile).toBeDefined();\n\t\t\texpect(emptyFile!.role).toBe('file');\n\t\t\texpect(emptyFile!.type).toBe('ui');\n\t\t\tconst indexFile = emptyItem.files.find((f) => f.path === 'empty/index.ts');\n\t\t\texpect(indexFile).toBeDefined();\n\t\t\texpect(indexFile!.role).toBe('file');\n\t\t\texpect(indexFile!.type).toBe('ui');\n\t\t});\n\n\t\tit('should not have duplicated dependencies', () => {\n\t\t\texpect(emptyItem.registryDependencies).toStrictEqual([]);\n\t\t\texpect(emptyItem.dependencies).toStrictEqual([\n\t\t\t\t{\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\tname: '@lucide/svelte',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t]);\n\t\t\texpect(emptyItem.devDependencies).toStrictEqual([]);\n\t\t});\n\t});\n\n\tdescribe('lanyard item (with binary asset files)', () => {\n\t\tlet lanyardItem: ResolvedItem;\n\n\t\tbeforeAll(() => {\n\t\t\tlanyardItem = firstRegistry.items.find((item) => item.name === 'lanyard')!;\n\t\t\tassert(lanyardItem !== undefined);\n\t\t});\n\n\t\tit('should have correct basic properties', () => {\n\t\t\texpect(lanyardItem.name).toBe('lanyard');\n\t\t\texpect(lanyardItem.type).toBe('ui');\n\t\t\texpect(lanyardItem.add).toBe('when-added');\n\t\t});\n\n\t\tit('should include binary asset files (.glb, .png) without errors', () => {\n\t\t\texpect(lanyardItem.files).toHaveLength(3);\n\t\t\texpect(lanyardItem.files.map((f) => f.path).sort()).toEqual([\n\t\t\t\t'lanyard/Lanyard.tsx',\n\t\t\t\t'lanyard/card.glb',\n\t\t\t\t'lanyard/lanyard.png',\n\t\t\t]);\n\t\t});\n\n\t\tit('should have content for all files including binary assets', () => {\n\t\t\tfor (const file of lanyardItem.files) {\n\t\t\t\texpect(file.content).toBeDefined();\n\t\t\t\texpect(file.content.length).toBeGreaterThan(0);\n\t\t\t}\n\t\t});\n\n\t\tit('should not have dependencies from binary files', () => {\n\t\t\t// Binary files don't have dependencies since they're skipped\n\t\t\t// Only the .tsx file's dependencies should be detected\n\t\t\texpect(lanyardItem.registryDependencies).toStrictEqual([]);\n\t\t\t// react is excluded in the config\n\t\t\texpect(lanyardItem.dependencies).toStrictEqual([]);\n\t\t\texpect(lanyardItem.devDependencies).toStrictEqual([]);\n\t\t});\n\n\t\tit('should not warn about binary asset files (.glb, .png)', () => {\n\t\t\tconst calls = warnSpy.mock.calls;\n\t\t\tconst binaryFileWarnings = calls.filter((call) => {\n\t\t\t\tconst warning = call[0];\n\t\t\t\treturn (\n\t\t\t\t\twarning instanceof LanguageNotFoundWarning &&\n\t\t\t\t\t(warning.path.includes('.glb') || warning.path.includes('.png'))\n\t\t\t\t);\n\t\t\t});\n\t\t\texpect(binaryFileWarnings).toStrictEqual([]);\n\t\t});\n\t});\n\n\tdescribe('glob pattern functionality', () => {\n\t\tit('should handle simple glob patterns (button-*.tsx)', () => {\n\t\t\tconst buttonItem = firstRegistry.items.find((item) => item.name === 'button');\n\t\t\tassert(buttonItem !== undefined);\n\n\t\t\t// Simple glob pattern should match files in the same directory\n\t\t\tconst exampleFile1 = buttonItem.files.find((f) => f.path === 'button-default.tsx');\n\t\t\texpect(exampleFile1).toBeDefined();\n\t\t\texpect(exampleFile1!.role).toBe('example');\n\n\t\t\tconst exampleFile2 = buttonItem.files.find((f) => f.path === 'button-loading.tsx');\n\t\t\texpect(exampleFile2).toBeDefined();\n\t\t\texpect(exampleFile2!.role).toBe('example');\n\t\t});\n\n\t\tit('should preserve subdirectory structure for recursive glob patterns (subdir/**/*.tsx)', () => {\n\t\t\tconst buttonItem = firstRegistry.items.find((item) => item.name === 'button');\n\t\t\tassert(buttonItem !== undefined);\n\n\t\t\t// Recursive glob pattern should match files in subdirectories and preserve path structure\n\t\t\tconst subdirFile = buttonItem.files.find((f) => f.path === 'button-subdir.tsx');\n\t\t\texpect(subdirFile).toBeDefined();\n\t\t\texpect(subdirFile!.role).toBe('example');\n\t\t\texpect(subdirFile!.path).toBe('button-subdir.tsx'); // Should preserve subdirectory\n\n\t\t\tconst nestedFile = buttonItem.files.find((f) => f.path === 'nested/button-nested.tsx');\n\t\t\texpect(nestedFile).toBeDefined();\n\t\t\texpect(nestedFile!.role).toBe('example');\n\t\t\texpect(nestedFile!.path).toBe('nested/button-nested.tsx'); // Should preserve nested subdirectory\n\t\t});\n\n\t\tit('should handle glob patterns in nested folder files', () => {\n\t\t\t// This test verifies that glob patterns work when used within folder file definitions\n\t\t\t// The math item uses folder files, so we can test this there if needed\n\t\t\tconst mathItem = firstRegistry.items.find((item) => item.name === 'math');\n\t\t\tassert(mathItem !== undefined);\n\n\t\t\t// Verify that folder files work correctly (not glob-related, but ensures compatibility)\n\t\t\texpect(mathItem.files.length).toBeGreaterThan(0);\n\t\t});\n\t});\n\n\tdescribe('build.remoteDependencyResolver', () => {\n\t\tasync function buildWithResolver(\n\t\t\tresolver: (\n\t\t\t\tdep: RemoteDependency,\n\t\t\t\toptions: { cwd: AbsolutePath }\n\t\t\t) => RemoteDependency | Promise<RemoteDependency>\n\t\t) {\n\t\t\tconst config = await loadConfigSearch({ cwd, promptForContinueIfNull: false });\n\t\t\tif (config === null) throw new Error('Config not found');\n\t\t\tconfig.config.build ??= {};\n\t\t\tconfig.config.build.remoteDependencyResolver = resolver;\n\n\t\t\tconst results = await forEachRegistry(\n\t\t\t\tconfig.config,\n\t\t\t\tasync (registry) => {\n\t\t\t\t\treturn await buildRegistry(registry, {\n\t\t\t\t\t\toptions: { cwd },\n\t\t\t\t\t\tconfig: config.config,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{ cwd }\n\t\t\t);\n\t\t\tconst first = results[0];\n\t\t\tassert(first !== undefined);\n\t\t\treturn first;\n\t\t}\n\n\t\tit('should rewrite resolved remote dependency versions', async () => {\n\t\t\tconst result = await buildWithResolver((dep) =>\n\t\t\t\tdep.name === 'chalk' ? { ...dep, version: '9.9.9' } : dep\n\t\t\t);\n\t\t\tconst build = result.match(\n\t\t\t\t(v) => v,\n\t\t\t\t(e) => {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t);\n\t\t\tconst math = build.items.find((item) => item.name === 'math');\n\t\t\tassert(math !== undefined);\n\t\t\texpect(math.dependencies).toContainEqual({\n\t\t\t\tecosystem: 'js',\n\t\t\t\tname: 'chalk',\n\t\t\t\tversion: '9.9.9',\n\t\t\t});\n\t\t});\n\n\t\tit('should dedupe dependencies after resolver rewrites keys', async () => {\n\t\t\tconst result = await buildWithResolver((dep) => ({\n\t\t\t\t...dep,\n\t\t\t\tname: 'dedup-target',\n\t\t\t\tversion: '1.0.0',\n\t\t\t}));\n\t\t\tconst build = result.match(\n\t\t\t\t(v) => v,\n\t\t\t\t(e) => {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t);\n\t\t\tconst shiki = build.items.find((item) => item.name === 'shiki');\n\t\t\tassert(shiki !== undefined);\n\t\t\texpect(shiki.dependencies).toStrictEqual([\n\t\t\t\t{\n\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\tname: 'dedup-target',\n\t\t\t\t\tversion: '1.0.0',\n\t\t\t\t},\n\t\t\t]);\n\t\t});\n\n\t\tit('should return a build error when resolver throws', async () => {\n\t\t\tconst result = await buildWithResolver((dep) => {\n\t\t\t\tif (dep.name === 'chalk') throw new Error('boom');\n\t\t\t\treturn dep;\n\t\t\t});\n\t\t\texpect(result.isErr()).toBe(true);\n\t\t\tif (result.isErr()) {\n\t\t\t\texpect(result.error.message).toContain('Failed to resolve remote dependency');\n\t\t\t}\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/config.test.ts",
    "content": "import { defineConfig } from 'jsrepo';\nimport { describe, expect, it } from 'vitest';\n\ndescribe('config', () => {\n\tit('should add default providers', () => {\n\t\tconst config = defineConfig({});\n\t\texpect(config.providers.length > 0).toBe(true);\n\t});\n\n\tit('should override default providers', () => {\n\t\tconst config = defineConfig({\n\t\t\tproviders: [],\n\t\t});\n\t\texpect(config.providers.length === 0).toBe(true);\n\t});\n\n\tit('should add default langs', () => {\n\t\tconst config = defineConfig({});\n\t\texpect(config.languages.length > 0).toBe(true);\n\t});\n\n\tit('should override default langs', () => {\n\t\tconst config = defineConfig({\n\t\t\tlanguages: [],\n\t\t});\n\t\texpect(config.languages.length === 0).toBe(true);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/.npmignore",
    "content": "# This is a test fixture - ignore node_modules and lock files\nnode_modules\npackage-lock.json\npnpm-lock.yaml\nyarn.lock\n\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/.npmrc",
    "content": "# This directory is a test fixture and should not be treated as a workspace package\n# Prevent pnpm from installing dependencies here\nignore-scripts=true\nlink-workspace-packages=false\n\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/jsrepo.config.ts",
    "content": "import { defineConfig } from \"../../../dist/api/index.mjs\";\n\nexport default defineConfig({\n\tregistry: {\n\t\tname: \"@jsrepo/test\",\n\t\tauthors: [\"Aidan Bleser\"],\n\t\tbugs: \"https://github.com/jsrepojs/jsrepo/issues\",\n\t\tdescription: \"A test registry\",\n\t\thomepage: \"https://github.com/jsrepojs/jsrepo\",\n\t\trepository: \"https://github.com/jsrepojs/jsrepo\",\n\t\ttags: [\"test\", \"registry\"],\n\t\tversion: \"0.0.1\",\n\t\texcludeDeps: [\"react\"],\n\t\tdefaultPaths: {\n\t\t\tutils: \"./src/utils\",\n\t\t},\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: \"math\",\n\t\t\t\ttype: \"utils\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/utils/math\",\n\t\t\t\t\t\tfiles: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: \"add.ts\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: \"answer-format.ts\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/utils/math',\n\t\t\t\t\t\trole: 'test',\n\t\t\t\t\t\tfiles: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: 'add.test.ts',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: 'answer-format.test.ts',\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t]\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\tcategories: ['math', 'utils'],\n\t\t\t\tmeta: {\n\t\t\t\t\textendedDescription: 'Use this for basic math operations'\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"stdout\",\n\t\t\t\ttype: \"utils\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/utils/stdout.ts\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"utils\",\n\t\t\t\ttype: \"lib\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/utils.ts\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"shiki\",\n\t\t\t\ttype: \"utils\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/utils/shiki.ts\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"button\",\n\t\t\t\ttitle: \"Button\",\n\t\t\t\tdescription: \"An awesome button component\",\n\t\t\t\ttype: \"ui\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/components/ui/button.tsx\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/routes/demos/button-*.tsx',\n\t\t\t\t\t\trole: 'example',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/routes/demos/subdir/**/*.tsx',\n\t\t\t\t\t\trole: 'example',\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"empty\",\n\t\t\t\ttype: \"ui\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/components/ui/empty\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"counter\",\n\t\t\t\ttype: \"ui\",\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: \"src/components/ui/counter.svelte\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'demo-page',\n\t\t\t\ttype: 'page',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/routes/demo',\n\t\t\t\t\t\ttarget: 'src/routes/demo'\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'lanyard',\n\t\t\t\ttype: 'ui',\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'src/components/ui/lanyard',\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t},\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/package.json",
    "content": "{\n\t\"name\": \"test\",\n\t\"private\": true,\n\t\"type\": \"module\",\n\t\"dependencies\": {\n\t\t\"chalk\": \"^5.6.2\"\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/button.tsx",
    "content": "import type React from 'react';\n\nexport function Button(props: React.ComponentProps<'button'>) {\n\treturn <button {...props}>Click me</button>;\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/counter.svelte",
    "content": "<script lang=\"ts\">\nimport { add } from '../../utils/math/add';\n\nlet count = $state(0);\n\nfunction increment() {\n\tcount = add(count, 1);\n}\n</script>\n\n<button onclick={increment}>\n    Count is {count}\n</button>"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/empty/empty-content.svelte",
    "content": "<script lang=\"ts\">\n    import { FileIcon } from '@lucide/svelte'\n</script>\n\n<FileIcon />"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/empty/empty.svelte",
    "content": "<script lang=\"ts\">\n    import { FileIcon } from '@lucide/svelte'\n</script>\n\n<FileIcon />"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/empty/index.ts",
    "content": "import EmptyContent from './empty-content.svelte';\nimport Empty from './empty.svelte';\n\nexport { EmptyContent, Empty };"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/lanyard/Lanyard.tsx",
    "content": "import React from 'react';\n\nexport const Lanyard = () => {\n\treturn <div>Lanyard Component</div>;\n};\n\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/components/ui/lanyard/card.glb",
    "content": "binary-placeholder\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/routes/demo/+page.server.ts",
    "content": ""
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/routes/demo/+page.svelte",
    "content": ""
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/routes/demos/button-default.tsx",
    "content": ""
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/routes/demos/button-loading.tsx",
    "content": ""
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/routes/demos/subdir/button-subdir.tsx",
    "content": "// subdir example\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/routes/demos/subdir/nested/button-nested.tsx",
    "content": "// nested example\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils/math/add.test.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { add } from \"./add\";\n\ndescribe(\"add\", () => {\n\tit(\"should add two numbers\", () => {\n\t\texpect(add(1, 2)).toBe(3);\n\t});\n});"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils/math/add.ts",
    "content": "export function add(a: number, b: number) {\n\treturn a + b;\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils/math/answer-format.test.ts",
    "content": "\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils/math/answer-format.ts",
    "content": "import color from 'chalk';\nimport { print } from '../stdout';\n\nexport function answerFormat(answer: number) {\n\treturn print(color.green(`The answer is ${answer}`));\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils/shiki.ts",
    "content": "import { createHighlighterCore } from \"shiki/core\";\nimport { createJavaScriptRegexEngine } from \"shiki/engine/javascript\";\n\nexport const highlighter = await createHighlighterCore({\n\tthemes: [\n\t\timport(\"@shikijs/themes/nord\"),\n\t\timport(\"@shikijs/themes/dark-plus\"),\n\t],\n\tlangs: [\n\t\timport(\"@shikijs/langs/typescript\"),\n\t\timport(\"@shikijs/langs/javascript\"),\n\t],\n\tengine: createJavaScriptRegexEngine(),\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils/stdout.ts",
    "content": "import { STDOUT_PREFIX } from \"../utils\";\n\nexport function print(msg: string) {\n\tconsole.log(STDOUT_PREFIX + msg);\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/build/src/utils.ts",
    "content": "export const STDOUT_PREFIX = 'stdout: ';"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/config/basic/jsrepo.config.ts",
    "content": "import { defineConfig } from 'jsrepo';\n\nexport default defineConfig({});\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/config/mts/jsrepo.config.mts",
    "content": "import { defineConfig } from 'jsrepo';\n\nexport default defineConfig({});\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/config/nested/jsrepo.config.ts",
    "content": "import { defineConfig } from 'jsrepo';\n\nexport default defineConfig({});\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js/logger.ts",
    "content": "import color from 'picocolors';\nimport { print } from './stdout';\n\nexport class Logger {\n\twarn(msg: string) {\n\t\tprint(color.yellow(msg));\n\t}\n\n\terror(msg: string) {\n\t\tprint(color.red(msg));\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js/math/add.ts",
    "content": "export function add(a: number, b: number) {\n\treturn a + b;\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js/math/subtract.ts",
    "content": "export function subtract(a: number, b: number) {\n\treturn a - b;\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js/print-answer.ts",
    "content": "import type { add } from './math/add';\nimport type { subtract } from './math/subtract';\nimport { print } from './stdout';\n\nexport function printAnswer(fn: typeof add | typeof subtract) {\n\tprint(String(fn(1, 2)));\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js/stdout.ts",
    "content": "export function print(msg: string) {\n\tconsole.log(msg);\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-arbitrary-extensions/component.svelte",
    "content": "<script lang=\"ts\">\n\texport let name = 'world';\n</script>\n\n<h1>Hello {name}</h1>\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-arbitrary-extensions/config.json",
    "content": "{\n\t\"answer\": 42\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-arbitrary-extensions/index.ts",
    "content": "import config from './config';\n\nexport const answer = config.answer;\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-arbitrary-extensions/svelte-entry.ts",
    "content": "import Component from './component';\n\nexport default Component;\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-baseurl-bare-imports/package.json",
    "content": "{\n\t\"name\": \"js-baseurl-bare-imports-fixture\",\n\t\"dependencies\": {\n\t\t\"bun\": \"^1.0.0\"\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-baseurl-bare-imports/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"baseUrl\": \".\"\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-baseurl-bare-imports/types.ts",
    "content": "import type { BunRequest } from 'bun';\n\nexport type BunRouteHandler = (req: BunRequest) => Response | Promise<Response>;\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-subpath-imports/index.ts",
    "content": "import { print } from '#utils/print';\nimport { version } from '#meta';\nimport { createStore } from '#state';\n\nprint(version, createStore);\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-subpath-imports/package.json",
    "content": "{\n\t\"name\": \"js-subpath-imports-fixture\",\n\t\"type\": \"module\",\n\t\"imports\": {\n\t\t\"#utils/*\": [\n\t\t\t\"./src/missing/*.ts\",\n\t\t\t\"./src/utils/*.ts\"\n\t\t],\n\t\t\"#meta\": {\n\t\t\t\"development\": \"./src/dev-meta.ts\",\n\t\t\t\"default\": \"./src/meta.ts\"\n\t\t},\n\t\t\"#state\": \"zustand/vanilla\"\n\t},\n\t\"dependencies\": {\n\t\t\"zustand\": \"^5.0.0\"\n\t}\n}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-subpath-imports/src/meta.ts",
    "content": "export const version = '0.0.0';\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/js-subpath-imports/src/utils/print.ts",
    "content": "export function print(..._args: unknown[]) {}\n"
  },
  {
    "path": "packages/jsrepo/tests/fixtures/langs/svelte/page.svelte",
    "content": "<script lang=\"ts\">\nimport { onMount } from 'svelte';\nimport { Logger } from '../js/logger';\n\nonMount(() => {\n\tLogger.warn('Mounted');\n});\n</script>\n\n<button onclick={() => Logger.warn('Warning')}>\n    Warn\n</button>"
  },
  {
    "path": "packages/jsrepo/tests/langs/js.test.ts",
    "content": "import fs from 'node:fs';\nimport path from 'pathe';\nimport { describe, expect, it, vi } from 'vitest';\nimport { getImports, js } from '@/langs/js';\nimport { joinAbsolute } from '@/utils/path';\nimport type { AbsolutePath, ItemRelativePath } from '@/utils/types';\n\nconst CWD = path.join(__dirname, '../fixtures/langs/js') as AbsolutePath;\nconst SUBPATH_IMPORTS_CWD = path.join(\n\t__dirname,\n\t'../fixtures/langs/js-subpath-imports'\n) as AbsolutePath;\nconst BASE_URL_BARE_IMPORTS_CWD = path.join(\n\t__dirname,\n\t'../fixtures/langs/js-baseurl-bare-imports'\n) as AbsolutePath;\nconst ARBITRARY_EXTENSIONS_CWD = path.join(\n\t__dirname,\n\t'../fixtures/langs/js-arbitrary-extensions'\n) as AbsolutePath;\n\ndescribe('js', () => {\n\tit('should resolve dependencies', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(CWD, 'logger.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies[0]?.import).toBe('./stdout');\n\t\texpect(result.devDependencies[0]?.name).toBe('picocolors');\n\t\texpect(result.devDependencies[0]?.version).toBe('catalog:');\n\t});\n\n\tit('should resolve dependencies to the correct path', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(CWD, 'print-answer.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: joinAbsolute(CWD, 'print-answer.ts'),\n\t\t\tcwd: CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies[0]?.import).toBe('./math/add');\n\t\texpect(result.localDependencies[1]?.import).toBe('./math/subtract');\n\t\texpect(result.localDependencies[2]?.import).toBe('./stdout');\n\t});\n\n\tit('should resolve dependencies using package.json subpath imports', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(SUBPATH_IMPORTS_CWD, 'index.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: SUBPATH_IMPORTS_CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies.map((dep) => dep.import)).toStrictEqual([\n\t\t\t'#utils/print',\n\t\t\t'#meta',\n\t\t]);\n\t\texpect(result.localDependencies.map((dep) => dep.fileName)).toStrictEqual([\n\t\t\tjoinAbsolute(SUBPATH_IMPORTS_CWD, 'src/utils/print.ts'),\n\t\t\tjoinAbsolute(SUBPATH_IMPORTS_CWD, 'src/meta.ts'),\n\t\t]);\n\t\texpect(result.dependencies).toStrictEqual([\n\t\t\t{\n\t\t\t\tecosystem: 'js',\n\t\t\t\tname: 'zustand',\n\t\t\t\tversion: '^5.0.0',\n\t\t\t},\n\t\t]);\n\t\texpect(result.devDependencies).toStrictEqual([]);\n\t\texpect(warn).not.toHaveBeenCalled();\n\t});\n\n\tit('should not resolve bare imports to lock files when baseUrl is set', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(BASE_URL_BARE_IMPORTS_CWD, 'types.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: BASE_URL_BARE_IMPORTS_CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies).toStrictEqual([]);\n\t\texpect(result.dependencies).toStrictEqual([\n\t\t\t{\n\t\t\t\tecosystem: 'js',\n\t\t\t\tname: 'bun',\n\t\t\t\tversion: '^1.0.0',\n\t\t\t},\n\t\t]);\n\t\texpect(result.devDependencies).toStrictEqual([]);\n\t\texpect(warn).not.toHaveBeenCalled();\n\t});\n\n\tit('should resolve extensionless local imports to json files', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(ARBITRARY_EXTENSIONS_CWD, 'index.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: ARBITRARY_EXTENSIONS_CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies.map((dep) => dep.import)).toStrictEqual(['./config']);\n\t\texpect(result.localDependencies.map((dep) => dep.fileName)).toStrictEqual([\n\t\t\tjoinAbsolute(ARBITRARY_EXTENSIONS_CWD, 'config.json'),\n\t\t]);\n\t\texpect(result.dependencies).toStrictEqual([]);\n\t\texpect(result.devDependencies).toStrictEqual([]);\n\t\texpect(warn).not.toHaveBeenCalled();\n\t});\n\n\tit('should resolve extensionless local imports to svelte files', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(ARBITRARY_EXTENSIONS_CWD, 'svelte-entry.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: ARBITRARY_EXTENSIONS_CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies.map((dep) => dep.import)).toStrictEqual(['./component']);\n\t\texpect(result.localDependencies.map((dep) => dep.fileName)).toStrictEqual([\n\t\t\tjoinAbsolute(ARBITRARY_EXTENSIONS_CWD, 'component.svelte'),\n\t\t]);\n\t\texpect(result.dependencies).toStrictEqual([]);\n\t\texpect(result.devDependencies).toStrictEqual([]);\n\t\texpect(warn).not.toHaveBeenCalled();\n\t});\n\n\tit('should exclude excluded dependencies', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(CWD, 'logger.ts');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await js().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: CWD,\n\t\t\texcludeDeps: ['picocolors'],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies[0]?.import).toBe('./stdout');\n\t\texpect(result.devDependencies[0]?.name).toBe(undefined);\n\t\texpect(result.devDependencies[0]?.version).toBe(undefined);\n\t});\n\n\tit('should transform imports correctly', async () => {\n\t\tconst result = await js().transformImports(\n\t\t\t`import { button } from '$lib/components/ui/button';\nimport { useClipboard } from '$lib/hooks/use-clipboard.svelte';\nimport { utils } from '$lib/utils.js';\nimport { add } from '../../utils/math/add.js';\nimport { Separator } from '@/registry/new-york-v4/ui/separator';\nimport foo from './foo.js';\nimport bar from './bar';\nimport nestedFoo from './nested/foo.js';\nimport nestedBar from './nested/bar';`,\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\timport: '$lib/components/ui/button',\n\t\t\t\t\titem: 'button',\n\t\t\t\t\tfile: { type: 'util', path: 'button/index.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: '$lib/hooks/use-clipboard.svelte',\n\t\t\t\t\titem: 'use-clipboard',\n\t\t\t\t\tfile: { type: 'util', path: 'use-clipboard.svelte.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: '$lib/utils.js',\n\t\t\t\t\titem: 'utils',\n\t\t\t\t\tfile: { type: 'util', path: 'utils.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: '../../utils/math/add.js',\n\t\t\t\t\titem: 'math',\n\t\t\t\t\tfile: { type: 'util', path: 'math/add.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: './foo.js',\n\t\t\t\t\titem: 'foo',\n\t\t\t\t\tfile: { type: 'util', path: 'Foo.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: './bar',\n\t\t\t\t\titem: 'bar',\n\t\t\t\t\tfile: { type: 'util', path: 'Bar.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: './nested/foo.js',\n\t\t\t\t\titem: 'nestedFoo',\n\t\t\t\t\tfile: { type: 'util', path: 'Nested/Foo.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\timport: './nested/bar',\n\t\t\t\t\titem: 'nestedBar',\n\t\t\t\t\tfile: { type: 'util', path: 'Nested/Bar.ts' as ItemRelativePath },\n\t\t\t\t\tmeta: {},\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\tcwd: CWD,\n\t\t\t\titem: 'button',\n\t\t\t\tfile: { type: 'util', path: 'button/index.ts' as ItemRelativePath },\n\t\t\t\tgetItemPath: (item) => {\n\t\t\t\t\tswitch (item.item) {\n\t\t\t\t\t\tcase 'button':\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tpath: 'src/lib/components/shadcn-svelte-extras/ui',\n\t\t\t\t\t\t\t\talias: '$lib/components/shadcn-svelte-extras/ui',\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\tcase 'use-clipboard':\n\t\t\t\t\t\t\treturn { path: 'src/lib/hooks', alias: '$lib/hooks' };\n\t\t\t\t\t\tcase 'utils':\n\t\t\t\t\t\t\treturn { path: 'src/lib', alias: '$lib' };\n\t\t\t\t\t\tcase 'math':\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tpath: 'src/lib/utils',\n\t\t\t\t\t\t\t\talias: '$lib/utils',\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\tcase 'foo':\n\t\t\t\t\t\t\treturn { path: 'src/lib/utils', alias: '$lib/utils' };\n\t\t\t\t\t\tcase 'bar':\n\t\t\t\t\t\t\treturn { path: 'src/lib/utils', alias: '$lib/utils' };\n\t\t\t\t\t\tcase 'nestedFoo':\n\t\t\t\t\t\t\treturn { path: 'src/lib/utils', alias: '$lib/utils' };\n\t\t\t\t\t\tcase 'nestedBar':\n\t\t\t\t\t\t\treturn { path: 'src/lib/utils', alias: '$lib/utils' };\n\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\tif (item.file.type === 'ui')\n\t\t\t\t\t\t\t\treturn { path: 'src/components/ui', alias: '@/components/ui' };\n\t\t\t\t\t\t\tif (item.file.type === 'component')\n\t\t\t\t\t\t\t\treturn { path: 'src/components', alias: '@/components' };\n\t\t\t\t\t\t\tthrow new Error(`Unknown item: ${item}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttargetPath: joinAbsolute(CWD, 'src/lib/components/ui/copy-button/copy-button.tsx'),\n\t\t\t}\n\t\t);\n\t\texpect(\n\t\t\tresult\n\t\t).toStrictEqual(`import { button } from '$lib/components/shadcn-svelte-extras/ui/button';\nimport { useClipboard } from '$lib/hooks/use-clipboard.svelte';\nimport { utils } from '$lib/utils.js';\nimport { add } from '$lib/utils/math/add.js';\nimport { Separator } from '@/components/ui/separator';\nimport foo from '$lib/utils/Foo.js';\nimport bar from '$lib/utils/Bar';\nimport nestedFoo from '$lib/utils/Nested/Foo.js';\nimport nestedBar from '$lib/utils/Nested/Bar';`);\n\t});\n});\n\ndescribe('getImports', () => {\n\tit('should get all the correct imports', async () => {\n\t\tconst code = `import { add } from './math/add';\nexport { subtract } from './math/subtract';\n\nconst thing = import('./thing');`;\n\t\tconst warn = vi.fn();\n\t\tconst result = await getImports(code, {\n\t\t\tfileName: joinAbsolute(CWD, 'logger.ts'),\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result).toStrictEqual(['./math/add', './thing', './math/subtract']);\n\t});\n\n\tit('should skip and warn on unresolvable dynamic imports', async () => {\n\t\tconst code = `import { add } from './math/add';\nexport { subtract } from './math/subtract';\n\nconst thing = import('./thing');\n\nconst foo = 'bar';\nconst thing2 = import(\\`foos/\\${foo}\\`);`;\n\t\tconst warn = vi.fn();\n\t\tconst result = await getImports(code, {\n\t\t\tfileName: joinAbsolute(CWD, 'logger.ts'),\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result).toStrictEqual(['./math/add', './thing', './math/subtract']);\n\t\texpect(warn).toHaveBeenCalledOnce();\n\t});\n\n\tit('should treat backticks as resolvable so long as they are literals', async () => {\n\t\tconst code = `import { add } from './math/add';\nexport { subtract } from './math/subtract';\n\nconst thing = import('./thing');\n\nconst foo = 'bar';\nconst thing2 = import(\\`./foo\\`);`;\n\t\tconst warn = vi.fn();\n\t\tconst result = await getImports(code, {\n\t\t\tfileName: joinAbsolute(CWD, 'logger.ts'),\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result).toStrictEqual(['./math/add', './thing', './foo', './math/subtract']);\n\t\texpect(warn).toHaveBeenCalledTimes(0);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/langs/svelte.test.ts",
    "content": "import fs from 'node:fs';\nimport path from 'pathe';\nimport { describe, expect, it, vi } from 'vitest';\nimport { svelte } from '@/langs/svelte';\nimport { joinAbsolute } from '@/utils/path';\nimport type { AbsolutePath } from '@/utils/types';\n\nconst CWD = path.join(__dirname, '../fixtures/langs/svelte') as AbsolutePath;\n\ndescribe('svelte', () => {\n\tit('should resolve dependencies', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(CWD, 'page.svelte');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await svelte().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: CWD,\n\t\t\texcludeDeps: [],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies[0]?.import).toBe('../js/logger');\n\t\texpect(result.dependencies[0]?.name).toBe('svelte');\n\t\texpect(result.dependencies[0]?.version).toBe(undefined);\n\t});\n\n\tit('should exclude excluded dependencies', async () => {\n\t\tconst warn = vi.fn();\n\t\tconst absolutePath = joinAbsolute(CWD, 'page.svelte');\n\t\tconst code = fs.readFileSync(absolutePath, 'utf-8');\n\t\tconst result = await svelte().resolveDependencies(code, {\n\t\t\tfileName: absolutePath,\n\t\t\tcwd: CWD,\n\t\t\texcludeDeps: ['svelte'],\n\t\t\twarn,\n\t\t});\n\n\t\texpect(result.localDependencies[0]?.import).toBe('../js/logger');\n\t\texpect(result.dependencies[0]?.name).toBe(undefined);\n\t\texpect(result.dependencies[0]?.version).toBe(undefined);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/providers/azure.test.ts",
    "content": "import { describe, it } from 'vitest';\nimport { azure } from '@/providers';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('azure', () => {\n\tit('correctly resolves repository url', async () => {\n\t\tconst az = azure();\n\t\tconst azureState = await az.create('azure/ieedan/std/std', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait azureState.fetch('README.md', { token: undefined });\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/providers/bitbucket.test.ts",
    "content": "import { describe, it } from 'vitest';\nimport { bitbucket } from '@/providers';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('bitbucket', () => {\n\tit('correctly resolves repository url', async () => {\n\t\tconst bb = bitbucket();\n\t\tconst bitbucketState = await bb.create('bitbucket/ieedan/std', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait bitbucketState.fetch('README.md', { token: undefined });\n\t});\n\n\tit('correctly resolves repository url with custom baseUrl', async () => {\n\t\tconst bb = bitbucket();\n\t\tconst bitbucketState = await bb.create('bitbucket:https://bitbucket.org/ieedan/std', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait bitbucketState.fetch('README.md', { token: undefined });\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/providers/fs.test.ts",
    "content": "import { describe, it } from 'vitest';\nimport { fs } from '@/providers';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('fs', () => {\n\tit('correctly resolves path', async () => {\n\t\tconst f = fs();\n\t\tconst fState = await f.create('fs://./tests/providers', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait fState.fetch('fs.test.ts', { token: undefined });\n\t});\n\n\tit('correctly resolves path with baseDir', async () => {\n\t\tconst f = fs({ baseDir: './tests/providers' });\n\t\tconst fState = await f.create('fs://.', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait fState.fetch('fs.test.ts', { token: undefined });\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/providers/github.test.ts",
    "content": "import { describe, it } from 'vitest';\nimport { github } from '@/providers';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('github', () => {\n\tit('correctly resolves repository url', async () => {\n\t\tconst gh = github();\n\t\tconst githubState = await gh.create('github/ieedan/std', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait githubState.fetch('README.md', { token: undefined });\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/providers/gitlab.test.ts",
    "content": "import { describe, it } from 'vitest';\nimport { gitlab } from '@/providers';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('gitlab', () => {\n\tit('correctly resolves repository url', async () => {\n\t\tconst gl = gitlab();\n\t\tconst gitlabState = await gl.create('gitlab/ieedan/std', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait gitlabState.fetch('README.md', { token: undefined });\n\t});\n\n\tit('correctly resolves repository url with custom baseUrl', async () => {\n\t\tconst gl = gitlab();\n\t\tconst gitlabState = await gl.create('gitlab:https://gitlab.com/ieedan/std', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait gitlabState.fetch('README.md', { token: undefined });\n\t});\n\n\tit('correctly resolves nested group urls', async () => {\n\t\tconst gl = gitlab();\n\t\tconst gitlabState = await gl.create('gitlab/jsrepo/tests/test1', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait gitlabState.fetch('README.md', { token: undefined });\n\t});\n\n\tit('correctly resolves nested group urls with ref', async () => {\n\t\tconst gl = gitlab();\n\t\tconst gitlabState = await gl.create('gitlab/jsrepo/tests/test1/-/tree/main', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait gitlabState.fetch('README.md', { token: undefined });\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/providers/http.test.ts",
    "content": "import { describe, expect, it, vi } from 'vitest';\nimport { http } from '@/providers';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('http', () => {\n\tit('correctly resolves url', async () => {\n\t\tconst mockFetch = vi.fn().mockResolvedValue({\n\t\t\tok: true,\n\t\t\ttext: vi.fn().mockResolvedValue('mock content'),\n\t\t\theaders: new Headers(),\n\t\t});\n\n\t\tconst h = http();\n\t\tconst httpState = await h.create('https://example.com', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\n\t\tawait httpState.fetch('jsrepo-manifest.json', {\n\t\t\ttoken: undefined,\n\t\t\tfetch: mockFetch,\n\t\t});\n\n\t\texpect(mockFetch).toHaveBeenCalledTimes(1);\n\t\texpect(mockFetch).toHaveBeenCalledWith(\n\t\t\t'https://example.com/jsrepo-manifest.json',\n\t\t\texpect.objectContaining({\n\t\t\t\theaders: {},\n\t\t\t})\n\t\t);\n\t});\n\n\tit('adds custom headers to the request', async () => {\n\t\tconst mockFetch = vi.fn().mockResolvedValue({\n\t\t\tok: true,\n\t\t\ttext: vi.fn().mockResolvedValue('mock content'),\n\t\t\theaders: new Headers(),\n\t\t});\n\n\t\tconst h = http({\n\t\t\tbaseUrl: 'https://example.com',\n\t\t\theaders: {\n\t\t\t\t'X-Custom-Header': 'custom value',\n\t\t\t},\n\t\t});\n\t\tconst httpState = await h.create('https://example.com', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait httpState.fetch('jsrepo-manifest.json', {\n\t\t\ttoken: undefined,\n\t\t\tfetch: mockFetch,\n\t\t});\n\t\texpect(mockFetch).toHaveBeenCalledTimes(1);\n\t\texpect(mockFetch).toHaveBeenCalledWith(\n\t\t\t'https://example.com/jsrepo-manifest.json',\n\t\t\texpect.objectContaining({\n\t\t\t\theaders: {\n\t\t\t\t\t'X-Custom-Header': 'custom value',\n\t\t\t\t},\n\t\t\t})\n\t\t);\n\t});\n\n\tit('authHeader overrides base provider headers', async () => {\n\t\tconst mockFetch = vi.fn().mockResolvedValue({\n\t\t\tok: true,\n\t\t\ttext: vi.fn().mockResolvedValue('mock content'),\n\t\t\theaders: new Headers(),\n\t\t});\n\n\t\tconst h = http({\n\t\t\tbaseUrl: 'https://example.com',\n\t\t\theaders: {\n\t\t\t\t'X-Custom-Header': 'base value',\n\t\t\t\tAuthorization: 'base-auth-value',\n\t\t\t},\n\t\t\tauthHeader: (token) => ({\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t}),\n\t\t});\n\t\tconst httpState = await h.create('https://example.com', {\n\t\t\tcwd: process.cwd() as AbsolutePath,\n\t\t\ttoken: undefined,\n\t\t});\n\t\tawait httpState.fetch('jsrepo-manifest.json', {\n\t\t\ttoken: 'test-token-123',\n\t\t\tfetch: mockFetch,\n\t\t});\n\t\texpect(mockFetch).toHaveBeenCalledTimes(1);\n\t\texpect(mockFetch).toHaveBeenCalledWith(\n\t\t\t'https://example.com/jsrepo-manifest.json',\n\t\t\texpect.objectContaining({\n\t\t\t\theaders: {\n\t\t\t\t\t'X-Custom-Header': 'base value',\n\t\t\t\t\tAuthorization: 'Bearer test-token-123',\n\t\t\t\t},\n\t\t\t})\n\t\t);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/add-plugins-to-config.test.ts",
    "content": "import dedent from 'dedent';\nimport { describe, expect, it } from 'vitest';\nimport { addPluginsToConfig, parsePluginName } from '@/utils/config/mods/add-plugins';\n\ndescribe('modifyConfig', () => {\n\tit('should return immediately if no plugins are provided', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            transforms: [],\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'transforms',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(configCode);\n\t});\n\n\tit('should modify the config', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            transforms: [],\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'prettier',\n\t\t\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'transforms',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import prettier from '@jsrepo/transform-prettier';\n\n        export default defineConfig({\n            transforms: [prettier()],\n        });\n`);\n\t});\n\n\tit('should modify the config when a function is passed', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig(() => ({\n            transforms: [],\n        }));\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'prettier',\n\t\t\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'transforms',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import prettier from '@jsrepo/transform-prettier';\n\n        export default defineConfig(() => ({\n            transforms: [prettier()],\n        }));\n`);\n\t});\n\n\tit('should modify the config with other keys present', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std'],\n            transforms: [],\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'prettier',\n\t\t\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'transforms',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import prettier from '@jsrepo/transform-prettier';\n\n        export default defineConfig({\n            registries: ['@ieedan/std'],\n            transforms: [prettier()],\n        });\n`);\n\t});\n\n\tit(\"should add the key if it doesn't exist\", async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'prettier',\n\t\t\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'transforms',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import prettier from '@jsrepo/transform-prettier';\n\n        export default defineConfig({\n        \ttransforms: [prettier()]\n        });\n`);\n\t});\n\n\tit('should add defaults to prevent breaking users', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(\n\t\t\tresult._unsafeUnwrap()\n\t\t).toEqual(dedent`import { defineConfig, DEFAULT_LANGS } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n        \tlanguages: [...DEFAULT_LANGS, go()]\n        });\n`);\n\t});\n\n\tit('should modify existing config without removing other plugins', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n            languages: [go()]\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'rust',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-rust',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n        import rust from '@jsrepo/jsrepo-language-rust';\n\n        export default defineConfig({\n            languages: [go(), rust()]\n        });\n`);\n\t});\n\n\tit('should be able to add multiple plugins at once', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            languages: []\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: 'rust',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-rust',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n        import rust from '@jsrepo/jsrepo-language-rust';\n\n        export default defineConfig({\n            languages: [go(), rust()]\n        });\n`);\n\t});\n\n\tit('should not re-add plugins that are already added', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n            languages: [go()]\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n            languages: [go()]\n        });\n`);\n\t});\n\n\tit('should handle a mixture of added and not added plugins', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n            languages: [go()]\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: 'rust',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-rust',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n        import rust from '@jsrepo/jsrepo-language-rust';\n\n        export default defineConfig({\n            languages: [go(), rust()]\n        });\n`);\n\t});\n\n\tit('should use the existing default', async () => {\n\t\tconst configCode = dedent`import { defineConfig, DEFAULT_LANGS } from 'jsrepo';\n\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(\n\t\t\tresult._unsafeUnwrap()\n\t\t).toEqual(dedent`import { defineConfig, DEFAULT_LANGS } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n        \tlanguages: [...DEFAULT_LANGS, go()]\n        });\n`);\n\t});\n\n\tit('should use the aliased version of the default if it was aliased', async () => {\n\t\tconst configCode = dedent`import { defineConfig, DEFAULT_LANGS as langs } from 'jsrepo';\n\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(\n\t\t\tresult._unsafeUnwrap()\n\t\t).toEqual(dedent`import { defineConfig, DEFAULT_LANGS as langs } from 'jsrepo';\n        import go from '@jsrepo/jsrepo-language-go';\n\n        export default defineConfig({\n        \tlanguages: [...langs, go()]\n        });\n`);\n\t});\n\n\tit('will fail if it cannot find the jsrepo import', async () => {\n\t\tconst configCode = dedent`\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'go',\n\t\t\t\t\tpackageName: '@jsrepo/jsrepo-language-go',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'languages',\n\t\t});\n\n\t\texpect(result.isErr()).toBe(true);\n\t});\n\n\tit('will add a comma if the previous key does not end with one', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tregistries: []\n\t\t});\n        `;\n\n\t\tconst result = await addPluginsToConfig({\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'prettier',\n\t\t\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\t\t\tversion: undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t\tkey: 'transforms',\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\t\timport prettier from '@jsrepo/transform-prettier';\n\n\t\texport default defineConfig({\n\t\t\tregistries: [],\n\t\t\ttransforms: [prettier()]\n\t\t});\n`);\n\t});\n});\n\ndescribe('parsePluginName', () => {\n\tit('should parse official plugins using shorthand', () => {\n\t\tconst result = parsePluginName('prettier', 'transform');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'prettier',\n\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\tversion: undefined,\n\t\t});\n\n\t\tconst biomeResult = parsePluginName('biome', 'transform');\n\t\texpect(biomeResult._unsafeUnwrap()).toEqual({\n\t\t\tname: 'biome',\n\t\t\tpackageName: '@jsrepo/transform-biome',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should parse full package names for transform plugins', () => {\n\t\tconst result = parsePluginName('jsrepo-transform-oxfmt', 'transform');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'oxfmt',\n\t\t\tpackageName: 'jsrepo-transform-oxfmt',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should parse scoped package names', () => {\n\t\tconst result = parsePluginName('@example/jsrepo-transform-prettier', 'transform');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'prettier',\n\t\t\tpackageName: '@example/jsrepo-transform-prettier',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should handle camelCase conversion for multi-word plugin names', () => {\n\t\tconst result = parsePluginName('jsrepo-transform-faster-prettier', 'transform');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'fasterPrettier',\n\t\t\tpackageName: 'jsrepo-transform-faster-prettier',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should parse provider plugins', () => {\n\t\tconst result = parsePluginName('jsrepo-provider-jsr', 'provider');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'jsr',\n\t\t\tpackageName: 'jsrepo-provider-jsr',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should parse language plugins', () => {\n\t\tconst result = parsePluginName('jsrepo-language-go', 'language');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'go',\n\t\t\tpackageName: 'jsrepo-language-go',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should handle scoped language plugins', () => {\n\t\tconst result = parsePluginName('@example/jsrepo-language-go', 'language');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'go',\n\t\t\tpackageName: '@example/jsrepo-language-go',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n\n\tit('should parse plugin with version', () => {\n\t\tconst result = parsePluginName('jsrepo-transform-prettier@1.0.0', 'transform');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'prettier',\n\t\t\tpackageName: 'jsrepo-transform-prettier',\n\t\t\tversion: '1.0.0',\n\t\t});\n\t});\n\n\tit('should parse scoped plugin with version', () => {\n\t\tconst result = parsePluginName('@jsrepo/transform-prettier@2.1.0', 'transform');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'prettier',\n\t\t\tpackageName: '@jsrepo/transform-prettier',\n\t\t\tversion: '2.1.0',\n\t\t});\n\t});\n\n\tit('should parse provider plugin with version', () => {\n\t\tconst result = parsePluginName('jsrepo-provider-jsr@0.5.0', 'provider');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'jsr',\n\t\t\tpackageName: 'jsrepo-provider-jsr',\n\t\t\tversion: '0.5.0',\n\t\t});\n\t});\n\n\tit('should parse language plugin with version', () => {\n\t\tconst result = parsePluginName('@example/jsrepo-language-go@3.2.1', 'language');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'go',\n\t\t\tpackageName: '@example/jsrepo-language-go',\n\t\t\tversion: '3.2.1',\n\t\t});\n\t});\n\n\tit('should parse shadcn plugin', () => {\n\t\tconst result = parsePluginName('@jsrepo/shadcn', 'provider');\n\t\texpect(result._unsafeUnwrap()).toEqual({\n\t\t\tname: 'shadcn',\n\t\t\tpackageName: '@jsrepo/shadcn',\n\t\t\tversion: undefined,\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/add-registries.test.ts",
    "content": "import dedent from 'dedent';\nimport { describe, expect, it } from 'vitest';\nimport { addRegistriesToConfig } from '@/utils/config/mods/add-registries';\n\ndescribe('addRegistriesToConfig', () => {\n\tit('should return immediately if no registries are provided', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std'],\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig([], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(configCode);\n\t});\n\n\tit('should modify the config', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: [],\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std'],\n        });\n`);\n\t});\n\n\tit('should modify the config when a function is passed', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig(() => ({\n            registries: [],\n        }));\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig(() => ({\n            registries: ['@ieedan/std'],\n        }));\n`);\n\t});\n\n\tit('should modify the config with other keys present', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            paths: {\n                ui: './src/ui',\n            },\n            registries: [],\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            paths: {\n                ui: './src/ui',\n            },\n            registries: ['@ieedan/std'],\n        });\n`);\n\t});\n\n\tit(\"should add the key if it doesn't exist\", async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n        \tregistries: ['@ieedan/std']\n        });\n`);\n\t});\n\n\tit('should modify existing registries without removing other registries', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std']\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/extras'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std', '@ieedan/extras']\n        });\n`);\n\t});\n\n\tit('should be able to add multiple registries at once', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: []\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std', '@ieedan/extras'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std', '@ieedan/extras']\n        });\n`);\n\t});\n\n\tit('should not re-add registries that are already added', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std']\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(configCode);\n\t});\n\n\tit('should handle a mixture of added and not added registries', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std']\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std', '@ieedan/extras'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std', '@ieedan/extras']\n        });\n`);\n\t});\n\n\tit('will add a comma if the previous key does not end with one', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {}\n\t\t});\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/std'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toContain('paths: {},');\n\t\texpect(result._unsafeUnwrap()).toContain('registries:');\n\t\texpect(result._unsafeUnwrap()).toContain(\"'@ieedan/std'\");\n\t});\n\n\tit('should handle registries with trailing commas', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std',],\n        });\n        `;\n\n\t\tconst result = await addRegistriesToConfig(['@ieedan/extras'], {\n\t\t\tconfig: {\n\t\t\t\tcode: configCode,\n\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t},\n\t\t});\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            registries: ['@ieedan/std', '@ieedan/extras'],\n        });\n`);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/add.test.ts",
    "content": "import { assert, describe, expect, it } from 'vitest';\nimport type { DistributedOutputManifest } from '@/outputs';\nimport { DEFAULT_PROVIDERS } from '@/providers';\nimport {\n\tgetTargetPath,\n\tnormalizeItemTypeForPath,\n\tparseWantedItems,\n\tresolveTree,\n\tresolveWantedItems,\n} from '@/utils/add';\nimport {\n\tMultipleRegistriesError,\n\tRegistryItemNotFoundError,\n\tRegistryNotProvidedError,\n} from '@/utils/errors';\nimport type { AbsolutePath, ItemRelativePath } from '@/utils/types';\n\ndescribe('parseWantedItems', () => {\n\tit('should parse fully qualified items', () => {\n\t\tconst result = parseWantedItems(['@ieedan/std/result'], {\n\t\t\tproviders: DEFAULT_PROVIDERS,\n\t\t\tregistries: [],\n\t\t});\n\n\t\tassert(result.isOk());\n\t\texpect(result.value).toEqual({\n\t\t\twantedItems: [\n\t\t\t\t{\n\t\t\t\t\tregistryUrl: '@ieedan/std',\n\t\t\t\t\titemName: 'result',\n\t\t\t\t},\n\t\t\t],\n\t\t\tneededRegistries: ['@ieedan/std'],\n\t\t});\n\t});\n\n\tit('should parse unqualified items', () => {\n\t\tconst result = parseWantedItems(['result'], {\n\t\t\tproviders: DEFAULT_PROVIDERS,\n\t\t\tregistries: ['@ieedan/std'],\n\t\t});\n\n\t\tassert(result.isOk());\n\t\texpect(result.value).toEqual({\n\t\t\twantedItems: [{ itemName: 'result' }],\n\t\t\tneededRegistries: ['@ieedan/std'],\n\t\t});\n\t});\n\n\tit('should error when it cannot determine the registry url', () => {\n\t\tconst result = parseWantedItems(['math'], {\n\t\t\tproviders: DEFAULT_PROVIDERS,\n\t\t\tregistries: [],\n\t\t});\n\n\t\tassert(result.isErr());\n\t\texpect(result.error).toBeInstanceOf(RegistryNotProvidedError);\n\t});\n});\n\ndescribe('resolveWantedItems', () => {\n\tit('should resolve wanted items', async () => {\n\t\tconst result = await resolveWantedItems(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tregistryUrl: '@ieedan/std@beta',\n\t\t\t\t\titemName: 'result',\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\tresolvedRegistries: new Map([['@ieedan/std@beta', RESOLVED_REGISTRY]]),\n\t\t\t\tnonInteractive: true,\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isOk());\n\t\tconst wantedItem = result.value[0]!;\n\t\texpect(wantedItem.registry.url).toBe('@ieedan/std@beta');\n\t\texpect(wantedItem.item.name).toBe('result');\n\t});\n\n\tit('should error when the item is not found', async () => {\n\t\tconst result = await resolveWantedItems(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\titemName: 'not-found',\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\tresolvedRegistries: new Map([['@ieedan/std@beta', RESOLVED_REGISTRY]]),\n\t\t\t\tnonInteractive: true,\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isErr());\n\t\texpect(result.error).toBeInstanceOf(RegistryItemNotFoundError);\n\t});\n\n\tit('should error when the item is not found in a specify registry', async () => {\n\t\tconst result = await resolveWantedItems(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tregistryUrl: '@ieedan/std@beta',\n\t\t\t\t\titemName: 'not-found',\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\tresolvedRegistries: new Map([['@ieedan/std@beta', RESOLVED_REGISTRY]]),\n\t\t\t\tnonInteractive: true,\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isErr());\n\t\texpect(result.error).toBeInstanceOf(RegistryItemNotFoundError);\n\t});\n\n\tit('should error when there are multiple matches in non interactive mode', async () => {\n\t\tconst result = await resolveWantedItems(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\titemName: 'result',\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\tresolvedRegistries: new Map([\n\t\t\t\t\t['@ieedan/std@beta', RESOLVED_REGISTRY],\n\t\t\t\t\t['@ieedan/std2@beta', RESOLVED_REGISTRY],\n\t\t\t\t]),\n\t\t\t\tnonInteractive: true,\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isErr());\n\t\texpect(result.error).toBeInstanceOf(MultipleRegistriesError);\n\t});\n});\n\nconst RESOLVED_REGISTRY = {\n\turl: '@ieedan/std@beta',\n\tprovider: {\n\t\tstate: {\n\t\t\turl: '@ieedan/std@beta',\n\t\t\tspecifier: undefined,\n\t\t\tscope: '@ieedan',\n\t\t\tregistryName: 'std',\n\t\t\tversion: 'beta',\n\t\t\tbaseUrl: 'https://www.jsrepo.com',\n\t\t},\n\t\tfetch: async () => {\n\t\t\treturn '';\n\t\t},\n\t\topts: {},\n\t},\n\tmanifest: {\n\t\tname: '@ieedan/std',\n\t\tdescription: 'Fully tested and documented TypeScript utilities brokered by jsrepo.',\n\t\tversion: '5.3.1-beta.0',\n\t\thomepage: 'https://ieedan.github.io/std/',\n\t\ttags: ['typescript', 'std', 'utilities'],\n\t\trepository: 'https://github.com/ieedan/std',\n\t\tbugs: 'https://github.com/ieedan/std/issues',\n\t\tauthors: ['Aidan Bleser'],\n\t\ttype: 'distributed',\n\t\tplugins: {\n\t\t\tlanguages: [],\n\t\t\tproviders: [],\n\t\t\ttransforms: [],\n\t\t},\n\t\tdefaultPaths: {},\n\t\titems: [\n\t\t\t{\n\t\t\t\tname: 'result',\n\t\t\t\ttype: 'util',\n\t\t\t\tregistryDependencies: ['types'],\n\t\t\t\tadd: 'when-added',\n\t\t\t\tdependencies: [],\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'result.ts' as ItemRelativePath,\n\t\t\t\t\t\trole: 'file',\n\t\t\t\t\t\ttype: 'util',\n\t\t\t\t\t\ttarget: undefined,\n\t\t\t\t\t\tregistryDependencies: undefined,\n\t\t\t\t\t\tdependencies: undefined,\n\t\t\t\t\t\tdevDependencies: undefined,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'result.test.ts' as ItemRelativePath,\n\t\t\t\t\t\trole: 'test',\n\t\t\t\t\t\ttype: 'util',\n\t\t\t\t\t\ttarget: undefined,\n\t\t\t\t\t\tregistryDependencies: ['add'],\n\t\t\t\t\t\tdependencies: undefined,\n\t\t\t\t\t\tdevDependencies: [{ ecosystem: 'js', name: 'vitest', version: undefined }],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdevDependencies: [],\n\t\t\t\tenvVars: {},\n\t\t\t\ttitle: undefined,\n\t\t\t\tdescription: undefined,\n\t\t\t\tcategories: undefined,\n\t\t\t\tmeta: undefined,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'types',\n\t\t\t\ttype: 'util',\n\t\t\t\tregistryDependencies: [],\n\t\t\t\tadd: 'when-added',\n\t\t\t\tdependencies: [],\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'types.ts' as ItemRelativePath,\n\t\t\t\t\t\trole: 'file',\n\t\t\t\t\t\ttype: 'util',\n\t\t\t\t\t\ttarget: undefined,\n\t\t\t\t\t\tregistryDependencies: undefined,\n\t\t\t\t\t\tdependencies: undefined,\n\t\t\t\t\t\tdevDependencies: undefined,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdevDependencies: [],\n\t\t\t\tenvVars: {},\n\t\t\t\ttitle: undefined,\n\t\t\t\tdescription: undefined,\n\t\t\t\tcategories: undefined,\n\t\t\t\tmeta: undefined,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'add',\n\t\t\t\ttype: 'util',\n\t\t\t\tregistryDependencies: [],\n\t\t\t\tadd: 'when-added',\n\t\t\t\tdependencies: [],\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'add.ts' as ItemRelativePath,\n\t\t\t\t\t\trole: 'file',\n\t\t\t\t\t\ttype: 'util',\n\t\t\t\t\t\ttarget: undefined,\n\t\t\t\t\t\tregistryDependencies: undefined,\n\t\t\t\t\t\tdependencies: undefined,\n\t\t\t\t\t\tdevDependencies: undefined,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdevDependencies: [],\n\t\t\t\tenvVars: {},\n\t\t\t\ttitle: undefined,\n\t\t\t\tdescription: undefined,\n\t\t\t\tcategories: undefined,\n\t\t\t\tmeta: undefined,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'circular-a',\n\t\t\t\ttype: 'util',\n\t\t\t\tregistryDependencies: ['circular-b'],\n\t\t\t\tadd: 'when-added',\n\t\t\t\tdependencies: [],\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'circular-a.ts' as ItemRelativePath,\n\t\t\t\t\t\trole: 'file',\n\t\t\t\t\t\ttype: 'util',\n\t\t\t\t\t\ttarget: undefined,\n\t\t\t\t\t\tregistryDependencies: undefined,\n\t\t\t\t\t\tdependencies: undefined,\n\t\t\t\t\t\tdevDependencies: undefined,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdevDependencies: [],\n\t\t\t\tenvVars: {},\n\t\t\t\ttitle: undefined,\n\t\t\t\tdescription: undefined,\n\t\t\t\tcategories: undefined,\n\t\t\t\tmeta: undefined,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'circular-b',\n\t\t\t\ttype: 'util',\n\t\t\t\tregistryDependencies: ['circular-a'],\n\t\t\t\tadd: 'when-added',\n\t\t\t\tdependencies: [],\n\t\t\t\tfiles: [\n\t\t\t\t\t{\n\t\t\t\t\t\tpath: 'circular-b.ts' as ItemRelativePath,\n\t\t\t\t\t\trole: 'file',\n\t\t\t\t\t\ttype: 'util',\n\t\t\t\t\t\ttarget: undefined,\n\t\t\t\t\t\tregistryDependencies: undefined,\n\t\t\t\t\t\tdependencies: undefined,\n\t\t\t\t\t\tdevDependencies: undefined,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdevDependencies: [],\n\t\t\t\tenvVars: {},\n\t\t\t\ttitle: undefined,\n\t\t\t\tdescription: undefined,\n\t\t\t\tcategories: undefined,\n\t\t\t\tmeta: undefined,\n\t\t\t},\n\t\t],\n\t} satisfies DistributedOutputManifest,\n};\n\ndescribe('resolveTree', () => {\n\tit(\"should resolve the item and it's dependencies\", () => {\n\t\tconst result = resolveTree(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tregistry: RESOLVED_REGISTRY,\n\t\t\t\t\titem: RESOLVED_REGISTRY.manifest.items[0]!,\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\toptions: {\n\t\t\t\t\twithRoles: new Set(),\n\t\t\t\t},\n\t\t\t\tresolvedItems: new Map(),\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isOk());\n\n\t\tconst itemNames = result.value.map((item) => item.name);\n\t\texpect(itemNames).toContain('result');\n\t\texpect(itemNames).toContain('types');\n\t});\n\n\tit('should resolve the items dependencies and the optional file dependencies', () => {\n\t\tconst result = resolveTree(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tregistry: RESOLVED_REGISTRY,\n\t\t\t\t\titem: RESOLVED_REGISTRY.manifest.items[0]!,\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\toptions: {\n\t\t\t\t\twithRoles: new Set(['test']),\n\t\t\t\t},\n\t\t\t\tresolvedItems: new Map(),\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isOk());\n\n\t\tconst itemNames = result.value.map((item) => item.name);\n\t\texpect(itemNames).toContain('result');\n\t\texpect(itemNames).toContain('types');\n\t\texpect(itemNames).toContain('add');\n\t});\n\n\tit('should successfully resolve an item with a circular dependency', () => {\n\t\tconst circularA = RESOLVED_REGISTRY.manifest.items.find((i) => i.name === 'circular-a')!;\n\t\tconst result = resolveTree(\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tregistry: RESOLVED_REGISTRY,\n\t\t\t\t\titem: circularA,\n\t\t\t\t},\n\t\t\t],\n\t\t\t{\n\t\t\t\toptions: {\n\t\t\t\t\twithRoles: new Set(),\n\t\t\t\t},\n\t\t\t\tresolvedItems: new Map(),\n\t\t\t}\n\t\t);\n\n\t\tassert(result.isOk());\n\n\t\t// Should resolve both items in the circular dependency\n\t\tconst itemNames = result.value.map((item) => item.name);\n\t\texpect(itemNames).toContain('circular-a');\n\t\texpect(itemNames).toContain('circular-b');\n\t\t// Should not have duplicates\n\t\texpect(itemNames.filter((name) => name === 'circular-a')).toHaveLength(1);\n\t\texpect(itemNames.filter((name) => name === 'circular-b')).toHaveLength(1);\n\t});\n});\n\ndescribe('getTargetPath', () => {\n\tit('should get the target path for a distributed file', () => {\n\t\tconst result = getTargetPath(\n\t\t\t{\n\t\t\t\tpath: 'result.ts' as ItemRelativePath,\n\t\t\t},\n\t\t\t{\n\t\t\t\titemPath: { path: 'src' },\n\t\t\t\toptions: { cwd: '' as AbsolutePath },\n\t\t\t}\n\t\t);\n\n\t\texpect(result).toBe('src/result.ts');\n\t});\n\n\tit('should get the target path for a distributed file with a target path', () => {\n\t\tconst result = getTargetPath(\n\t\t\t{\n\t\t\t\tpath: 'result.ts' as ItemRelativePath,\n\t\t\t\ttarget: 'src/result/result.ts',\n\t\t\t},\n\t\t\t{\n\t\t\t\titemPath: { path: 'src' },\n\t\t\t\toptions: { cwd: '' as AbsolutePath },\n\t\t\t}\n\t\t);\n\n\t\texpect(result).toBe('src/result/result.ts');\n\t});\n\n\tit('should get the target path for a distributed file with a relative path', () => {\n\t\tconst result = getTargetPath(\n\t\t\t{\n\t\t\t\tpath: 'result.ts' as ItemRelativePath,\n\t\t\t},\n\t\t\t{\n\t\t\t\titemPath: { path: 'src/result' },\n\t\t\t\toptions: { cwd: '' as AbsolutePath },\n\t\t\t}\n\t\t);\n\n\t\texpect(result).toBe('src/result/result.ts');\n\t});\n});\n\ndescribe('normalizeItemTypeForPath', () => {\n\tit('should normalize prefixed item types', () => {\n\t\texpect(normalizeItemTypeForPath('registry:util')).toBe('util');\n\t\texpect(normalizeItemTypeForPath('registry:registry:util')).toBe('registry:util');\n\t});\n\n\tit('should not normalize unprefixed item types', () => {\n\t\texpect(normalizeItemTypeForPath('util')).toBe('util');\n\t\texpect(normalizeItemTypeForPath('blocks:registry:component')).toBe(\n\t\t\t'blocks:registry:component'\n\t\t);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/casing.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { kebabToCamel } from '@/utils/casing';\n\ndescribe('kebabToCamel', () => {\n\tit('correctly converts to camelCase', () => {\n\t\texpect(kebabToCamel('hello-world')).toBe('helloWorld');\n\t\texpect(kebabToCamel('hello')).toBe('hello');\n\t});\n\n\tit('Removes trailing dash', () => {\n\t\texpect(kebabToCamel('hello-world-')).toBe('helloWorld');\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/config-mcp.test.ts",
    "content": "import { assert, describe, expect, it } from 'vitest';\nimport { updateJsonConfig } from '@/commands/config/mcp';\nimport { stringify } from '@/utils/json';\n\ndescribe('updateJsonConfig', () => {\n\tit('should overwrite existing keys', () => {\n\t\tconst config = {\n\t\t\tmcpServers: {\n\t\t\t\tjsrepo: {\n\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t\t\tenv: {\n\t\t\t\t\t\tFOO: 'bar',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst result = updateJsonConfig({\n\t\t\texistingContent: JSON.stringify(config),\n\t\t\tserverName: 'jsrepo',\n\t\t\tserverConfig: {\n\t\t\t\tcommand: 'npx',\n\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t},\n\t\t});\n\t\tassert(result.isOk());\n\t\texpect(result.value).toBe(\n\t\t\tstringify(\n\t\t\t\t{\n\t\t\t\t\tmcpServers: {\n\t\t\t\t\t\tjsrepo: {\n\t\t\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ format: true }\n\t\t\t)\n\t\t);\n\t});\n\n\tit('should create a new file when empty', () => {\n\t\tconst result = updateJsonConfig({\n\t\t\texistingContent: '',\n\t\t\tserverName: 'jsrepo',\n\t\t\tserverConfig: { command: 'npx', args: ['@jsrepo/mcp'] },\n\t\t});\n\t\tassert(result.isOk());\n\t\texpect(result.value).toBe(\n\t\t\tstringify(\n\t\t\t\t{\n\t\t\t\t\tmcpServers: {\n\t\t\t\t\t\tjsrepo: {\n\t\t\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ format: true }\n\t\t\t)\n\t\t);\n\t});\n\n\tit('should maintain existing keys', () => {\n\t\tconst config = {\n\t\t\tmcpServers: {\n\t\t\t\tgithub: {\n\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\targs: ['@github/mcp'],\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst result = updateJsonConfig({\n\t\t\texistingContent: JSON.stringify(config),\n\t\t\tserverName: 'jsrepo',\n\t\t\tserverConfig: {\n\t\t\t\tcommand: 'npx',\n\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t},\n\t\t});\n\t\tassert(result.isOk());\n\t\texpect(result.value).toBe(\n\t\t\tstringify(\n\t\t\t\t{\n\t\t\t\t\tmcpServers: {\n\t\t\t\t\t\tgithub: {\n\t\t\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\t\t\targs: ['@github/mcp'],\n\t\t\t\t\t\t},\n\t\t\t\t\t\tjsrepo: {\n\t\t\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ format: true }\n\t\t\t)\n\t\t);\n\t});\n\n\tit('should preserve non-stdio server configs', () => {\n\t\tconst config = {\n\t\t\tmcpServers: {\n\t\t\t\tstitch: {\n\t\t\t\t\ttype: 'http',\n\t\t\t\t\turl: 'https://stitch.googleapis.com/mcp',\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t'X-Goog-Api-Key': 'test-key',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst result = updateJsonConfig({\n\t\t\texistingContent: JSON.stringify(config),\n\t\t\tserverName: 'jsrepo',\n\t\t\tserverConfig: {\n\t\t\t\tcommand: 'npx',\n\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t},\n\t\t});\n\t\tassert(result.isOk());\n\t\texpect(result.value).toBe(\n\t\t\tstringify(\n\t\t\t\t{\n\t\t\t\t\tmcpServers: {\n\t\t\t\t\t\tstitch: {\n\t\t\t\t\t\t\ttype: 'http',\n\t\t\t\t\t\t\turl: 'https://stitch.googleapis.com/mcp',\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t'X-Goog-Api-Key': 'test-key',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tjsrepo: {\n\t\t\t\t\t\t\tcommand: 'npx',\n\t\t\t\t\t\t\targs: ['@jsrepo/mcp'],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ format: true }\n\t\t\t)\n\t\t);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/config-utils.test.ts",
    "content": "import path from 'pathe';\nimport { describe, expect, it } from 'vitest';\nimport { loadConfigSearch } from '@/utils/config/utils';\nimport type { AbsolutePath } from '@/utils/types';\n\ndescribe('loadConfigSearch', () => {\n\tit('should load the config', async () => {\n\t\tconst config = await loadConfigSearch({\n\t\t\tcwd: path.join(__dirname, '../fixtures/config/basic') as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\texpect(config).not.toBeNull();\n\t});\n\n\tit('should load a mts config', async () => {\n\t\tconst config = await loadConfigSearch({\n\t\t\tcwd: path.join(__dirname, '../fixtures/config/mts') as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\texpect(config).not.toBeNull();\n\t});\n\n\tit('should find the config in a higher directory', async () => {\n\t\tconst config = await loadConfigSearch({\n\t\t\tcwd: path.join(__dirname, '../fixtures/config/nested/lower') as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\texpect(config).not.toBeNull();\n\t});\n\n\tit('should not find the config when it does not exist', async () => {\n\t\tconst config = await loadConfigSearch({\n\t\t\tcwd: path.join(__dirname, '../fixtures/config/none') as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\texpect(config).toBeNull();\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/env.test.ts",
    "content": "import dedent from 'dedent';\nimport { describe, expect, it } from 'vitest';\nimport { parseEnvVariables, updateEnvFile } from '@/utils/env';\n\ndescribe('parseEnvVariables', () => {\n\tit('should parse all the environment variables', () => {\n\t\tconst contents = dedent`\n\t\tTEST=test\n\t\tTEST2=test2\n\t\tTEST3=test3\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({ TEST: 'test', TEST2: 'test2', TEST3: 'test3' });\n\t});\n\n\tit('should parse all the environment variables', () => {\n\t\tconst contents = dedent`\n\t\tTEST=test\n\t\tTEST2=test2\n\n\t\tTEST3=test3\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({ TEST: 'test', TEST2: 'test2', TEST3: 'test3' });\n\t});\n\n\tit('should parse all the environment variables with quotes', () => {\n\t\tconst contents = dedent`\n\t\tTEST=\"test\"\n\t\tTEST2=\"test2\"\n        \n\t\tTEST3=\"test3\"\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({ TEST: '\"test\"', TEST2: '\"test2\"', TEST3: '\"test3\"' });\n\t});\n\n\tit('should parse environment variables with equals signs in values', () => {\n\t\tconst contents = dedent`\n\t\tKEY=value=with=equals\n\t\tCONNECTION_STRING=postgresql://user:pass@host:5432/db?param=value&other=test\n\t\tSIMPLE=normal_value\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tKEY: 'value=with=equals',\n\t\t\tCONNECTION_STRING: 'postgresql://user:pass@host:5432/db?param=value&other=test',\n\t\t\tSIMPLE: 'normal_value',\n\t\t});\n\t});\n\n\tit('should parse multi-line environment variables', () => {\n\t\tconst contents = dedent`\n\t\tMULTI_LINE=\"some\nmulti-line\nvariable\"\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tMULTI_LINE: 'some\\nmulti-line\\nvariable\\n',\n\t\t});\n\t});\n\n\tit('should parse variables with empty values', () => {\n\t\tconst contents = dedent`\n\t\tEMPTY=\n\t\tKEY=value\n\t\tANOTHER_EMPTY=\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tEMPTY: '',\n\t\t\tKEY: 'value',\n\t\t\tANOTHER_EMPTY: '',\n\t\t});\n\t});\n\n\tit('should skip lines without equals signs', () => {\n\t\tconst contents = dedent`\n\t\tVALID_KEY=value\n\t\tnot a valid line\n\t\tANOTHER_VALID=another_value\n\t\talso not valid\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tVALID_KEY: 'value',\n\t\t\tANOTHER_VALID: 'another_value',\n\t\t});\n\t});\n\n\tit('should handle variables with whitespace in names and values', () => {\n\t\tconst contents = dedent`\n\t\tKEY_WITH_SPACES = value with spaces\n\t\tANOTHER_KEY=value with\ttab\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tKEY_WITH_SPACES: ' value with spaces',\n\t\t\tANOTHER_KEY: 'value with\\ttab',\n\t\t});\n\t});\n\n\tit('should handle variables with special characters', () => {\n\t\tconst contents = dedent`\n\t\tSPECIAL_CHARS=\"!@#$%^&*()_+-=[]{}|;:'\",.<>?/\"\n\t\tURL=https://example.com/path?query=value&other=test\n\t\tJSON={\"key\":\"value\",\"number\":123}\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tSPECIAL_CHARS: '\"!@#$%^&*()_+-=[]{}|;:\\'\",.<>?/\"',\n\t\t\tURL: 'https://example.com/path?query=value&other=test',\n\t\t\tJSON: '{\"key\":\"value\",\"number\":123}',\n\t\t});\n\t});\n\n\tit('should handle multi-line variables with only one line after opening quote', () => {\n\t\tconst contents = dedent`\n\t\tSINGLE_LINE_MULTI=\"value\"\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tSINGLE_LINE_MULTI: '\"value\"',\n\t\t});\n\t});\n\n\tit('should not parse comments', () => {\n\t\tconst contents = dedent`\n\t\t# This is a comment\n\t\tSECRET_KEY=YOURSECRETKEYGOESHERE # also a comment\n\t\tSECRET_HASH=\"something-with-a-hash-#-this-is-not-a-comment\"\n\t\t`;\n\t\tconst envVars = parseEnvVariables(contents);\n\t\texpect(envVars).toEqual({\n\t\t\tSECRET_KEY: 'YOURSECRETKEYGOESHERE',\n\t\t\tSECRET_HASH: '\"something-with-a-hash-#-this-is-not-a-comment\"',\n\t\t});\n\t});\n});\n\ndescribe('updateEnvFile', () => {\n\tit('should update the env file', () => {\n\t\tconst contents = dedent`\n\t\tTEST=test\n\t\tTEST2=test2\n\t\tTEST3=test3\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTEST: 'test4',\n\t\t\tTEST2: 'test5',\n\t\t\tTEST3: 'test6',\n\t\t\tTEST4: 'test7',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST=test4\n\t\tTEST2=test5\n\t\tTEST3=test6\n\t\tTEST4=test7\n\t\t`);\n\t});\n\n\tit('should add variables to a blank env file', () => {\n\t\tconst newContents = updateEnvFile('', {\n\t\t\tTEST4: 'test7',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST4=test7\n\t\t`);\n\t});\n\n\tit('should not overwrite already present variables', () => {\n\t\tconst contents = dedent`\n\t\tTEST=test\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTEST: '',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST=test\n\t\t`);\n\t});\n\n\tit('should handle multi-line variables', () => {\n\t\tconst contents = dedent`\n\t\tTEST=\"some\nmulti-line\nvariable\n\"\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTEST: 'test4',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST=\"test4\"\n\t\t`);\n\t});\n\n\tit('should update non-quoted variable and preserve format', () => {\n\t\tconst contents = dedent`\n\t\tTEST=original_value\n\t\tOTHER=other_value\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTEST: 'new_value',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST=new_value\n\t\tOTHER=other_value\n\t\t`);\n\t});\n\n\tit('should update quoted variable to multi-line value', () => {\n\t\tconst contents = dedent`\n\t\tTEST=\"original\"\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTEST: 'new\\nmulti\\nline',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST=\"new\nmulti\nline\"\n\t\t`);\n\t});\n\n\tit('should update multi-line variable to another multi-line value', () => {\n\t\tconst contents = dedent`\n\t\tTEST=\"old\nvalue\"\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTEST: 'new\\nvalue\\nhere',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tTEST=\"new\nvalue\nhere\"\n\t\t`);\n\t});\n\n\tit('should handle variables with special characters in values', () => {\n\t\tconst contents = dedent`\n\t\tSPECIAL=original\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tSPECIAL: 'value=with=equals&special!chars',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tSPECIAL=value=with=equals&special!chars\n\t\t`);\n\t});\n\n\tit('should add multiple new variables at once', () => {\n\t\tconst contents = dedent`\n\t\tEXISTING=value\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tNEW1: 'value1',\n\t\t\tNEW2: 'value2',\n\t\t\tNEW3: 'value3',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tEXISTING=value\n\t\tNEW1=value1\n\t\tNEW2=value2\n\t\tNEW3=value3\n\t\t`);\n\t});\n\n\tit('should handle updating variable at the beginning of file', () => {\n\t\tconst contents = dedent`\n\t\tFIRST=first_value\n\t\tSECOND=second_value\n\t\tTHIRD=third_value\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tFIRST: 'updated_first',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tFIRST=updated_first\n\t\tSECOND=second_value\n\t\tTHIRD=third_value\n\t\t`);\n\t});\n\n\tit('should handle updating variable at the end of file', () => {\n\t\tconst contents = dedent`\n\t\tFIRST=first_value\n\t\tSECOND=second_value\n\t\tTHIRD=third_value\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tTHIRD: 'updated_third',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tFIRST=first_value\n\t\tSECOND=second_value\n\t\tTHIRD=updated_third\n\t\t`);\n\t});\n\n\tit('should handle updating multiple existing variables', () => {\n\t\tconst contents = dedent`\n\t\tVAR1=value1\n\t\tVAR2=value2\n\t\tVAR3=value3\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tVAR1: 'new1',\n\t\t\tVAR2: 'new2',\n\t\t\tVAR3: 'new3',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tVAR1=new1\n\t\tVAR2=new2\n\t\tVAR3=new3\n\t\t`);\n\t});\n\n\tit('should preserve original quote format when updating', () => {\n\t\tconst contents = dedent`\n\t\tQUOTED=\"quoted_value\"\n\t\tUNQUOTED=unquoted_value\n\t\t`;\n\t\tconst newContents = updateEnvFile(contents, {\n\t\t\tQUOTED: 'new_quoted',\n\t\t\tUNQUOTED: 'new_unquoted',\n\t\t});\n\t\texpect(newContents).toEqual(dedent`\n\t\tQUOTED=\"new_quoted\"\n\t\tUNQUOTED=new_unquoted\n\t\t`);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/package.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { type PackageJson, shouldInstall } from '@/utils/package';\n\ndescribe('shouldInstall', () => {\n\tit('should install dependencies not present in package.json', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: '4.17.21' }],\n\t\t\tdevDependencies: [{ ecosystem: 'js', name: 'react', version: '18.0.0' }],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdependencies: {},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual(dependencies);\n\t});\n\n\tit('should not install dependencies already present with satisfying versions', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: '4.17.21' }],\n\t\t\tdevDependencies: [],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdependencies: {\n\t\t\t\tlodash: '^4.17.21',\n\t\t\t},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual({ dependencies: [], devDependencies: [] });\n\t});\n\n\tit('should install dependencies with newer versions', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: '5.0.0' }],\n\t\t\tdevDependencies: [],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdependencies: {\n\t\t\t\tlodash: '^4.17.20',\n\t\t\t},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual({\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: '5.0.0' }],\n\t\t\tdevDependencies: [],\n\t\t});\n\t});\n\n\tit('should handle dev dependencies correctly', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [],\n\t\t\tdevDependencies: [{ ecosystem: 'js', name: 'jest', version: '29.0.0' }],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdevDependencies: {\n\t\t\t\tjest: '^28.0.0',\n\t\t\t},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual({\n\t\t\tdependencies: [],\n\t\t\tdevDependencies: [{ ecosystem: 'js', name: 'jest', version: '29.0.0' }],\n\t\t});\n\t});\n\n\tit('should not install dev dependencies already present with satisfying versions', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [],\n\t\t\tdevDependencies: [{ ecosystem: 'js', name: 'jest', version: '28.5.0' }],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdevDependencies: {\n\t\t\t\tjest: '^28.5.0',\n\t\t\t},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual({\n\t\t\tdependencies: [],\n\t\t\tdevDependencies: [],\n\t\t});\n\t});\n\n\tit('should skip dependencies with undefined version if they exist', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: undefined }],\n\t\t\tdevDependencies: [],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdependencies: {\n\t\t\t\tlodash: '^4.17.20',\n\t\t\t},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual({\n\t\t\tdependencies: [],\n\t\t\tdevDependencies: [],\n\t\t});\n\t});\n\n\tit(\"should install dependencies with undefined version if they don't exist\", () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: undefined }],\n\t\t\tdevDependencies: [],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdependencies: {},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual(dependencies);\n\t});\n\n\tit('should handle duplicate dependencies by keeping the last one', () => {\n\t\tconst dependencies = {\n\t\t\tdependencies: [\n\t\t\t\t{ ecosystem: 'js', name: 'lodash', version: '4.17.20' },\n\t\t\t\t{ ecosystem: 'js', name: 'lodash', version: '4.17.21' },\n\t\t\t],\n\t\t\tdevDependencies: [],\n\t\t};\n\n\t\tconst pkg: Partial<PackageJson> = {\n\t\t\tdependencies: {},\n\t\t};\n\n\t\tconst result = shouldInstall(dependencies, { pkg });\n\n\t\texpect(result).toEqual({\n\t\t\tdependencies: [{ ecosystem: 'js', name: 'lodash', version: '4.17.21' }],\n\t\t\tdevDependencies: [],\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/parse-package-name.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { parsePackageName } from '@/utils/parse-package-name';\n\ndescribe('parsePackageName', () => {\n\tit('should parse a simple package name', () => {\n\t\tconst result = parsePackageName('lodash');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: 'lodash',\n\t\t\t\tversion: undefined,\n\t\t\t\tpath: '',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a package name with version', () => {\n\t\tconst result = parsePackageName('lodash@4.17.21');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: 'lodash',\n\t\t\t\tversion: '4.17.21',\n\t\t\t\tpath: '',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a package name with path', () => {\n\t\tconst result = parsePackageName('lodash/debounce');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: 'lodash',\n\t\t\t\tversion: undefined,\n\t\t\t\tpath: '/debounce',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a package name with version and path', () => {\n\t\tconst result = parsePackageName('lodash@4.17.21/debounce');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: 'lodash',\n\t\t\t\tversion: '4.17.21',\n\t\t\t\tpath: '/debounce',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a scoped package name', () => {\n\t\tconst result = parsePackageName('@jsrepo/transform-prettier');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: '@jsrepo/transform-prettier',\n\t\t\t\tversion: undefined,\n\t\t\t\tpath: '',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a scoped package name with version', () => {\n\t\tconst result = parsePackageName('@jsrepo/transform-prettier@2.1.0');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: '@jsrepo/transform-prettier',\n\t\t\t\tversion: '2.1.0',\n\t\t\t\tpath: '',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a scoped package name with path', () => {\n\t\tconst result = parsePackageName('@jsrepo/transform-prettier/lib');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: '@jsrepo/transform-prettier',\n\t\t\t\tversion: undefined,\n\t\t\t\tpath: '/lib',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should parse a scoped package name with version and path', () => {\n\t\tconst result = parsePackageName('@jsrepo/transform-prettier@2.1.0/lib/utils');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: '@jsrepo/transform-prettier',\n\t\t\t\tversion: '2.1.0',\n\t\t\t\tpath: '/lib/utils',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should handle complex version specifiers', () => {\n\t\tconst result = parsePackageName('lodash@^4.17.0');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: 'lodash',\n\t\t\t\tversion: '^4.17.0',\n\t\t\t\tpath: '',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should handle beta versions', () => {\n\t\tconst result = parsePackageName('react@18.0.0-beta.1');\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value).toEqual({\n\t\t\t\tname: 'react',\n\t\t\t\tversion: '18.0.0-beta.1',\n\t\t\t\tpath: '',\n\t\t\t});\n\t\t}\n\t});\n\n\tit('should return error for invalid package names', () => {\n\t\tconst result = parsePackageName('');\n\t\texpect(result.isErr()).toBe(true);\n\t\tif (result.isErr()) {\n\t\t\texpect(result.error).toBe('invalid package name: ');\n\t\t}\n\t});\n\n\tit('should return error for malformed package names', () => {\n\t\tconst result = parsePackageName('@');\n\t\texpect(result.isErr()).toBe(true);\n\t\tif (result.isErr()) {\n\t\t\texpect(result.error).toBe('invalid package name: @');\n\t\t}\n\t});\n\n\tit('should return error for invalid scoped package names', () => {\n\t\tconst result = parsePackageName('@scope/');\n\t\texpect(result.isErr()).toBe(true);\n\t\tif (result.isErr()) {\n\t\t\texpect(result.error).toBe('invalid package name: @scope/');\n\t\t}\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/roles.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { isOptionalRole, resolveWithRoles, shouldIncludeRole } from '@/utils/roles';\n\ndescribe('resolveWithRoles', () => {\n\tit('combines explicit roles with legacy flags', () => {\n\t\tconst result = resolveWithRoles({\n\t\t\twith: ['storybook', 'example'],\n\t\t\twithExamples: true,\n\t\t\twithDocs: false,\n\t\t\twithTests: true,\n\t\t});\n\n\t\texpect(result.has('storybook')).toBe(true);\n\t\texpect(result.has('example')).toBe(true);\n\t\texpect(result.has('test')).toBe(true);\n\t\texpect(result.has('doc')).toBe(false);\n\t});\n});\n\ndescribe('shouldIncludeRole', () => {\n\tit('includes file roles by default and respects exact role matching', () => {\n\t\tconst withRoles = new Set(['example', 'storybook']);\n\n\t\texpect(shouldIncludeRole(undefined, withRoles)).toBe(true);\n\t\texpect(shouldIncludeRole('file', withRoles)).toBe(true);\n\t\texpect(shouldIncludeRole('example', withRoles)).toBe(true);\n\t\texpect(shouldIncludeRole('storybook', withRoles)).toBe(true);\n\t\texpect(shouldIncludeRole('doc', withRoles)).toBe(false);\n\t\texpect(shouldIncludeRole('EXAMPLE', withRoles)).toBe(false);\n\t\texpect(shouldIncludeRole(' example ', withRoles)).toBe(false);\n\t});\n});\n\ndescribe('isOptionalRole', () => {\n\tit('distinguishes optional roles from file', () => {\n\t\texpect(isOptionalRole(undefined)).toBe(false);\n\t\texpect(isOptionalRole('file')).toBe(false);\n\t\texpect(isOptionalRole(' example ')).toBe(true);\n\t\texpect(isOptionalRole('storybook')).toBe(true);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/update-paths.test.ts",
    "content": "import dedent from 'dedent';\nimport { describe, expect, it } from 'vitest';\nimport { updateConfigPaths } from '@/utils/config/mods/update-paths';\n\ndescribe('updateConfigPaths', () => {\n\tit('should return immediately if no paths are provided', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            paths: {\n                ui: './src/ui',\n            },\n        });\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(configCode);\n\t});\n\n\tit('should modify the config', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {},\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/ui',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui'\n\t\t\t},\n\t\t});\n`);\n\t});\n\n\tit('should modify the config when a function is passed', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig(() => ({\n\t\t\tpaths: {},\n\t\t}));\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/ui',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig(() => ({\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui'\n\t\t\t},\n\t\t}));\n`);\n\t});\n\n\tit('should modify the config with other keys present', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tregistries: ['@ieedan/std'],\n\t\t\tpaths: {},\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/ui',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tregistries: ['@ieedan/std'],\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui'\n\t\t\t},\n\t\t});\n`);\n\t});\n\n\tit(\"should add the key if it doesn't exist\", async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n        });\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/ui',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n        \tpaths: {\n        \t\tui: './src/ui'\n        \t}\n        });\n`);\n\t});\n\n\tit('should modify existing paths without removing other paths', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui'\n\t\t\t}\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tlib: './src/lib',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui',\n\t\t\t\tlib: './src/lib'\n\t\t\t}\n\t\t});\n`);\n\t});\n\n\tit('should be able to add multiple paths at once', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {}\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/ui',\n\t\t\t\tlib: './src/lib',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui',\n\t\t\t\tlib: './src/lib'\n\t\t\t}\n\t\t});\n`);\n\t});\n\n\tit('should update existing path values', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            paths: {\n                ui: './src/old-ui'\n            }\n        });\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/new-ui',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n        export default defineConfig({\n            paths: {\n                ui: './src/new-ui'\n            }\n        });\n        `);\n\t});\n\n\tit('should handle a mixture of new and existing paths', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui'\n\t\t\t}\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/new-ui',\n\t\t\t\tlib: './src/lib',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\tui: './src/new-ui',\n\t\t\t\tlib: './src/lib'\n\t\t\t}\n\t\t});\n`);\n\t});\n\n\tit('will add a comma if the previous key does not end with one', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tregistries: []\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\tui: './src/ui',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tregistries: [],\n\t\t\tpaths: {\n\t\t\t\tui: './src/ui'\n\t\t\t}\n\t\t});\n        `);\n\t});\n\n\tit('should handle paths with special characters in keys', async () => {\n\t\tconst configCode = dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {}\n\t\t});\n        `;\n\n\t\tconst result = await updateConfigPaths(\n\t\t\t{\n\t\t\t\t'ui/button': './src/components/ui',\n\t\t\t\t'*': './src/items',\n\t\t\t},\n\t\t\t{\n\t\t\t\tconfig: {\n\t\t\t\t\tcode: configCode,\n\t\t\t\t\tpath: 'jsrepo.config.ts',\n\t\t\t\t},\n\t\t\t}\n\t\t);\n\n\t\texpect(result._unsafeUnwrap()).toEqual(dedent`import { defineConfig } from 'jsrepo';\n\n\t\texport default defineConfig({\n\t\t\tpaths: {\n\t\t\t\t'ui/button': './src/components/ui',\n\t\t\t\t'*': './src/items'\n\t\t\t}\n\t\t});\n        `);\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tests/utils/zod.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { z } from 'zod';\nimport { safeParseFromJSON, safeValidate } from '@/utils/zod';\n\ndescribe('safeParse', () => {\n\tit('returns error when schema invalid', () => {\n\t\tconst schema = z.object({\n\t\t\ttitle: z.string(),\n\t\t});\n\n\t\tconst object = {};\n\n\t\tconst result = safeValidate(schema, object);\n\n\t\texpect(result.isErr()).toBe(true);\n\t});\n\n\tit('returns ok when schema valid', () => {\n\t\tconst schema = z.object({\n\t\t\ttitle: z.string(),\n\t\t});\n\n\t\tconst object = { title: 'Test' };\n\n\t\tconst result = safeValidate(schema, object);\n\n\t\texpect(result.isOk()).toBe(true);\n\t});\n});\n\ndescribe('safeParseFromJSON', () => {\n\tit('returns error when JSON is invalid', () => {\n\t\tconst schema = z.object({\n\t\t\ttitle: z.string(),\n\t\t});\n\n\t\tconst invalidJSON = '{ title: \"Test\" '; // Missing closing brace\n\n\t\tconst result = safeParseFromJSON(schema, invalidJSON);\n\n\t\texpect(result.isErr()).toBe(true);\n\t});\n\n\tit('returns error when JSON is valid but schema validation fails', () => {\n\t\tconst schema = z.object({\n\t\t\ttitle: z.string(),\n\t\t});\n\n\t\tconst validJSON = '{}'; // Valid JSON but missing required title\n\n\t\tconst result = safeParseFromJSON(schema, validJSON);\n\n\t\texpect(result.isErr()).toBe(true);\n\t});\n\n\tit('returns ok when JSON is valid and schema validation passes', () => {\n\t\tconst schema = z.object({\n\t\t\ttitle: z.string(),\n\t\t});\n\n\t\tconst validJSON = '{ \"title\": \"Test\" }';\n\n\t\tconst result = safeParseFromJSON(schema, validJSON);\n\n\t\texpect(result.isOk()).toBe(true);\n\t\tif (result.isOk()) {\n\t\t\texpect(result.value.title).toBe('Test');\n\t\t}\n\t});\n});\n"
  },
  {
    "path": "packages/jsrepo/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true,\n\t\t\"paths\": {\n\t\t\t\"@/*\": [\"./src/*\"]\n\t\t}\n\t},\n\t\"include\": [\"src/**/*.ts\", \"tests/**/*.ts\"],\n\t\"exclude\": [\"tests/fixtures/**/*\"]\n}\n"
  },
  {
    "path": "packages/jsrepo/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: [\n\t\t'src/bin.ts',\n\t\t'src/api/index.ts',\n\t\t'src/api/providers.ts',\n\t\t'src/api/langs/index.ts',\n\t\t'src/api/langs/js.ts',\n\t\t'src/api/outputs.ts',\n\t\t'src/api/config.ts',\n\t\t'src/api/errors.ts',\n\t\t'src/api/utils.ts',\n\t\t'src/api/warnings.ts',\n\t],\n\tformat: ['esm'],\n\talias: {\n\t\t'@/': './src/',\n\t},\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/jsrepo/vitest.config.ts",
    "content": "/// <reference types=\"vitest/config\" />\nimport path from 'pathe';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n\tresolve: {\n\t\talias: {\n\t\t\t'@': path.resolve(__dirname, 'src'),\n\t\t},\n\t},\n\ttest: {\n\t\tinclude: ['tests/**/*.test.ts', '!tests/fixtures/**/*.test.ts'],\n\t},\n});\n"
  },
  {
    "path": "packages/mcp/CHANGELOG.md",
    "content": "# @jsrepo/mcp\n\n## 0.0.26\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 0.0.25\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 0.0.24\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 0.0.23\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 0.0.22\n### Patch Changes\n\n\n- chore: bump deps ([#776](https://github.com/jsrepojs/jsrepo/pull/776))\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 0.0.21\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 0.0.20\n### Patch Changes\n\n\n- fix(mcp): Log to stdout instead of stderr so that clients like antigravity work properly ([#768](https://github.com/jsrepojs/jsrepo/pull/768))\n\n## 0.0.19\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 0.0.18\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 0.0.17\n### Patch Changes\n\n\n- feat: Add --with <role> support for optional file roles and allow arbitrary role strings; deprecate legacy --with-* flags. MCP now accepts role filters too. ([#759](https://github.com/jsrepojs/jsrepo/pull/759))\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 0.0.16\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 0.0.15\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 0.0.14\n### Patch Changes\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n\n## 0.0.13\n### Patch Changes\n\n- Updated dependencies [[`fcbb4b6`](https://github.com/jsrepojs/jsrepo/commit/fcbb4b69d684844a99f403e6880071302a176a27), [`43584f1`](https://github.com/jsrepojs/jsrepo/commit/43584f102a7e085195a4b738a56cb9a8aac65383)]:\n  - jsrepo@3.1.0\n\n## 0.0.12\n### Patch Changes\n\n- Updated dependencies [[`0bf74a4`](https://github.com/jsrepojs/jsrepo/commit/0bf74a448c0ff526aa353a0924646b3d647fd1bd), [`069cc97`](https://github.com/jsrepojs/jsrepo/commit/069cc979f6a4ecaac71cb5beece5c83860fbb915)]:\n  - jsrepo@3.0.11\n\n## 0.0.11\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n- Updated dependencies [[`07d0399`](https://github.com/jsrepojs/jsrepo/commit/07d039908e241f7bdcc084c5697a9534fd2e4788), [`2362f8e`](https://github.com/jsrepojs/jsrepo/commit/2362f8e90ad79ba6df0fe6cfa4ceb82362ee5b62)]:\n  - jsrepo@3.0.10\n\n## 0.0.10\n### Patch Changes\n\n- Updated dependencies [[`7d4e98a`](https://github.com/jsrepojs/jsrepo/commit/7d4e98a5e1588d430f5195181b30e59303e25efb)]:\n  - jsrepo@3.0.9\n\n## 0.0.9\n### Patch Changes\n\n- Updated dependencies [[`a704bd0`](https://github.com/jsrepojs/jsrepo/commit/a704bd09d678ff608c5c2e61c8b91f8c09717012)]:\n  - jsrepo@3.0.8\n\n## 0.0.8\n### Patch Changes\n\n- Updated dependencies [[`da29502`](https://github.com/jsrepojs/jsrepo/commit/da29502db16dccd2761b88f33e3a921ff7fd21ec)]:\n  - jsrepo@3.0.7\n\n## 0.0.7\n### Patch Changes\n\n- Updated dependencies [[`129abaf`](https://github.com/jsrepojs/jsrepo/commit/129abafd8b560e8240c6edb414ce3b74d4c7b3a5)]:\n  - jsrepo@3.0.6\n\n## 0.0.6\n### Patch Changes\n\n- Updated dependencies [[`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5)]:\n  - jsrepo@3.0.5\n\n## 0.0.5\n### Patch Changes\n\n- Updated dependencies [[`ed6cd82`](https://github.com/jsrepojs/jsrepo/commit/ed6cd82054a39a74513d39631d406dad829be9a9)]:\n  - jsrepo@3.0.4\n\n## 0.0.4\n### Patch Changes\n\n- Updated dependencies [[`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0)]:\n  - jsrepo@3.0.3\n\n## 0.0.3\n### Patch Changes\n\n- Updated dependencies [[`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59), [`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59)]:\n  - jsrepo@3.0.2\n\n## 0.0.2\n### Patch Changes\n\n- Updated dependencies [[`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad)]:\n  - jsrepo@3.0.1\n\n## 0.0.1\n### Patch Changes\n\n\n- fix: add banner to ensure mcp works with `npx` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- chore: update docs links ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- rename `search` tool to `list` and make searching optional ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- chore: bump deps ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- initial beta release ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n- Updated dependencies [[`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843)]:\n  - jsrepo@3.0.0\n\n## 0.0.1-beta.31\n### Patch Changes\n\n- Updated dependencies [[`9256a16`](https://github.com/jsrepojs/jsrepo/commit/9256a16d6ae360840097a7824dcf89743f7d9c69)]:\n  - jsrepo@3.0.0-beta.29\n\n## 0.0.1-beta.30\n### Patch Changes\n\n- Updated dependencies [[`db08900`](https://github.com/jsrepojs/jsrepo/commit/db08900353eac423d359b437362802c3f4b5b331)]:\n  - jsrepo@3.0.0-beta.28\n\n## 0.0.1-beta.29\n### Patch Changes\n\n- Updated dependencies [[`a5b3f2f`](https://github.com/jsrepojs/jsrepo/commit/a5b3f2f9c18539b7816af385c5b477cfdf991261)]:\n  - jsrepo@3.0.0-beta.27\n\n## 0.0.1-beta.28\n### Patch Changes\n\n- Updated dependencies [[`2d20f97`](https://github.com/jsrepojs/jsrepo/commit/2d20f97614e90bdc2b99e0347e13612687830466)]:\n  - jsrepo@3.0.0-beta.26\n\n## 0.0.1-beta.27\n### Patch Changes\n\n- Updated dependencies [[`44724ef`](https://github.com/jsrepojs/jsrepo/commit/44724ef4fbf15315b420328ee3dd5a158124fc94), [`dc4b4c9`](https://github.com/jsrepojs/jsrepo/commit/dc4b4c9892ede2e8f619b9af8af479fc0e5f72bc)]:\n  - jsrepo@3.0.0-beta.25\n\n## 0.0.1-beta.26\n### Patch Changes\n\n- Updated dependencies [[`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc), [`eb5f7c0`](https://github.com/jsrepojs/jsrepo/commit/eb5f7c03bf2d68ebcf1b72d9950aa2549acb0873), [`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc)]:\n  - jsrepo@3.0.0-beta.24\n\n## 0.0.1-beta.25\n### Patch Changes\n\n- Updated dependencies [[`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4), [`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4)]:\n  - jsrepo@3.0.0-beta.23\n\n## 0.0.1-beta.24\n### Patch Changes\n\n- Updated dependencies [[`01e6c72`](https://github.com/jsrepojs/jsrepo/commit/01e6c7261b29ab1d3a6b29b728fb4fc92e43e8ce)]:\n  - jsrepo@3.0.0-beta.22\n\n## 0.0.1-beta.23\n### Patch Changes\n\n- Updated dependencies [[`cbb1656`](https://github.com/jsrepojs/jsrepo/commit/cbb165612ec61d9e3e2c0277fbd3e7e684dc881e)]:\n  - jsrepo@3.0.0-beta.21\n\n## 0.0.1-beta.22\n### Patch Changes\n\n\n- fix: add banner to ensure mcp works with `npx` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 0.0.1-beta.21\n### Patch Changes\n\n- Updated dependencies [[`b378550`](https://github.com/jsrepojs/jsrepo/commit/b378550b23ebc4df17770176b0ec74a6a69d2f18)]:\n  - jsrepo@3.0.0-beta.20\n\n## 0.0.1-beta.20\n### Patch Changes\n\n- Updated dependencies [[`86260de`](https://github.com/jsrepojs/jsrepo/commit/86260de53bdf690db170f97baa98c17e551d0d6d)]:\n  - jsrepo@3.0.0-beta.19\n\n## 0.0.1-beta.19\n### Patch Changes\n\n- Updated dependencies [[`fec54f8`](https://github.com/jsrepojs/jsrepo/commit/fec54f88e9a15f5c8d6bcc931c18c52758e1bcb6), [`dc9c06f`](https://github.com/jsrepojs/jsrepo/commit/dc9c06fa996a88d241378b31b5fd218dee592837)]:\n  - jsrepo@3.0.0-beta.18\n\n## 0.0.1-beta.18\n### Patch Changes\n\n- Updated dependencies [[`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563), [`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563)]:\n  - jsrepo@3.0.0-beta.17\n\n## 0.0.1-beta.17\n### Patch Changes\n\n- Updated dependencies [[`e2cfb54`](https://github.com/jsrepojs/jsrepo/commit/e2cfb54e2e7e8149d88ec2b370393ac1a9704e85)]:\n  - jsrepo@3.0.0-beta.16\n\n## 0.0.1-beta.16\n### Patch Changes\n\n- Updated dependencies [[`7541537`](https://github.com/jsrepojs/jsrepo/commit/754153741b409e92e867d306c2b0999a64f74b6d)]:\n  - jsrepo@3.0.0-beta.15\n\n## 0.0.1-beta.15\n### Patch Changes\n\n- Updated dependencies [[`dc0a14d`](https://github.com/jsrepojs/jsrepo/commit/dc0a14ddbf1da96705ad72e55f839675bc232dab)]:\n  - jsrepo@3.0.0-beta.14\n\n## 0.0.1-beta.14\n### Patch Changes\n\n- Updated dependencies [[`c168a33`](https://github.com/jsrepojs/jsrepo/commit/c168a33477541ff026a7247736a14c6dad4efa53), [`9f93eae`](https://github.com/jsrepojs/jsrepo/commit/9f93eae1cad08f934f4c50c45f5a70538c487a63)]:\n  - jsrepo@3.0.0-beta.13\n\n## 0.0.1-beta.13\n### Patch Changes\n\n- Updated dependencies [[`bef071f`](https://github.com/jsrepojs/jsrepo/commit/bef071fde5f7d8143346abdffd766ed8dec866f3)]:\n  - jsrepo@3.0.0-beta.12\n\n## 0.0.1-beta.12\n### Patch Changes\n\n- Updated dependencies [[`f9d2e1f`](https://github.com/jsrepojs/jsrepo/commit/f9d2e1f906f0d39b8c341c2aa678ac31ce0a2f3f)]:\n  - jsrepo@3.0.0-beta.11\n\n## 0.0.1-beta.11\n### Patch Changes\n\n- Updated dependencies [[`709a24d`](https://github.com/jsrepojs/jsrepo/commit/709a24d734ed712fd60add5f2b4b70243908e94b)]:\n  - jsrepo@3.0.0-beta.10\n\n## 0.0.1-beta.10\n### Patch Changes\n\n- Updated dependencies [[`4c664ed`](https://github.com/jsrepojs/jsrepo/commit/4c664ed08947f4a1906f80655b8fd12b7b6ec63b)]:\n  - jsrepo@3.0.0-beta.9\n\n## 0.0.1-beta.9\n### Patch Changes\n\n- Updated dependencies [[`0c33653`](https://github.com/jsrepojs/jsrepo/commit/0c336530ead46f05e85ece847a3dc76a51149c82)]:\n  - jsrepo@3.0.0-beta.8\n\n## 0.0.1-beta.8\n### Patch Changes\n\n- Updated dependencies [[`5e5759b`](https://github.com/jsrepojs/jsrepo/commit/5e5759be0e35f7b46664198ab3156b6998532c28)]:\n  - jsrepo@3.0.0-beta.7\n\n## 0.0.1-beta.7\n### Patch Changes\n\n\n- rename `search` tool to `list` and make searching optional ([#649](https://github.com/jsrepojs/jsrepo/pull/649))\n\n## 0.0.1-beta.6\n### Patch Changes\n\n- Updated dependencies [[`bfaabbb`](https://github.com/jsrepojs/jsrepo/commit/bfaabbb7ae29e0b511e9e84f61fdfb922af6b413)]:\n  - jsrepo@3.0.0-beta.6\n\n## 0.0.1-beta.5\n### Patch Changes\n\n- Updated dependencies [[`95fd7da`](https://github.com/jsrepojs/jsrepo/commit/95fd7da66287c6595ad1fd3de25664719aa6c9b4)]:\n  - jsrepo@3.0.0-beta.5\n\n## 0.0.1-beta.4\n### Patch Changes\n\n- Updated dependencies [[`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a)]:\n  - jsrepo@3.0.0-beta.4\n\n## 0.0.1-beta.3\n### Patch Changes\n\n- Updated dependencies [[`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857), [`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857)]:\n  - jsrepo@3.0.0-beta.3\n\n## 0.0.1-beta.2\n### Patch Changes\n\n\n- chore: bump deps ([#641](https://github.com/jsrepojs/jsrepo/pull/641))\n\n- Updated dependencies [[`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4), [`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4)]:\n  - jsrepo@3.0.0-beta.2\n\n## 0.0.1-beta.1\n### Patch Changes\n\n\n- chore: update docs links ([#637](https://github.com/jsrepojs/jsrepo/pull/637))\n\n- Updated dependencies [[`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2b9e4be`](https://github.com/jsrepojs/jsrepo/commit/2b9e4be45964bf2cde16f36515bf760e3ddf66a7)]:\n  - jsrepo@3.0.0-beta.1\n\n## 0.0.1-beta.0\n### Patch Changes\n\n\n- initial beta release ([#635](https://github.com/jsrepojs/jsrepo/pull/635))\n\n- Updated dependencies [[`4416209`](https://github.com/jsrepojs/jsrepo/commit/44162092b38171d68817f4d0598f3ec8ddcc795d)]:\n  - jsrepo@3.0.0-beta.0\n"
  },
  {
    "path": "packages/mcp/README.md",
    "content": "# @jsrepo/mcp\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/mcp)](https://npmjs.com/package/@jsrepo/mcp)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/mcp)](https://npmjs.com/package/@jsrepo/mcp)\n\nThe MCP server for **jsrepo**.\n\n## Usage\n\nYou can automatically configure the MCP server for your environment by running the following command:\n\n```sh\nnpx jsrepo config mcp\n```\n\n### Manual Configuration\n\nJSON config (for Cursor, Claude Code, Antigravity):\n\n```json\n{\n\t\"mcpServers\": {\n\t\t\"jsrepo\": {\n\t\t\t\"command\": \"npx\",\n\t\t\t\"args\": [\"@jsrepo/mcp\"]\n\t\t}\n\t}\n}\n```\n\nJSON config (for VS Code):\n\n```json\n{\n\t\"servers\": {\n\t\t\"jsrepo\": {\n\t\t\t\"command\": \"npx\",\n\t\t\t\"args\": [\"@jsrepo/mcp\"]\n\t\t}\n\t}\n}\n```\n\nTOML config (for Codex):\n\n```toml\n[mcp_servers.jsrepo]\ncommand = \"npx\"\nargs = [\"@jsrepo/mcp\"]\n```\n\n## Available Tools\n\n| Tool   | Description                                       |\n| ------ | ------------------------------------------------- |\n| `add`  | Add a registry item or items to the users project |\n| `view` | View a registry item                              |\n"
  },
  {
    "path": "packages/mcp/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/mcp/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/mcp\",\n\t\"description\": \"An MCP server for jsrepo.\",\n\t\"version\": \"0.0.26\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev/docs/mcp\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"mcp\",\n\t\t\"server\",\n\t\t\"ai\"\n\t],\n\t\"type\": \"module\",\n\t\"bin\": \"./dist/index.mjs\",\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"start\": \"node ./dist/index.mjs mcp\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"mcp:inspector\": \"pnpm dlx @modelcontextprotocol/inspector node ./dist/index.mjs mcp\",\n\t\t\"test\": \"vitest\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"dependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@tmcp/adapter-zod\": \"^0.1.7\",\n\t\t\"@tmcp/transport-in-memory\": \"^0.0.5\",\n\t\t\"@tmcp/transport-stdio\": \"^0.4.1\",\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"dedent\": \"catalog:\",\n\t\t\"fuzzysort\": \"^3.1.0\",\n\t\t\"picocolors\": \"catalog:\",\n\t\t\"tmcp\": \"^1.19.2\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"vitest\": \"catalog:\",\n\t\t\"zod\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/mcp/src/index.ts",
    "content": "#!/usr/bin/env node\n\nimport { StdioTransport } from '@tmcp/transport-stdio';\nimport pc from 'picocolors';\nimport { server } from './server';\n\nconst transport = new StdioTransport(server);\ntransport.listen();\n\n// Important: stdout must remain reserved for MCP JSON-RPC traffic.\n// Log any human-readable messages to stderr so MCP clients (like Antigravity) don't see non-JSON text when parsing responses.\nprocess.stderr.write(`Server running...\\n`);\nprocess.stderr.write(pc.dim(`Press ${pc.bold('Ctrl+C')} to exit\\n`));\n"
  },
  {
    "path": "packages/mcp/src/server.ts",
    "content": "import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';\nimport dedent from 'dedent';\nimport fuzzysort from 'fuzzysort';\nimport {\n\tgetPathsForItems,\n\tnormalizeItemTypeForPath,\n\tprepareUpdates,\n\tpromptAddEnvVars,\n\tRegistryItemNotFoundError,\n\ttype RegistryItemWithContent,\n\tresolveAndFetchAllItems,\n\tupdateFiles,\n} from 'jsrepo';\nimport { loadConfigSearch } from 'jsrepo/config';\nimport { DEFAULT_PROVIDERS } from 'jsrepo/providers';\nimport {\n\ttype AbsolutePath,\n\tparseWantedItems,\n\tpromptInstallDependenciesByEcosystem,\n\tresolveRegistries,\n\tresolveWantedItems,\n\tresolveWithRoles,\n} from 'jsrepo/utils';\nimport { McpServer } from 'tmcp';\nimport { z } from 'zod';\nimport pkg from '../package.json';\nimport { commonOptions } from './utils';\n\nconst adapter = new ZodJsonSchemaAdapter();\nconst server = new McpServer(\n\t{\n\t\tname: 'jsrepo',\n\t\tversion: pkg.version,\n\t\tdescription:\n\t\t\t'An MCP server for adding, viewing, and searching items from jsrepo registries.',\n\t},\n\t{\n\t\tadapter,\n\t\tcapabilities: {\n\t\t\ttools: { listChanged: true },\n\t\t},\n\t}\n);\n\nserver.tool(\n\t{\n\t\tname: 'add_item_to_project',\n\t\tdescription: 'Add a registry item or items to the users project.',\n\t\tschema: z.object({\n\t\t\tcwd: commonOptions.cwd,\n\t\t\titemNames: z\n\t\t\t\t.array(\n\t\t\t\t\tz\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.describe(\n\t\t\t\t\t\t\t'The registry and name of the item to add `<registry>/<item-name>`. i.e. \"github/ieedan/std/math\", \"@ieedan/std/math\", \"https://example.com/registry/math\"'\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\t.describe(\n\t\t\t\t\t'Fully qualifed registry items to add. i.e. [\"github/ieedan/std/math\", \"@ieedan/std/math\", \"https://example.com/registry/math\"]'\n\t\t\t\t),\n\t\t\twith: z\n\t\t\t\t.array(z.string())\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Include files with these roles. Built-in optional roles: example, doc, test. Custom roles are also supported.'\n\t\t\t\t),\n\t\t\twithExamples: z.boolean().optional().describe('Add items with examples.'),\n\t\t\twithDocs: z.boolean().optional().describe('Add items with docs.'),\n\t\t\twithTests: z.boolean().optional().describe('Add items with tests.'),\n\t\t}),\n\t},\n\tasync ({ itemNames, ...options }) => {\n\t\tconst withRoles = resolveWithRoles(options);\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: options.cwd as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\tconst providers = configResult?.config?.providers ?? DEFAULT_PROVIDERS;\n\t\tconst registries = configResult?.config?.registries ?? [];\n\n\t\tconst { wantedItems, neededRegistries } = parseWantedItems(itemNames, {\n\t\t\tproviders,\n\t\t\tregistries,\n\t\t}).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst resolvedRegistries = (\n\t\t\tawait resolveRegistries(neededRegistries, {\n\t\t\t\tcwd: options.cwd as AbsolutePath,\n\t\t\t\tproviders,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst resolvedWantedItems = (\n\t\t\tawait resolveWantedItems(wantedItems, {\n\t\t\t\tresolvedRegistries,\n\t\t\t\tnonInteractive: true,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst items = (\n\t\t\tawait resolveAndFetchAllItems(resolvedWantedItems, {\n\t\t\t\twithRoles,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst getPathsForItemsResult = await getPathsForItems({\n\t\t\titems,\n\t\t\tconfig: configResult?.config,\n\t\t\toptions: { cwd: options.cwd as AbsolutePath, yes: true },\n\t\t});\n\t\tif (getPathsForItemsResult.isErr()) {\n\t\t\t// give the llm a way to fix the error\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\ttext: dedent`\n\t\t\t\t\t\tFailed to get paths for items: ${getPathsForItemsResult.error.message}.\n\t\t\t\t\t\tYou will need to add the paths to the users config file (if they aren't already added) like so:\n\t\t\t\t\t\t\\`\\`\\`\n\t\t\t\t\t\timport { defineConfig } from \"jsrepo\";\n\n\t\t\t\t\t\texport default defineConfig({\n\t\t\t\t\t\t\t// ...\n\t\t\t\t\t\t\tpaths: {\n\t\t\t\t\t\t\t    // ...\n\t\t\t\t\t\t\t\t${items.map((item) => `\"${normalizeItemTypeForPath(item.type)}\": \"put the expected path here\",`).join('\\n')}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t\t\\`\\`\\`\n\t\t\t\t\t\t`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t}\n\t\tconst { itemPaths } = getPathsForItemsResult.value;\n\n\t\tconst { neededDependencies, neededEnvVars, neededFiles } = (\n\t\t\tawait prepareUpdates({\n\t\t\t\tconfigResult,\n\t\t\t\toptions: {\n\t\t\t\t\tcwd: options.cwd as AbsolutePath,\n\t\t\t\t\tyes: true,\n\t\t\t\t\twithRoles,\n\t\t\t\t},\n\t\t\t\titemPaths,\n\t\t\t\titems,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tawait updateFiles({\n\t\t\tfiles: neededFiles,\n\t\t\toptions: { overwrite: true, cwd: options.cwd, expand: false, maxUnchanged: 10 },\n\t\t});\n\n\t\tif (neededEnvVars)\n\t\t\tawait promptAddEnvVars(neededEnvVars, {\n\t\t\t\toptions: { cwd: options.cwd as AbsolutePath, yes: true },\n\t\t\t});\n\n\t\tawait promptInstallDependenciesByEcosystem(neededDependencies, {\n\t\t\toptions: { cwd: options.cwd as AbsolutePath, yes: true },\n\t\t\tconfig: configResult?.config,\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: dedent`Added ${items.length} items to the users project:\n\t\t\t\t\t${items.map((item) => displayItemDetails(item)).join('\\n')}`,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n);\n\nserver.tool(\n\t{\n\t\tname: 'view_registry_item',\n\t\tdescription: 'View the code and information about a registry item.',\n\t\tschema: z.object({\n\t\t\tcwd: commonOptions.cwd,\n\t\t\titem: z\n\t\t\t\t.string()\n\t\t\t\t.describe(\n\t\t\t\t\t'The registry and name of the item to add `<registry>/<item-name>`. i.e. \"github/ieedan/std/math\", \"@ieedan/std/math\", \"https://example.com/registry/math\"'\n\t\t\t\t),\n\t\t}),\n\t},\n\tasync ({ cwd, item }) => {\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: cwd as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\tconst providers = configResult?.config?.providers ?? DEFAULT_PROVIDERS;\n\t\tconst registries = configResult?.config?.registries ?? [];\n\n\t\tconst { wantedItems, neededRegistries } = parseWantedItems([item], {\n\t\t\tproviders,\n\t\t\tregistries,\n\t\t}).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst resolvedRegistries = (\n\t\t\tawait resolveRegistries(neededRegistries, {\n\t\t\t\tcwd: cwd as AbsolutePath,\n\t\t\t\tproviders,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst resolvedWantedItems = (\n\t\t\tawait resolveWantedItems(wantedItems, {\n\t\t\t\tresolvedRegistries,\n\t\t\t\tnonInteractive: true,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst items = (await resolveAndFetchAllItems(resolvedWantedItems)).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst wantedItem = wantedItems[0]!;\n\n\t\tconst itemResult = items.find((i) => i.name === wantedItem.itemName);\n\t\tif (!itemResult)\n\t\t\tthrow new RegistryItemNotFoundError(wantedItem.itemName, wantedItem.registryUrl);\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: displayItemDetails(itemResult),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n);\n\nserver.tool(\n\t{\n\t\tname: 'list_items_in_registry',\n\t\tdescription:\n\t\t\t'List items in a registry. You can optionally provide a query to filter the results.',\n\t\tschema: z.object({\n\t\t\tcwd: commonOptions.cwd,\n\t\t\tregistries: z\n\t\t\t\t.array(z.string())\n\t\t\t\t.describe(\n\t\t\t\t\t'The registries to search for items in. i.e. [\"github/ieedan/std\", \"@ieedan/std\"]'\n\t\t\t\t),\n\t\t\tquery: z.string().optional().describe('The query to search for. i.e. \"math\"'),\n\t\t\tlimit: z\n\t\t\t\t.number()\n\t\t\t\t.optional()\n\t\t\t\t.describe('The maximum number of items to return. Defaults to 10.'),\n\t\t\toffset: z.number().optional().describe('The numbers to skip for pagination.'),\n\t\t}),\n\t},\n\tasync ({ cwd, registries, query, limit: l, offset: o }) => {\n\t\tconst limit = l ?? 10;\n\t\tconst offset = o ?? 0;\n\t\tconst configResult = await loadConfigSearch({\n\t\t\tcwd: cwd as AbsolutePath,\n\t\t\tpromptForContinueIfNull: false,\n\t\t});\n\n\t\tconst providers = configResult?.config?.providers ?? DEFAULT_PROVIDERS;\n\n\t\tconst resolvedRegistries = (\n\t\t\tawait resolveRegistries(registries, {\n\t\t\t\tcwd: cwd as AbsolutePath,\n\t\t\t\tproviders,\n\t\t\t})\n\t\t).match(\n\t\t\t(value) => value,\n\t\t\t(error) => {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t);\n\n\t\tconst candidateItems = Array.from(resolvedRegistries.values()).flatMap((registry) =>\n\t\t\tregistry.manifest.items.map((item) => ({ item, registry }))\n\t\t);\n\n\t\tlet results: typeof candidateItems;\n\t\tif (query) {\n\t\t\tconst sortedResults = fuzzysort.go(query, candidateItems, {\n\t\t\t\tkeys: ['item.name', 'item.description', 'item.type', 'item.categories'],\n\t\t\t});\n\n\t\t\tif (sortedResults.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\t\ttext: dedent`\n\t\t\t\t\t\t\tNo results found for \"${query}\".\n\t\t\t\t\t\t\t`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tresults = sortedResults.map((result) => result.obj);\n\t\t} else {\n\t\t\tresults = candidateItems;\n\t\t}\n\n\t\tconst cutResults = results.slice(offset, Math.min(offset + limit, results.length));\n\n\t\tfunction displayItem(item: (typeof candidateItems)[number]) {\n\t\t\treturn dedent`\n\t\t\t## ${item.registry.url}/${item.item.name} - ${item.item.type}\n\t\t\t${item.item.description ?? ''}\n\n\t\t\tFiles: \n\t\t\t${item.item.files.map((file) => `- ${file.path}`).join('\\n')}\n\t\t\t`;\n\t\t}\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\ttext: dedent`\n\t\t\t\t\t# Search results\n\t\t\t\t\tFound ${results.length} results for \"${query}\".\n\t\t\t\t\tShowing ${cutResults.length} results.\n\n\t\t\t\t\t${cutResults.map(displayItem).join('\\n')}\n\t\t\t\t\t`,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n);\n\nfunction displayItemDetails(item: RegistryItemWithContent): string {\n\treturn dedent`\n\t# ${item.name} - ${item.type}${item.categories ? ` (Categories: ${item.categories.join(', ')})` : ''}\n\t${item.description ?? ''}\n\n\t## Files\n\t${item.files\n\t\t?.filter((file) => file.role === undefined || file.role === 'file')\n\t\t.map((file) => `\\`\\`\\`${file.path}\\n${file.content}\\n\\`\\`\\``)\n\t\t.join('\\n')}\n\n\t## Dependencies\n\t${item.dependencies?.map((dependency) => `- ${typeof dependency === 'string' ? dependency : dependency.name}`).join('\\n')}\n\n\t## Examples\n\t${item.files\n\t\t?.filter((file) => file.role === 'example')\n\t\t.map((file) => `\\`\\`\\`${file.path}\\n${file.content}\\n\\`\\`\\``)\n\t\t.join('\\n')}\n\n\t## Docs\n\t${item.files\n\t\t?.filter((file) => file.role === 'doc')\n\t\t.map((file) => `\\`\\`\\`${file.path}\\n${file.content}\\n\\`\\`\\``)\n\t\t.join('\\n')}\n\n\t## Tests\n\t${item.files\n\t\t?.filter((file) => file.role === 'test')\n\t\t.map((file) => `\\`\\`\\`${file.path}\\n${file.content}\\n\\`\\`\\``)\n\t\t.join('\\n')}\n\n\t## Environment Variables\n\tEnvironment variables are required for the item to work. Blank variables will need to be filled in.\n\t\\`\\`\\`env\n\t${Object.entries(item.envVars ?? {})\n\t\t.map(([name, value]) => `${name}=\"${value}\"`)\n\t\t.join('\\n')}\n\t\\`\\`\\`\n\n\tAdditional info:\n\t${Object.entries(item.meta ?? {})\n\t\t.map(([key, value]) => `- ${key}: ${value}`)\n\t\t.join('\\n')}\n\t`;\n}\n\nexport { server };\n"
  },
  {
    "path": "packages/mcp/src/utils.ts",
    "content": "import { z } from 'zod';\n\nexport const commonOptions = {\n\tcwd: z\n\t\t.string()\n\t\t.describe(\n\t\t\t'The path of the current working directory. Used to find the jsrepo.config file.'\n\t\t),\n};\n"
  },
  {
    "path": "packages/mcp/tests/mcp.test.ts",
    "content": "import { InMemoryTransport } from '@tmcp/transport-in-memory';\nimport { assert, describe, expect, it } from 'vitest';\nimport { server } from '../src/server';\n\nconst transport = new InMemoryTransport(server);\nconst session = transport.session();\n\ndescribe('server', () => {\n\t// kinda a stupid test for now but it ensures I ship the MCP server with the correct tools\n\tit('should list the expected tools', async () => {\n\t\tconst listResult = await session.listTools();\n\n\t\tconst expectedTools = [\n\t\t\t'add_item_to_project',\n\t\t\t'view_registry_item',\n\t\t\t'list_items_in_registry',\n\t\t];\n\n\t\tconst toolNames = listResult.tools.map((t) => t.name);\n\n\t\tfor (const tool of expectedTools) {\n\t\t\texpect(toolNames).toContain(tool);\n\t\t}\n\t});\n\n\tit('should list items for a registry', async () => {\n\t\tconst result = await session.callTool('list_items_in_registry', {\n\t\t\tcwd: process.cwd(),\n\t\t\tregistries: ['@ieedan/shadcn-svelte-extras@beta'],\n\t\t\tquery: 'math',\n\t\t});\n\n\t\tassert(result.content?.[0].type === 'text');\n\t\texpect(result.content[0].text).toContain('# Search results\\nFound');\n\t});\n\n\tit('should show a specific registry item', async () => {\n\t\tconst result = await session.callTool('view_registry_item', {\n\t\t\tcwd: process.cwd(),\n\t\t\titem: '@ieedan/shadcn-svelte-extras@beta/button',\n\t\t});\n\n\t\tassert(result.content?.[0].type === 'text');\n\t\texpect(result.content[0].text).toContain('buttonVariants');\n\t});\n});\n"
  },
  {
    "path": "packages/mcp/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/mcp/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/migrate/CHANGELOG.md",
    "content": "# @jsrepo/migrate\n\n## 0.0.25\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 0.0.24\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 0.0.23\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 0.0.22\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 0.0.21\n### Patch Changes\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 0.0.20\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 0.0.19\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 0.0.18\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 0.0.17\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 0.0.16\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 0.0.15\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 0.0.14\n### Patch Changes\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n\n## 0.0.13\n### Patch Changes\n\n- Updated dependencies [[`fcbb4b6`](https://github.com/jsrepojs/jsrepo/commit/fcbb4b69d684844a99f403e6880071302a176a27), [`43584f1`](https://github.com/jsrepojs/jsrepo/commit/43584f102a7e085195a4b738a56cb9a8aac65383)]:\n  - jsrepo@3.1.0\n\n## 0.0.12\n### Patch Changes\n\n- Updated dependencies [[`0bf74a4`](https://github.com/jsrepojs/jsrepo/commit/0bf74a448c0ff526aa353a0924646b3d647fd1bd), [`069cc97`](https://github.com/jsrepojs/jsrepo/commit/069cc979f6a4ecaac71cb5beece5c83860fbb915)]:\n  - jsrepo@3.0.11\n\n## 0.0.11\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n- Updated dependencies [[`07d0399`](https://github.com/jsrepojs/jsrepo/commit/07d039908e241f7bdcc084c5697a9534fd2e4788), [`2362f8e`](https://github.com/jsrepojs/jsrepo/commit/2362f8e90ad79ba6df0fe6cfa4ceb82362ee5b62)]:\n  - jsrepo@3.0.10\n\n## 0.0.10\n### Patch Changes\n\n\n- fix: Improve package manager detection to fallback on the detected user agent ([#725](https://github.com/jsrepojs/jsrepo/pull/725))\n\n- Updated dependencies [[`7d4e98a`](https://github.com/jsrepojs/jsrepo/commit/7d4e98a5e1588d430f5195181b30e59303e25efb)]:\n  - jsrepo@3.0.9\n\n## 0.0.9\n### Patch Changes\n\n- Updated dependencies [[`a704bd0`](https://github.com/jsrepojs/jsrepo/commit/a704bd09d678ff608c5c2e61c8b91f8c09717012)]:\n  - jsrepo@3.0.8\n\n## 0.0.8\n### Patch Changes\n\n- Updated dependencies [[`da29502`](https://github.com/jsrepojs/jsrepo/commit/da29502db16dccd2761b88f33e3a921ff7fd21ec)]:\n  - jsrepo@3.0.7\n\n## 0.0.7\n### Patch Changes\n\n- Updated dependencies [[`129abaf`](https://github.com/jsrepojs/jsrepo/commit/129abafd8b560e8240c6edb414ce3b74d4c7b3a5)]:\n  - jsrepo@3.0.6\n\n## 0.0.6\n### Patch Changes\n\n- Updated dependencies [[`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5)]:\n  - jsrepo@3.0.5\n\n## 0.0.5\n### Patch Changes\n\n- Updated dependencies [[`ed6cd82`](https://github.com/jsrepojs/jsrepo/commit/ed6cd82054a39a74513d39631d406dad829be9a9)]:\n  - jsrepo@3.0.4\n\n## 0.0.4\n### Patch Changes\n\n- Updated dependencies [[`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0)]:\n  - jsrepo@3.0.3\n\n## 0.0.3\n### Patch Changes\n\n- Updated dependencies [[`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59), [`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59)]:\n  - jsrepo@3.0.2\n\n## 0.0.2\n### Patch Changes\n\n- Updated dependencies [[`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad)]:\n  - jsrepo@3.0.1\n\n## 0.0.1\n### Patch Changes\n\n\n- fix: improve error formatting ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- initial release ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix: only build the registry when a registry config was migrated ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n- Updated dependencies [[`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843)]:\n  - jsrepo@3.0.0\n\n## 0.0.1-beta.8\n### Patch Changes\n\n- Updated dependencies [[`9256a16`](https://github.com/jsrepojs/jsrepo/commit/9256a16d6ae360840097a7824dcf89743f7d9c69)]:\n  - jsrepo@3.0.0-beta.29\n\n## 0.0.1-beta.7\n### Patch Changes\n\n- Updated dependencies [[`db08900`](https://github.com/jsrepojs/jsrepo/commit/db08900353eac423d359b437362802c3f4b5b331)]:\n  - jsrepo@3.0.0-beta.28\n\n## 0.0.1-beta.6\n### Patch Changes\n\n- Updated dependencies [[`a5b3f2f`](https://github.com/jsrepojs/jsrepo/commit/a5b3f2f9c18539b7816af385c5b477cfdf991261)]:\n  - jsrepo@3.0.0-beta.27\n\n## 0.0.1-beta.5\n### Patch Changes\n\n\n- fix: only build the registry when a registry config was migrated ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 0.0.1-beta.4\n### Patch Changes\n\n- Updated dependencies [[`2d20f97`](https://github.com/jsrepojs/jsrepo/commit/2d20f97614e90bdc2b99e0347e13612687830466)]:\n  - jsrepo@3.0.0-beta.26\n\n## 0.0.1-beta.3\n### Patch Changes\n\n- Updated dependencies [[`44724ef`](https://github.com/jsrepojs/jsrepo/commit/44724ef4fbf15315b420328ee3dd5a158124fc94), [`dc4b4c9`](https://github.com/jsrepojs/jsrepo/commit/dc4b4c9892ede2e8f619b9af8af479fc0e5f72bc)]:\n  - jsrepo@3.0.0-beta.25\n\n## 0.0.1-beta.2\n### Patch Changes\n\n\n- fix: improve error formatting ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n- Updated dependencies [[`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc), [`eb5f7c0`](https://github.com/jsrepojs/jsrepo/commit/eb5f7c03bf2d68ebcf1b72d9950aa2549acb0873), [`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc)]:\n  - jsrepo@3.0.0-beta.24\n\n## 0.0.1-beta.1\n### Patch Changes\n\n- Updated dependencies [[`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4), [`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4)]:\n  - jsrepo@3.0.0-beta.23\n\n## 0.0.1-beta.0\n### Patch Changes\n\n\n- initial release ([#678](https://github.com/jsrepojs/jsrepo/pull/678))\n"
  },
  {
    "path": "packages/migrate/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 jsrepo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/migrate/README.md",
    "content": "# @jsrepo/migrate\n\nAutomatically migrate your jsrepo project to the newest version of jsrepo.\n\n```sh\npnpm dlx @jsrepo/migrate v3\n```\n\n## Commands\n\n### `v3`\n\n```sh\npnpm dlx @jsrepo/migrate v3\n```\n\n- Migrates your `jsrepo-build-config.json` and `jsrepo.json` files into the new `jsrepo.config.ts` file.\n- Installs `jsrepo` and the correct formatting transform (if you were using one)\n- Builds your registry using both v2 and v3 to ensure compatibility"
  },
  {
    "path": "packages/migrate/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/migrate/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/migrate\",\n\t\"description\": \"Automatically migrate your jsrepo project to the newest version of jsrepo.\",\n\t\"version\": \"0.0.25\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"migrate\",\n\t\t\"jsrepo\",\n\t\t\"cli\",\n\t\t\"v3\"\n\t],\n\t\"type\": \"module\",\n\t\"bin\": \"./dist/bin.mjs\",\n\t\"main\": \"./dist/bin.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"start\": \"node ./dist/bin.mjs\",\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"dependencies\": {\n\t\t\"commander\": \"catalog:\",\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@clack/prompts\": \"1.0.0-alpha.6\",\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"@types/semver\": \"^7.7.1\",\n\t\t\"nevereverthrow\": \"^0.3.0\",\n\t\t\"package-manager-detector\": \"^1.6.0\",\n\t\t\"pathe\": \"^2.0.3\",\n\t\t\"picocolors\": \"catalog:\",\n\t\t\"semver\": \"^7.7.3\",\n\t\t\"tinyexec\": \"^1.0.2\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"zod\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/migrate/src/bin.ts",
    "content": "#!/usr/bin/env node\n\nimport { cli } from '@/cli';\n\ncli.parse();\n"
  },
  {
    "path": "packages/migrate/src/cli.ts",
    "content": "import { program } from 'commander';\nimport pkg from '@/../package.json';\nimport * as commands from '@/commands';\n\nconst cli = program\n\t.name(pkg.name)\n\t.description(pkg.description)\n\t.version(pkg.version)\n\t.addCommand(commands.v3);\n\nexport { cli };\n"
  },
  {
    "path": "packages/migrate/src/commands/index.ts",
    "content": "import { v3 } from '@/commands/v3';\n\nexport { v3 };\n"
  },
  {
    "path": "packages/migrate/src/commands/utils.ts",
    "content": "import * as p from '@clack/prompts';\nimport { Option } from 'commander';\nimport type { Result } from 'nevereverthrow';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport { InvalidOptionsError, JsrepoError } from '@/utils/errors';\nimport type { AbsolutePath } from '@/utils/types';\nimport { safeValidate } from '@/utils/zod';\n\nexport const TRACE_ENV_VAR = 'JSREPO_TRACE';\n\nexport const defaultCommandOptionsSchema = z.object({\n\tcwd: z.string().transform((v) => v as AbsolutePath),\n});\n\nexport const commonOptions = {\n\tyes: new Option('--yes', 'Skip the confirmation prompt.').default(false),\n\tcwd: new Option('--cwd <path>', 'The current working directory.').default(process.cwd()),\n};\n\nexport function parseOptions<T>(\n\tschema: z.ZodSchema<T>,\n\trawOptions: unknown\n): z.infer<typeof schema> {\n\treturn safeValidate(schema, rawOptions).match(\n\t\t(v) => v,\n\t\t(e) => error(new InvalidOptionsError(e.zodError))\n\t);\n}\n\n/**\n * Tries to run the command. If the command fails, it will log the error and exit the program.\n * @param command\n * @returns\n */\nexport async function tryCommand<T, E extends JsrepoError>(\n\tcommand: Promise<Result<T, E>>\n): Promise<T> {\n\ttry {\n\t\tconst result = await command;\n\t\tif (result.isErr()) return error(result.error);\n\t\treturn result.value;\n\t} catch (e) {\n\t\tif (e instanceof JsrepoError) error(e);\n\t\terror(\n\t\t\tnew JsrepoError(e instanceof Error ? e.message : String(e), {\n\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t})\n\t\t);\n\t}\n}\n\n/**\n * Log error and exit the program.\n * @param error - The error to print.\n */\nexport function error(err: Error): never {\n\treturn handleError(err);\n}\n\nfunction handleError(err: Error): never {\n\tp.log.message();\n\tif (process.env[TRACE_ENV_VAR] === '1') {\n\t\tconsole.trace(err);\n\t\tprocess.exit(1);\n\t} else {\n\t\tp.cancel(pc.red(err.toString()));\n\t\tprocess.exit(1);\n\t}\n}\n"
  },
  {
    "path": "packages/migrate/src/commands/v3.ts",
    "content": "import { existsSync } from 'node:fs';\nimport { cancel, confirm, isCancel, log, text } from '@clack/prompts';\nimport { Command } from 'commander';\nimport type { RegistryConfig, RegistryItem, RegistryItemFile } from 'jsrepo/config';\nimport { DEFAULT_PROVIDERS } from 'jsrepo/providers';\nimport { resolveRegistries } from 'jsrepo/utils';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport { detect, resolveCommand } from 'package-manager-detector';\nimport path from 'pathe';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport {\n\tcommonOptions,\n\tdefaultCommandOptionsSchema,\n\tparseOptions,\n\ttryCommand,\n} from '@/commands/utils';\nimport {\n\ttype CLIError,\n\tInvalidJSONError,\n\tJsrepoError,\n\tManifestNotFoundError,\n\tNoPackageJsonFoundError,\n} from '@/utils/errors';\nimport { readFileSync, rmSync, writeFileSync } from '@/utils/fs';\nimport { stringify } from '@/utils/json';\nimport { type PackageJson, tryGetPackage } from '@/utils/package';\nimport { joinAbsolute } from '@/utils/path';\nimport { installDependencies, intro, outro, runCommands } from '@/utils/prompts';\nimport type { AbsolutePath } from '@/utils/types';\nimport {\n\tisDocsFile,\n\tisTestFile,\n\tMANIFEST_FILE_V2,\n\tManifestSchemaV2,\n\tPROJECT_CONFIG_FILE_V2,\n\tProjectConfigSchemaV2,\n\ttype ProjectConfigV2,\n\tREGISTRY_CONFIG_FILE_V2,\n\tRegistryConfigSchemaV2,\n\ttype RegistryConfigV2,\n} from '@/utils/v2/config';\nimport { safeParseFromJSON } from '@/utils/zod';\n\nexport const schema = defaultCommandOptionsSchema.extend({\n\tyes: z.boolean(),\n});\n\nexport type V3Options = z.infer<typeof schema>;\n\nexport const v3 = new Command('v3')\n\t.description('Migrate your jsrepo config to v3.')\n\t.addOption(commonOptions.cwd)\n\t.addOption(commonOptions.yes)\n\t.action(async (rawOptions) => {\n\t\tconst options = parseOptions(schema, rawOptions);\n\n\t\tintro();\n\n\t\tconst result = await tryCommand(runV3(options));\n\n\t\toutro('Successfully migrated to jsrepo v3.');\n\n\t\tif (result.migratedConfigs.registry !== null) {\n\t\t\tprocess.stdout.write(pc.green('   Next steps:\\n\\n'));\n\t\t\tprocess.stdout.write(\n\t\t\t\t`   ${pc.gray('1.')} Update documentation for the \\`jsrepo add\\` command ${pc.gray(\n\t\t\t\t\t`\\`jsrepo add <category>/<item>\\``\n\t\t\t\t)} -> ${pc.cyan(`\\`jsrepo add <item>\\``)}\\n`\n\t\t\t);\n\t\t\tprocess.stdout.write(\n\t\t\t\t`   ${pc.gray('2.')} Add descriptions to registry items (optional)\\n`\n\t\t\t);\n\t\t\tprocess.stdout.write(\n\t\t\t\t`   ${pc.gray('3.')} Checkout the new docs to learn more! ${pc.cyan(`https://jsrepo.dev`)}\\n`\n\t\t\t);\n\t\t\tprocess.stdout.write('\\n');\n\t\t}\n\t});\n\nexport type V3CommandResult = {\n\tconfigPath: string;\n\tmigratedConfigs: {\n\t\tregistry: { oldConfig: RegistryConfigV2; newConfig: RegistryConfig } | null;\n\t\tproject: {\n\t\t\toldConfig: ProjectConfigV2;\n\t\t\tnewConfig: { registries: string[]; paths: Record<string, string> };\n\t\t} | null;\n\t};\n};\n\nexport async function runV3(options: V3Options): Promise<Result<V3CommandResult, CLIError>> {\n\tconst packagePath = joinAbsolute(options.cwd, 'package.json');\n\tconst packageJsonResult = tryGetPackage(packagePath);\n\tif (packageJsonResult.isErr()) return err(new NoPackageJsonFoundError());\n\tconst packageJson = packageJsonResult.value;\n\n\tconst oldRegistryConfigPath = joinAbsolute(options.cwd, REGISTRY_CONFIG_FILE_V2);\n\tlet oldRegistryConfig: RegistryConfigV2 | null;\n\tif (existsSync(oldRegistryConfigPath)) {\n\t\tconst oldConfigResult = readFileSync(oldRegistryConfigPath);\n\t\tif (oldConfigResult.isErr()) return err(oldConfigResult.error);\n\t\tconst oldConfigParseResult = safeParseFromJSON(\n\t\t\tRegistryConfigSchemaV2,\n\t\t\toldConfigResult.value\n\t\t);\n\t\tif (oldConfigParseResult.isErr())\n\t\t\treturn err(new InvalidJSONError(oldConfigParseResult.error));\n\t\toldRegistryConfig = oldConfigParseResult.value;\n\t} else {\n\t\toldRegistryConfig = null;\n\t}\n\n\tconst oldProjectConfigPath = joinAbsolute(options.cwd, PROJECT_CONFIG_FILE_V2);\n\tlet oldProjectConfig: ProjectConfigV2 | null;\n\tif (existsSync(oldProjectConfigPath)) {\n\t\tconst oldConfigResult = readFileSync(oldProjectConfigPath);\n\t\tif (oldConfigResult.isErr()) return err(oldConfigResult.error);\n\t\tconst oldConfigParseResult = safeParseFromJSON(\n\t\t\tProjectConfigSchemaV2,\n\t\t\toldConfigResult.value\n\t\t);\n\t\tif (oldConfigParseResult.isErr())\n\t\t\treturn err(new InvalidJSONError(oldConfigParseResult.error));\n\t\toldProjectConfig = oldConfigParseResult.value;\n\t} else {\n\t\toldProjectConfig = null;\n\t}\n\n\tif (oldRegistryConfig === null && oldProjectConfig === null) {\n\t\treturn err(\n\t\t\tnew JsrepoError(`No configs to migrate at ${path.resolve(options.cwd)}!`, {\n\t\t\t\tsuggestion:\n\t\t\t\t\t'Please ensure you are running this command in the root of your jsrepo project.',\n\t\t\t})\n\t\t);\n\t}\n\n\tlet registryConfig: RegistryConfig | null = null;\n\tif (oldRegistryConfig !== null) {\n\t\tconst registryItemsResult = await migrateRegistryConfig({\n\t\t\tpackageJson,\n\t\t\toldConfigPath: oldRegistryConfigPath,\n\t\t\toldConfig: oldRegistryConfig,\n\t\t\toptions,\n\t\t});\n\t\tif (registryItemsResult.isErr()) return err(registryItemsResult.error);\n\t\tregistryConfig = registryItemsResult.value;\n\t}\n\n\tlet projectConfig: { registries: string[]; paths: Record<string, string> } | null = null;\n\tif (oldProjectConfig !== null) {\n\t\tconst projectConfigResult = await migrateProjectConfig({\n\t\t\toldConfig: oldProjectConfig,\n\t\t\toptions,\n\t\t});\n\t\tif (projectConfigResult.isErr()) return err(projectConfigResult.error);\n\t\tprojectConfig = projectConfigResult.value;\n\t}\n\n\tconst neededDependencies: string[] = [\n\t\t'jsrepo@latest',\n\t\t...(oldProjectConfig?.formatter ? [`@jsrepo/transform-${oldProjectConfig.formatter}`] : []),\n\t];\n\n\tconst newConfigCode = `import { defineConfig } from \"jsrepo\";${\n\t\tregistryConfig\n\t\t\t? `\\nimport { ${oldRegistryConfig?.outputDir ? `distributed` : `repository`} } from \"jsrepo/outputs\";`\n\t\t\t: ''\n\t}${\n\t\toldProjectConfig?.formatter\n\t\t\t? `\\nimport ${oldProjectConfig.formatter} from \"@jsrepo/transform-${oldProjectConfig.formatter}\";`\n\t\t\t: ''\n\t}\n    \nexport default defineConfig({${\n\t\tregistryConfig\n\t\t\t? `\\n\\tregistry: {\n        name: \"${registryConfig.name}\",\n\t\tdescription: ${registryConfig.description ? `\"${registryConfig.description}\"` : undefined},\n\t\thomepage: ${registryConfig.homepage ? `\"${registryConfig.homepage}\"` : undefined},\n\t\tauthors: ${stringify(registryConfig.authors, { format: true })},\n\t\tbugs: ${registryConfig.bugs ? `\"${registryConfig.bugs}\"` : undefined},\n\t\trepository: ${registryConfig.repository ? `\"${registryConfig.repository}\"` : undefined},\n\t\ttags: ${stringify(registryConfig.tags, { format: true })},\n\t\tversion: ${registryConfig.version ? `\"${registryConfig.version}\"` : undefined},\n\t\taccess: ${registryConfig.access ? `\"${registryConfig.access}\"` : undefined},\n\t\tdefaultPaths: ${stringify(registryConfig.defaultPaths, { format: true })},\n\t\texcludeDeps: ${stringify(registryConfig.excludeDeps, { format: true })},\n        outputs: [${\n\t\t\toldRegistryConfig?.outputDir\n\t\t\t\t? `distributed({ dir: \"${oldRegistryConfig.outputDir}\" })`\n\t\t\t\t: `repository({ format: true })`\n\t\t}],\n        items: ${stringify(registryConfig.items, { format: true })}\n    },`\n\t\t\t: ''\n\t}${\n\t\tprojectConfig\n\t\t\t? `\\n\\tregistries: ${stringify(projectConfig.registries, { format: true })},\\n\\tpaths: ${stringify(\n\t\t\t\t\tprojectConfig.paths,\n\t\t\t\t\t{ format: true }\n\t\t\t\t)},${oldProjectConfig?.formatter ? `\\n\\ttransforms: [${oldProjectConfig.formatter}()],` : ''}`\n\t\t\t: ''\n\t}\n});`;\n\n\tconst newConfigPath = joinAbsolute(\n\t\toptions.cwd,\n\t\tpackageJson.type === 'module' ? 'jsrepo.config.ts' : 'jsrepo.config.cjs'\n\t);\n\n\tconst writeNewConfigResult = writeFileSync(newConfigPath, newConfigCode);\n\tif (writeNewConfigResult.isErr()) return err(writeNewConfigResult.error);\n\n\t// build if we migrated a registry config\n\tif (oldRegistryConfig !== null) {\n\t\tconst newBuildCommand = resolveCommand(\n\t\t\t(await detect({ cwd: options.cwd }))?.agent ?? 'npm',\n\t\t\t'execute',\n\t\t\t['jsrepo@latest', 'build']\n\t\t);\n\t\tif (newBuildCommand === null)\n\t\t\treturn err(\n\t\t\t\tnew JsrepoError('Failed to resolve `jsrepo build` command', {\n\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t'Please ensure you can build your registry with the `jsrepo build` command.',\n\t\t\t\t})\n\t\t\t);\n\n\t\tawait runCommands({\n\t\t\ttitle: 'Running `jsrepo build`',\n\t\t\tcwd: options.cwd,\n\t\t\tcommands: [newBuildCommand],\n\t\t\tmessages: {\n\t\t\t\tsuccess: () => 'v3 Build completed successfully',\n\t\t\t\terror: () => {\n\t\t\t\t\tthrow new JsrepoError('Failed to build registry', {\n\t\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t\t'The migration was successful, but the build failed. Please try again.',\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t}\n\n\tawait installDependencies(\n\t\t{\n\t\t\tdependencies: [],\n\t\t\tdevDependencies: neededDependencies,\n\t\t},\n\t\t{\n\t\t\tcwd: options.cwd,\n\t\t}\n\t);\n\n\tlet deleteOldConfigs = options.yes;\n\tif (!options.yes) {\n\t\tconst choice = await confirm({\n\t\t\tmessage: 'Remove old config files?',\n\t\t\tinitialValue: true,\n\t\t});\n\n\t\tif (isCancel(choice)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tdeleteOldConfigs = choice;\n\t}\n\n\tif (deleteOldConfigs) {\n\t\tif (oldRegistryConfig) {\n\t\t\trmSync(oldRegistryConfigPath);\n\t\t\trmSync(joinAbsolute(options.cwd, MANIFEST_FILE_V2));\n\t\t}\n\t\tif (oldProjectConfig) rmSync(oldProjectConfigPath);\n\n\t\tlog.success('Removed old config files.');\n\t}\n\n\treturn ok({\n\t\tconfigPath: path.relative(options.cwd, newConfigPath),\n\t\tmigratedConfigs: {\n\t\t\tregistry:\n\t\t\t\t!oldRegistryConfig || !registryConfig\n\t\t\t\t\t? null\n\t\t\t\t\t: { oldConfig: oldRegistryConfig, newConfig: registryConfig },\n\t\t\tproject:\n\t\t\t\t!oldProjectConfig || !projectConfig\n\t\t\t\t\t? null\n\t\t\t\t\t: { oldConfig: oldProjectConfig, newConfig: projectConfig },\n\t\t},\n\t});\n}\n\nfunction getFileRole(file: string): RegistryItemFile['role'] {\n\tif (isTestFile(file)) return 'test';\n\tif (isDocsFile(file)) return 'doc';\n\treturn undefined;\n}\n\nasync function migrateProjectConfig({\n\toldConfig,\n\toptions,\n}: {\n\toldConfig: ProjectConfigV2;\n\toptions: { cwd: AbsolutePath };\n}): Promise<Result<{ registries: string[]; paths: Record<string, string> }, JsrepoError>> {\n\tif (oldConfig.repos.length > 0) {\n\t\tconst resolvedRegistries = await resolveRegistries(oldConfig.repos, {\n\t\t\tcwd: options.cwd,\n\t\t\tproviders: DEFAULT_PROVIDERS,\n\t\t});\n\t\tif (resolvedRegistries.isErr()) {\n\t\t\tlog.warn(\n\t\t\t\t'There was an error resolving one or more of the registries in your project config. v2 registries are not compatible with jsrepo v3. Please ensure all the registries referenced by your config have upgraded to v3.'\n\t\t\t);\n\t\t}\n\t}\n\n\treturn ok({\n\t\tregistries: oldConfig.repos,\n\t\tpaths: oldConfig.paths ?? {},\n\t});\n}\n\nasync function migrateRegistryConfig({\n\tpackageJson,\n\toldConfigPath,\n\toldConfig,\n\toptions,\n}: {\n\tpackageJson: Partial<PackageJson>;\n\toldConfigPath: AbsolutePath;\n\toldConfig: RegistryConfigV2;\n\toptions: { cwd: AbsolutePath };\n}): Promise<Result<RegistryConfig, JsrepoError>> {\n\tconst writeResult = writeFileSync(\n\t\toldConfigPath,\n\t\t// remove outputDir to ensure we just output to the current directory\n\t\tstringify({ ...oldConfig, outputDir: undefined } satisfies RegistryConfigV2, {\n\t\t\tformat: true,\n\t\t})\n\t);\n\tif (writeResult.isErr()) return err(writeResult.error);\n\n\tconst oldBuildCommand = resolveCommand(\n\t\t(await detect({ cwd: options.cwd }))?.agent ?? 'npm',\n\t\t'execute',\n\t\t['jsrepo@2', 'build']\n\t);\n\tif (oldBuildCommand === null)\n\t\treturn err(\n\t\t\tnew JsrepoError('Failed to resolve `jsrepo build` command', {\n\t\t\t\tsuggestion:\n\t\t\t\t\t'Please ensure you can build your registry with the `jsrepo build` command.',\n\t\t\t})\n\t\t);\n\n\tawait runCommands({\n\t\ttitle: 'Running `jsrepo build`',\n\t\tcwd: options.cwd,\n\t\tcommands: [oldBuildCommand],\n\t\tmessages: {\n\t\t\tsuccess: () => 'v2 Build completed successfully',\n\t\t\terror: () => {\n\t\t\t\tthrow new JsrepoError('Failed to build registry', {\n\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t'Please ensure you can build your registry with the `jsrepo build` command.',\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t});\n\n\tconst manifestPath = joinAbsolute(options.cwd, MANIFEST_FILE_V2);\n\n\tconst manifestResult = readFileSync(manifestPath);\n\tif (manifestResult.isErr()) return err(new ManifestNotFoundError({ path: manifestPath }));\n\tconst manifestParseResult = safeParseFromJSON(ManifestSchemaV2, manifestResult.value);\n\tif (manifestParseResult.isErr()) return err(new InvalidJSONError(manifestParseResult.error));\n\tconst manifest = manifestParseResult.value;\n\n\tif (manifest.name === undefined) {\n\t\tconst name = await text({\n\t\t\tmessage: 'Enter the name of your registry',\n\t\t\tvalidate(value) {\n\t\t\t\tif (!value || value.trim() === '') return 'Please provide a value';\n\t\t\t},\n\t\t\tinitialValue: packageJson.name,\n\t\t\tdefaultValue: packageJson.name,\n\t\t\tplaceholder: packageJson.name,\n\t\t});\n\n\t\tif (isCancel(name)) {\n\t\t\tcancel('Canceled!');\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tmanifest.name = name;\n\t}\n\n\tconst items: RegistryItem[] = [];\n\n\tfor (const category of manifest.categories) {\n\t\tfor (const block of category.blocks) {\n\t\t\tconst item: RegistryItem = {\n\t\t\t\tname: block.name,\n\t\t\t\tadd: block.list ? 'when-added' : 'when-needed',\n\t\t\t\ttype: category.name,\n\t\t\t\tfiles: block.subdirectory\n\t\t\t\t\t? [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpath: block.directory,\n\t\t\t\t\t\t\t\tfiles: block.files.map((file) => ({\n\t\t\t\t\t\t\t\t\tpath: file,\n\t\t\t\t\t\t\t\t\trole: getFileRole(file),\n\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t: block.files.map((file) => ({\n\t\t\t\t\t\t\tpath: path.join(block.directory, file),\n\t\t\t\t\t\t\trole: getFileRole(file),\n\t\t\t\t\t\t})),\n\t\t\t};\n\n\t\t\titems.push(item);\n\t\t}\n\t}\n\n\tfor (const configFile of manifest.configFiles ?? []) {\n\t\tconst item: RegistryItem = {\n\t\t\tname: configFile.name,\n\t\t\ttitle: configFile.name,\n\t\t\tadd: configFile.optional ? 'optionally-on-init' : 'on-init',\n\t\t\ttype: 'config',\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tpath: configFile.path,\n\t\t\t\t\ttarget: configFile.expectedPath,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\n\t\titems.push(item);\n\t}\n\n\treturn ok({\n\t\tname: manifest.name,\n\t\tdescription: manifest.meta?.description,\n\t\thomepage: manifest.meta?.homepage,\n\t\tauthors: manifest.meta?.authors,\n\t\tbugs: manifest.meta?.bugs,\n\t\trepository: manifest.meta?.repository,\n\t\ttags: manifest.meta?.tags,\n\t\tversion: manifest.version,\n\t\taccess: manifest.access,\n\t\tdefaultPaths: manifest.defaultPaths,\n\t\texcludeDeps: oldConfig.excludeDeps,\n\t\titems,\n\t});\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/errors.ts",
    "content": "import pc from 'picocolors';\nimport type { z } from 'zod';\n\nexport type CLIError =\n\t| JsrepoError\n\t| NoPackageJsonFoundError\n\t| InvalidOptionsError\n\t| InvalidJSONError\n\t| ManifestNotFoundError;\n\nexport class JsrepoError extends Error {\n\tprivate readonly suggestion: string;\n\tprivate readonly docsLink?: string;\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tsuggestion: string;\n\t\t\tdocsLink?: string;\n\t\t}\n\t) {\n\t\tsuper(message);\n\n\t\tthis.suggestion = options.suggestion;\n\t\tthis.docsLink = options.docsLink;\n\t}\n\n\ttoString() {\n\t\treturn `${this.message} ${this.suggestion}${pc.gray(this.docsLink ? `\\n   See: ${this.docsLink}` : '')}`;\n\t}\n}\n\nexport class NoPackageJsonFoundError extends JsrepoError {\n\tconstructor() {\n\t\tsuper('No package.json found.', {\n\t\t\tsuggestion:\n\t\t\t\t'Please run create a package.json first before initializing a jsrepo project.',\n\t\t});\n\t}\n}\n\nexport class ManifestNotFoundError extends JsrepoError {\n\tconstructor({ path }: { path: string }) {\n\t\tsuper(`Manifest not found at ${pc.bold(path)}.`, {\n\t\t\tsuggestion:\n\t\t\t\t'This is a bug, please report it here: https://github.com/jsrepojs/jsrepo/issues',\n\t\t});\n\t}\n}\n\nexport class InvalidOptionsError extends JsrepoError {\n\tconstructor(error: z.ZodError) {\n\t\tsuper(\n\t\t\t`Invalid options: ${error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`).join(', ')}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Please check the options and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n\nexport class ZodError extends JsrepoError {\n\treadonly zodError: z.ZodError;\n\tconstructor(error: z.ZodError) {\n\t\tsuper(`Zod error: ${error.message}`, {\n\t\t\tsuggestion: 'Check the input schema and try again.',\n\t\t});\n\t\tthis.zodError = error;\n\t}\n}\n\nexport class InvalidJSONError extends JsrepoError {\n\tconstructor(error: unknown) {\n\t\tsuper(\n\t\t\t`Invalid JSON: ${error instanceof Error ? (error.stack ?? error.message) : `${error}`}`,\n\t\t\t{\n\t\t\t\tsuggestion: 'Check the input JSON and try again.',\n\t\t\t}\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/fs.ts",
    "content": "import fs from 'node:fs';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport { JsrepoError } from './errors';\nimport { dirname } from './path';\nimport type { AbsolutePath } from './types';\n\nexport function readFileSync(p: AbsolutePath): Result<string, JsrepoError> {\n\ttry {\n\t\treturn ok(fs.readFileSync(p, 'utf-8'));\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to read file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\n/**\n * Write to a file. Automatically creates the directory recursively if it doesn't exist.\n *\n * @param p\n * @param data\n * @returns\n */\nexport function writeFileSync(p: AbsolutePath, data: string): Result<void, JsrepoError> {\n\ttry {\n\t\tconst res = mkdirSync(dirname(p) as AbsolutePath);\n\t\tif (res.isErr()) return err(res.error);\n\t\tfs.writeFileSync(p, data);\n\t\treturn ok();\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to write file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nexport function mkdirSync(p: AbsolutePath): Result<void, JsrepoError> {\n\ttry {\n\t\tfs.mkdirSync(p, { recursive: true });\n\t\treturn ok();\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to create directory ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\n/**\n * Removes a file if it exists.\n *\n * @param p\n * @returns\n */\nexport function rmSync(p: AbsolutePath): Result<void, JsrepoError> {\n\ttry {\n\t\tif (!existsSync(p)) return ok();\n\t\tfs.rmSync(p);\n\t\treturn ok();\n\t} catch (e) {\n\t\treturn err(\n\t\t\tnew JsrepoError(\n\t\t\t\t`Failed to remove file ${p}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t\t\t{\n\t\t\t\t\tsuggestion: 'Please try again.',\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t}\n}\n\nexport function existsSync(p: AbsolutePath): boolean {\n\treturn fs.existsSync(p);\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/json.ts",
    "content": "export function stringify(data: unknown, options: { format?: boolean } = {}): string {\n\treturn JSON.stringify(data, null, options.format ? '\\t' : undefined);\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/package.ts",
    "content": "import os from 'node:os';\nimport { err, ok, type Result } from 'nevereverthrow';\nimport path from 'pathe';\nimport semver from 'semver';\nimport { existsSync, readFileSync } from './fs';\nimport { parsePackageName } from './parse-package-name';\nimport { dirname, joinAbsolute } from './path';\nimport type { AbsolutePath } from './types';\n\nexport function findNearestPackageJson(\n\tcwd: AbsolutePath\n): { path: AbsolutePath; package: Partial<PackageJson> } | undefined {\n\tif (cwd === os.homedir() || cwd === path.parse(cwd).root) return undefined;\n\n\tconst packagePath = joinAbsolute(cwd, 'package.json');\n\tif (existsSync(packagePath))\n\t\treturn {\n\t\t\tpath: packagePath,\n\t\t\tpackage: getPackage(packagePath),\n\t\t};\n\n\treturn findNearestPackageJson(dirname(cwd));\n}\n\nexport function tryGetPackage(path: AbsolutePath): Result<Partial<PackageJson>, string> {\n\ttry {\n\t\treturn ok(getPackage(path));\n\t} catch (error) {\n\t\treturn err(`Error while trying to get package.json at ${path}: ${error}`);\n\t}\n}\n\nfunction getPackage(path: AbsolutePath): Partial<PackageJson> {\n\treturn JSON.parse(readFileSync(path)._unsafeUnwrap().toString());\n}\n\nexport type PackageJson = {\n\tname: string;\n\tversion: string;\n\tdescription: string;\n\tscripts: Record<string, string>;\n\tdependencies: Record<string, string>;\n\tdevDependencies: Record<string, string>;\n\ttype: string;\n\t// rest props\n\t[key: string]: unknown;\n};\n\nexport function cleanVersion(version: string) {\n\tif (version[0] === '^') {\n\t\treturn version.slice(1);\n\t}\n\n\treturn version;\n}\n\n/** Returns only the dependencies that should be installed based on what is already in the package.json */\nexport function shouldInstall(\n\tdependencies: { dependencies: string[]; devDependencies: string[] },\n\t{ pkg }: { pkg: Partial<PackageJson> }\n): { dependencies: string[]; devDependencies: string[] } {\n\tfunction shouldShouldInstallDependency(\n\t\tdep: { name: string; version: string | undefined },\n\t\tpkgDeps: Record<string, string> | undefined\n\t): boolean {\n\t\tconst foundDep = pkgDeps?.[dep.name];\n\n\t\t// if version isn't pinned and dep exists delete\n\t\tif (dep.version === undefined && foundDep) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if the version installed satisfies the requested version remove the dep\n\t\tif (\n\t\t\tfoundDep &&\n\t\t\t(dep.version === undefined || semver.satisfies(cleanVersion(foundDep), dep.version))\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tconst deps = new Set<string>();\n\tfor (const dep of dependencies.dependencies) {\n\t\tconst parsed = parsePackageName(dep)._unsafeUnwrap();\n\t\tif (!shouldShouldInstallDependency(parsed, pkg.dependencies)) continue;\n\n\t\tdeps.add(dep);\n\t}\n\tconst devDeps = new Set<string>();\n\tfor (const dep of dependencies.devDependencies) {\n\t\tconst parsed = parsePackageName(dep)._unsafeUnwrap();\n\t\tif (!shouldShouldInstallDependency(parsed, pkg.devDependencies)) continue;\n\n\t\tdevDeps.add(dep);\n\t}\n\treturn {\n\t\tdependencies: Array.from(deps.values()),\n\t\tdevDependencies: Array.from(devDeps.values()),\n\t};\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/parse-package-name.ts",
    "content": "/**\n * Adapted from https://github.com/egoist/parse-package-name/blob/main/src/index.ts\n * @module\n */\n\nimport { err, ok } from 'nevereverthrow';\n\n// Parsed a scoped package name into name, version, and path.\nconst RE_SCOPED = /^(@[^/]+\\/[^@/]+)(?:@([^/]+))?(\\/.*)?$/;\n// Parsed a non-scoped package name into name, version, path\nconst RE_NON_SCOPED = /^([^@/]+)(?:@([^/]+))?(\\/.*)?$/;\n\nexport type Package = {\n\t/** Name of the package as it would be installed from npm */\n\tname: string;\n\t/** Version of the package */\n\tversion: string;\n\tpath: string;\n};\n\nexport function parsePackageName(input: string) {\n\tconst m = RE_SCOPED.exec(input) || RE_NON_SCOPED.exec(input);\n\n\tif (!m) return err(`invalid package name: ${input}`);\n\n\treturn ok({\n\t\tname: m[1] || '',\n\t\tversion: m[2] || undefined,\n\t\tpath: m[3] || '',\n\t});\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/path.ts",
    "content": "import path from 'pathe';\nimport type { AbsolutePath } from './types';\n\n/**\n * Join all arguments together and normalize the resulting path.\n *\n * @param p\n * @param paths\n * @returns\n */\nexport function joinAbsolute(p: AbsolutePath, ...paths: string[]): AbsolutePath {\n\treturn path.join(p, ...paths) as AbsolutePath;\n}\n\n/**\n * Return the directory name of a path. Similar to the Unix dirname command.\n *\n * @param path the path to evaluate.\n * @throws {TypeError} if path is not a string.\n * @returns\n */\nexport function dirname(p: AbsolutePath): AbsolutePath {\n\treturn path.dirname(p) as AbsolutePath;\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/prompts.ts",
    "content": "import { intro as _intro, taskLog } from '@clack/prompts';\nimport { detectPackageManager } from 'jsrepo/utils';\nimport { type ResolvedCommand, resolveCommand } from 'package-manager-detector';\nimport pc from 'picocolors';\nimport { x } from 'tinyexec';\nimport pkg from '@/../package.json';\nimport { findNearestPackageJson, shouldInstall } from '@/utils/package';\nimport type { AbsolutePath } from '@/utils/types';\n\nexport function intro() {\n\tconsole.clear();\n\n\t_intro(`${pc.bgYellow(pc.black(` ${pkg.name} `))}${pc.gray(` v${pkg.version} `)}`);\n}\n\nexport { outro } from '@clack/prompts';\n\nexport async function runCommands({\n\ttitle,\n\tcommands,\n\tcwd,\n\tmessages,\n}: {\n\ttitle: string;\n\tcommands: ResolvedCommand[];\n\tcwd: string;\n\tmessages: {\n\t\tsuccess: () => string;\n\t\terror: (err: unknown) => string;\n\t};\n}) {\n\tconst task = taskLog({\n\t\ttitle,\n\t\tlimit: Math.ceil(process.stdout.rows / 2),\n\t\tspacing: 0,\n\t\tretainLog: true,\n\t});\n\n\tconst runCmd = async (cmd: ResolvedCommand) => {\n\t\tconst proc = x(cmd.command, [...cmd.args], { nodeOptions: { cwd } });\n\n\t\tfor await (const line of proc) {\n\t\t\ttask.message(line);\n\t\t}\n\t};\n\n\ttry {\n\t\tfor (const command of commands) {\n\t\t\tawait runCmd(command);\n\t\t}\n\n\t\ttask.success(messages.success());\n\t} catch (err) {\n\t\ttask.error(messages.error(err));\n\t\tprocess.exit(1);\n\t}\n}\n\nexport async function installDependencies(\n\tdependencies: { dependencies: string[]; devDependencies: string[] },\n\t{ cwd }: { cwd: AbsolutePath }\n): Promise<void> {\n\tconst packageResult = findNearestPackageJson(cwd);\n\tif (!packageResult) return;\n\tconst pm = await detectPackageManager(cwd);\n\n\t// this is only if no dependencies were provided\n\tif (dependencies.dependencies.length === 0 && dependencies.devDependencies.length === 0) {\n\t\tconst installCmd = resolveCommand(pm, 'install', []);\n\n\t\tif (installCmd === null) return;\n\n\t\tawait runCommands({\n\t\t\ttitle: `Installing dependencies with ${pm}...`,\n\t\t\tcommands: [installCmd],\n\t\t\tcwd,\n\t\t\tmessages: {\n\t\t\t\tsuccess: () => `Installed dependencies`,\n\t\t\t\terror: (err) =>\n\t\t\t\t\t`Failed to install dependencies: ${err instanceof Error ? err.message : err}`,\n\t\t\t},\n\t\t});\n\t\treturn;\n\t}\n\n\tconst { dependencies: deps, devDependencies: devDeps } = shouldInstall(dependencies, {\n\t\tpkg: packageResult.package,\n\t});\n\n\tif (deps.length === 0 && devDeps.length === 0) return;\n\n\tconst add = resolveCommand(pm, 'add', [...deps]);\n\tconst addDev = resolveCommand(pm, 'add', ['-D', ...devDeps]);\n\n\tawait runCommands({\n\t\ttitle: `Installing dependencies with ${pm}...`,\n\t\tcommands: [\n\t\t\t...(add && deps.length > 0 ? [add] : []),\n\t\t\t...(addDev && devDeps.length > 0 ? [addDev] : []),\n\t\t],\n\t\tcwd,\n\t\tmessages: {\n\t\t\tsuccess: () => `Installed ${pc.cyan([...deps, ...devDeps].join(', '))}`,\n\t\t\terror: (err) =>\n\t\t\t\t`Failed to install dependencies: ${err instanceof Error ? err.message : err}`,\n\t\t},\n\t});\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/strings.ts",
    "content": "/** Returns the matched suffix for the string (if it exists). Great for matching string union types.\n *\n * @param str\n * @param strings\n * @returns\n *\n * ## Usage\n * ```ts\n * endsWithOneOf('cb', 'a', 'b'); // 'b'\n * endsWithOneOf('cc', 'a', 'b'); // undefined\n * ```\n */\nexport function endsWithOneOf<T extends string>(str: string, strings: readonly T[]): T | undefined {\n\tfor (const s of strings) {\n\t\tif (str.endsWith(s)) return s;\n\t}\n\n\treturn undefined;\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/types.ts",
    "content": "export type { AbsolutePath } from 'jsrepo/utils';\n"
  },
  {
    "path": "packages/migrate/src/utils/v2/config.ts",
    "content": "import { z } from 'zod';\nimport { endsWithOneOf } from '../strings';\n\nexport const REGISTRY_CONFIG_FILE_V2 = 'jsrepo-build-config.json';\nexport const MANIFEST_FILE_V2 = 'jsrepo-manifest.json';\nexport const PROJECT_CONFIG_FILE_V2 = 'jsrepo.json';\n\nexport const ManifestMetaSchemaV2 = z.object({\n\tauthors: z.array(z.string()).optional(),\n\tbugs: z.string().optional(),\n\tdescription: z.string().optional(),\n\thomepage: z.string().optional(),\n\trepository: z.string().optional(),\n\ttags: z.array(z.string()).optional(),\n});\n\nexport const AccessLevelSchemaV2 = z.union([\n\tz.literal('public'),\n\tz.literal('private'),\n\tz.literal('marketplace'),\n]);\n\nexport const ConfigFileSchemaV2 = z.object({\n\tname: z.string(),\n\tpath: z.string(),\n\texpectedPath: z.string().optional(),\n\toptional: z.boolean().optional().default(false),\n});\n\nconst RuleConfigSchemaV2 = z.record(\n\tz.string(),\n\tz.union([\n\t\tz.string(),\n\t\tz.tuple([z.string(), z.union([z.string(), z.number()])], z.union([z.string(), z.number()])),\n\t])\n);\n\nexport const PeerDependencySchemaV2 = z.record(\n\tz.string(),\n\tz.union([z.string(), z.object({ version: z.string(), message: z.string() })])\n);\n\nexport const RegistryConfigSchemaV2 = z.object({\n\t$schema: z.string(),\n\tname: z.string().optional(),\n\tversion: z.string().optional(),\n\treadme: z.string().optional(),\n\taccess: AccessLevelSchemaV2.optional(),\n\tmeta: ManifestMetaSchemaV2.optional(),\n\tdefaultPaths: z.record(z.string(), z.string()).optional(),\n\tpeerDependencies: PeerDependencySchemaV2.optional(),\n\tconfigFiles: z.array(ConfigFileSchemaV2).optional(),\n\tdirs: z.array(z.string()),\n\toutputDir: z.string().optional(),\n\tincludeBlocks: z.array(z.string()).optional().default([]),\n\tincludeCategories: z.array(z.string()).optional().default([]),\n\tincludeFiles: z.array(z.string()).optional().default([]),\n\texcludeBlocks: z.array(z.string()).optional().default([]),\n\texcludeCategories: z.array(z.string()).optional().default([]),\n\tdoNotListBlocks: z.array(z.string()).optional().default([]),\n\tdoNotListCategories: z.array(z.string()).optional().default([]),\n\tlistBlocks: z.array(z.string()).optional().default([]),\n\tlistCategories: z.array(z.string()).optional().default([]),\n\texcludeDeps: z.array(z.string()).optional().default([]),\n\tallowSubdirectories: z.boolean().optional(),\n\tpreview: z.boolean().optional(),\n\tincludeDocs: z.boolean().optional().default(false),\n\trules: RuleConfigSchemaV2.optional(),\n});\n\nexport type RegistryConfigV2 = z.infer<typeof RegistryConfigSchemaV2>;\n\nexport const ManifestConfigFileSchemaV2 = ConfigFileSchemaV2.extend({\n\tdependencies: z.array(z.string()).optional(),\n\tdevDependencies: z.array(z.string()).optional(),\n});\n\nexport const BlockSchemaV2 = z.object({\n\tname: z.string(),\n\tcategory: z.string(),\n\tlocalDependencies: z.array(z.string()),\n\tdependencies: z.array(z.string()),\n\tdevDependencies: z.array(z.string()),\n\ttests: z.boolean(),\n\tdocs: z.boolean().optional().default(false),\n\tlist: z.boolean().optional().default(true),\n\t/** Where to find the block relative to root */\n\tdirectory: z.string(),\n\tsubdirectory: z.boolean(),\n\tfiles: z.array(z.string()),\n\t_imports_: z.record(z.string(), z.string()),\n});\n\nexport const CategorySchemaV2 = z.object({\n\tname: z.string(),\n\tblocks: z.array(BlockSchemaV2),\n});\n\nexport const ManifestSchemaV2 = z.object({\n\tname: z.string().optional(),\n\tversion: z.string().optional(),\n\tmeta: ManifestMetaSchemaV2.optional(),\n\taccess: AccessLevelSchemaV2.optional(),\n\tdefaultPaths: z.record(z.string(), z.string()).optional(),\n\tconfigFiles: z.array(ManifestConfigFileSchemaV2).optional(),\n\tcategories: z.array(CategorySchemaV2),\n});\n\nexport const FormatterSchemaV2 = z.union([z.literal('prettier'), z.literal('biome')]);\n\nexport const ProjectConfigSchemaV2 = z.object({\n\t$schema: z.string(),\n\trepos: z.array(z.string()).optional().default([]),\n\tincludeTests: z.boolean().optional().default(false),\n\tincludeDocs: z.boolean().optional().default(false),\n\tpaths: z.record(z.string(), z.string()).optional(),\n\tconfigFiles: z.record(z.string(), z.string()).optional(),\n\twatermark: z.optional(z.boolean()).default(true),\n\tformatter: z.optional(FormatterSchemaV2),\n});\n\nexport type ProjectConfigV2 = z.infer<typeof ProjectConfigSchemaV2>;\n\nconst TEST_SUFFIXES = [\n\t'.test.ts',\n\t'_test.ts',\n\t'.test.js',\n\t'_test.js',\n\t'.spec.ts',\n\t'_spec.ts',\n\t'.spec.js',\n\t'_spec.js',\n\t'.stories.jsx',\n\t'_stories.jsx',\n\t'.stories.tsx',\n\t'_stories.tsx',\n] as const;\n\nconst DOCS_SUFFIXES = ['.mdx', '.md'] as const;\n\nexport function isTestFile(file: string): boolean {\n\treturn endsWithOneOf(file, TEST_SUFFIXES) !== undefined;\n}\n\nexport function isDocsFile(file: string): boolean {\n\treturn endsWithOneOf(file, DOCS_SUFFIXES) !== undefined;\n}\n"
  },
  {
    "path": "packages/migrate/src/utils/zod.ts",
    "content": "import { err, ok, type Result } from 'nevereverthrow';\nimport type { z } from 'zod';\nimport { InvalidJSONError, ZodError } from '@/utils/errors';\n\nexport function safeValidate<T>(schema: z.ZodSchema<T>, data: unknown): Result<T, ZodError> {\n\tconst parsed = schema.safeParse(data);\n\tif (parsed.success) {\n\t\treturn ok(parsed.data);\n\t}\n\treturn err(new ZodError(parsed.error));\n}\n\nexport function safeParseFromJSON<T>(\n\tschema: z.ZodSchema<T>,\n\tdata: string\n): Result<T, InvalidJSONError | ZodError> {\n\ttry {\n\t\tconst parsed = schema.safeParse(JSON.parse(data));\n\t\tif (parsed.success) {\n\t\t\treturn ok(parsed.data);\n\t\t}\n\t\treturn err(new ZodError(parsed.error));\n\t} catch (error) {\n\t\treturn err(new InvalidJSONError(error));\n\t}\n}\n"
  },
  {
    "path": "packages/migrate/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true,\n\t\t\"paths\": {\n\t\t\t\"@/*\": [\"./src/*\"]\n\t\t}\n\t},\n\t\"include\": [\"src/**/*.ts\", \"tests/**/*.ts\"],\n\t\"exclude\": [\"tests/fixtures/**/*\"]\n}\n"
  },
  {
    "path": "packages/migrate/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/bin.ts'],\n\tformat: ['esm'],\n\talias: {\n\t\t'@/': './src/',\n\t},\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/pnpm/CHANGELOG.md",
    "content": "# @jsrepo/pnpm\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 1.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 1.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 1.0.0\n### Patch Changes\n\n\n- chore: setup trusted publish ([#775](https://github.com/jsrepojs/jsrepo/pull/775))\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n"
  },
  {
    "path": "packages/pnpm/README.md",
    "content": "# @jsrepo/pnpm\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/pnpm)](https://npmjs.com/package/@jsrepo/pnpm)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/pnpm)](https://npmjs.com/package/@jsrepo/pnpm)\n\nA remote dependency resolver for **jsrepo** that resolves pnpm `workspace:` and `catalog:` protocol versions to concrete semver strings during registry builds.\n\n## Usage\n\nInstall the package:\n\n```sh\npnpm install @jsrepo/pnpm -D\n```\n\nAdd the resolver to your jsrepo config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport { pnpm } from \"@jsrepo/pnpm\";\n\nexport default defineConfig({\n  build: {\n    remoteDependencyResolver: pnpm(),\n  },\n});\n```\n\nNow your `workspace:` and `catalog:` versions will be resolved to concrete versions when building your registry.\n"
  },
  {
    "path": "packages/pnpm/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/pnpm\",\n\t\"description\": \"Remote dependency resolver for pnpm workspace and catalog protocols.\",\n\t\"version\": \"2.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"pnpm\",\n\t\t\"workspace\",\n\t\t\"catalog\",\n\t\t\"resolver\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"test\": \"vitest\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"dependencies\": {\n\t\t\"fast-glob\": \"^3.3.3\",\n\t\t\"pathe\": \"^2.0.3\",\n\t\t\"yaml\": \"^2.7.0\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"vitest\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/pnpm/src/catalog.ts",
    "content": "import fs, { existsSync } from 'node:fs';\nimport { join } from 'pathe';\nimport { parse as parseYaml } from 'yaml';\nimport { buildWorkspacePackageMap, findPnpmWorkspaceRoot } from './workspace.js';\n\nexport type PnpmWorkspaceConfig = {\n\tpackages?: string[];\n\tcatalog?: Record<string, string>;\n\tcatalogs?: Record<string, Record<string, string>>;\n};\n\nexport type ParsedState = {\n\tworkspaceRoot: string;\n\tworkspacePackages: Map<string, string>;\n\tcatalogDefault: Map<string, string>;\n\tcatalogsNamed: Map<string, Map<string, string>>;\n};\n\n/**\n * Parse pnpm-workspace.yaml and return packages array, catalog, and catalogs.\n */\nexport function parsePnpmWorkspaceYaml(workspaceRoot: string): PnpmWorkspaceConfig {\n\tconst filePath = join(workspaceRoot, 'pnpm-workspace.yaml');\n\tif (!existsSync(filePath)) {\n\t\tthrow new Error(`pnpm-workspace.yaml not found at ${filePath}`);\n\t}\n\tconst content = fs.readFileSync(filePath, 'utf-8');\n\tconst parsed = parseYaml(content) as PnpmWorkspaceConfig;\n\treturn parsed ?? {};\n}\n\n/**\n * Build catalog lookup: default catalog (catalog:) and named catalogs (catalog:name).\n */\nfunction buildCatalogMaps(config: PnpmWorkspaceConfig): {\n\tcatalogDefault: Map<string, string>;\n\tcatalogsNamed: Map<string, Map<string, string>>;\n} {\n\tconst catalogDefault = new Map<string, string>();\n\tconst catalogsNamed = new Map<string, Map<string, string>>();\n\n\tif (config.catalog && typeof config.catalog === 'object') {\n\t\tfor (const [name, version] of Object.entries(config.catalog)) {\n\t\t\tif (typeof version === 'string') {\n\t\t\t\tcatalogDefault.set(name, version);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (config.catalogs && typeof config.catalogs === 'object') {\n\t\tfor (const [catalogName, entries] of Object.entries(config.catalogs)) {\n\t\t\tif (entries && typeof entries === 'object') {\n\t\t\t\tconst map = new Map<string, string>();\n\t\t\t\tfor (const [pkgName, version] of Object.entries(entries)) {\n\t\t\t\t\tif (typeof version === 'string') {\n\t\t\t\t\t\tmap.set(pkgName, version);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatalogsNamed.set(catalogName, map);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { catalogDefault, catalogsNamed };\n}\n\n/**\n * Parse workspace and catalog state for a given cwd.\n */\nexport function parsePnpmState(cwd: string): ParsedState {\n\tconst workspaceRoot = findPnpmWorkspaceRoot(cwd);\n\tif (!workspaceRoot) {\n\t\tthrow new Error(`Could not find pnpm-workspace.yaml by walking up from ${cwd}`);\n\t}\n\n\tconst config = parsePnpmWorkspaceYaml(workspaceRoot);\n\tconst packages = config.packages ?? [];\n\tconst workspacePackages = buildWorkspacePackageMap(workspaceRoot, packages);\n\tconst { catalogDefault, catalogsNamed } = buildCatalogMaps(config);\n\n\treturn {\n\t\tworkspaceRoot,\n\t\tworkspacePackages,\n\t\tcatalogDefault,\n\t\tcatalogsNamed,\n\t};\n}\n"
  },
  {
    "path": "packages/pnpm/src/index.ts",
    "content": "import fs, { existsSync } from 'node:fs';\nimport type {\n\tRemoteDependency,\n\tRemoteDependencyResolver,\n\tRemoteDependencyResolverOptions,\n} from 'jsrepo/config';\nimport { join } from 'pathe';\nimport { type ParsedState, parsePnpmState } from './catalog.js';\n\n/**\n * Resolve `workspace:` and `catalog:` versions to concrete semver strings.\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import { pnpm } from \"@jsrepo/pnpm\";\n *\n * export default defineConfig({\n *   // ...\n *   build: {\n *     remoteDependencyResolver: pnpm(),\n *   },\n * });\n * ```\n */\nexport function pnpm(): RemoteDependencyResolver {\n\treturn async (\n\t\tdep: RemoteDependency,\n\t\toptions: RemoteDependencyResolverOptions\n\t): Promise<RemoteDependency> => {\n\t\tconst version = dep.version;\n\t\tif (!version) return dep;\n\n\t\tconst workspaceParsed = parseWorkspaceVersion(version);\n\t\tif (workspaceParsed) {\n\t\t\tconst state = getOrParseState(options.cwd);\n\n\t\t\tif (workspaceParsed.type === 'path') {\n\t\t\t\t// workspace:../path - resolve from workspace root\n\t\t\t\tconst resolvedVersion = resolveWorkspacePath(state, workspaceParsed.path);\n\t\t\t\tif (resolvedVersion) {\n\t\t\t\t\treturn { ...dep, version: resolvedVersion };\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Could not resolve workspace path ${workspaceParsed.path} for ${dep.name}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// workspace:packageName@versionSpec or workspace:* (packageName from dep.name)\n\t\t\tconst packageName = workspaceParsed.packageName || dep.name;\n\t\t\tconst versionSpec = workspaceParsed.versionSpec;\n\t\t\tconst workspaceVersion = state.workspacePackages.get(packageName);\n\n\t\t\tif (!workspaceVersion) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Workspace package \"${packageName}\" not found. Available: ${[\n\t\t\t\t\t\t...state.workspacePackages.keys(),\n\t\t\t\t\t].join(', ')}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst resolvedVersion = resolveWorkspaceVersion(versionSpec, workspaceVersion);\n\n\t\t\t// workspace:foo@* means alias: dep.name is the alias, we output npm:foo@version\n\t\t\tif (workspaceParsed.packageName && workspaceParsed.packageName !== dep.name) {\n\t\t\t\treturn {\n\t\t\t\t\t...dep,\n\t\t\t\t\tversion: `npm:${packageName}@${resolvedVersion}`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn { ...dep, version: resolvedVersion };\n\t\t}\n\n\t\tconst catalogParsed = parseCatalogVersion(version);\n\t\tif (catalogParsed) {\n\t\t\tconst state = getOrParseState(options.cwd);\n\n\t\t\tlet catalogMap: Map<string, string>;\n\t\t\tif (catalogParsed.catalogName === 'default') {\n\t\t\t\tcatalogMap = state.catalogDefault;\n\t\t\t} else {\n\t\t\t\tconst named = state.catalogsNamed.get(catalogParsed.catalogName);\n\t\t\t\tif (!named) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Catalog \"${catalogParsed.catalogName}\" not found. Available: default, ${[\n\t\t\t\t\t\t\t...state.catalogsNamed.keys(),\n\t\t\t\t\t\t].join(', ')}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcatalogMap = named;\n\t\t\t}\n\n\t\t\tconst catalogVersion = catalogMap.get(dep.name);\n\t\t\tif (!catalogVersion) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Package \"${dep.name}\" not found in catalog \"${catalogParsed.catalogName}\". Available: ${[\n\t\t\t\t\t\t...catalogMap.keys(),\n\t\t\t\t\t].join(', ')}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn { ...dep, version: catalogVersion };\n\t\t}\n\n\t\treturn dep;\n\t};\n}\n\nconst stateCache = new Map<string, ParsedState>();\n\nfunction getOrParseState(cwd: string): ParsedState {\n\tlet state = stateCache.get(cwd);\n\tif (!state) {\n\t\tstate = parsePnpmState(cwd);\n\t\tstateCache.set(cwd, state);\n\t}\n\treturn state;\n}\n\nfunction resolveWorkspaceVersion(versionSpec: string, workspaceVersion: string): string {\n\tif (versionSpec === '*' || versionSpec === '' || !versionSpec) {\n\t\treturn workspaceVersion;\n\t}\n\tif (versionSpec === '^') {\n\t\treturn `^${workspaceVersion}`;\n\t}\n\tif (versionSpec === '~') {\n\t\treturn `~${workspaceVersion}`;\n\t}\n\t// workspace:1.0.0 or similar - use literal\n\treturn versionSpec;\n}\n\n/**\n * Parse workspace: protocol value. Returns { type, packageName?, versionSpec?, path? }\n */\nfunction parseWorkspaceVersion(version: string):\n\t| {\n\t\t\ttype: 'workspace';\n\t\t\tpackageName: string;\n\t\t\tversionSpec: string;\n\t  }\n\t| {\n\t\t\ttype: 'path';\n\t\t\tpath: string;\n\t  }\n\t| null {\n\tif (!version.startsWith('workspace:')) return null;\n\tconst rest = version.slice('workspace:'.length);\n\n\t// workspace:../foo or workspace:./packages/bar or workspace:packages/pkg-a (path with /)\n\tif (\n\t\trest.startsWith('.') ||\n\t\trest.startsWith('/') ||\n\t\t(rest.includes('/') && !rest.includes('@'))\n\t) {\n\t\treturn { type: 'path', path: rest };\n\t}\n\n\t// workspace:foo@* or workspace:foo@^ etc - alias\n\tconst atIdx = rest.lastIndexOf('@');\n\tif (atIdx > 0) {\n\t\tconst packageName = rest.slice(0, atIdx);\n\t\tconst versionSpec = rest.slice(atIdx + 1) || '*';\n\t\treturn { type: 'workspace', packageName, versionSpec };\n\t}\n\n\t// workspace:* or workspace:^ or workspace:~ or workspace:1.0.0\n\treturn { type: 'workspace', packageName: '', versionSpec: rest || '*' };\n}\n\nfunction parseCatalogVersion(version: string): { catalogName: string } | null {\n\tif (!version.startsWith('catalog:')) return null;\n\tconst name = version.slice('catalog:'.length).trim();\n\t// catalog: or catalog:default -> default\n\treturn { catalogName: name === '' ? 'default' : name };\n}\n\nfunction resolveWorkspacePath(state: ParsedState, pathSpec: string): string | null {\n\tconst targetDir = join(state.workspaceRoot, pathSpec);\n\tconst pkgPath = join(targetDir, 'package.json');\n\tif (!existsSync(pkgPath)) return null;\n\ttry {\n\t\tconst content = fs.readFileSync(pkgPath, 'utf-8');\n\t\tconst pkg = JSON.parse(content) as { version?: string };\n\t\treturn typeof pkg.version === 'string' ? pkg.version : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "packages/pnpm/src/workspace.ts",
    "content": "import fs, { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport fg from 'fast-glob';\nimport { dirname, join, resolve } from 'pathe';\n\nexport type WorkspacePackage = {\n\tname: string;\n\tversion: string;\n};\n\n/**\n * Walk up from cwd to find the directory containing pnpm-workspace.yaml.\n */\nexport function findPnpmWorkspaceRoot(cwd: string): string | null {\n\tlet dir = resolve(cwd);\n\tconst root = path.parse(dir).root;\n\n\twhile (dir !== root) {\n\t\tconst workspaceFile = join(dir, 'pnpm-workspace.yaml');\n\t\tif (existsSync(workspaceFile)) {\n\t\t\treturn dir;\n\t\t}\n\t\tdir = dirname(dir);\n\t}\n\n\treturn null;\n}\n\n/**\n * Expand workspace package globs (e.g. \"packages/*\") to absolute directory paths.\n */\nexport function expandWorkspacePackages(workspaceRoot: string, packages: string[]): string[] {\n\tconst dirs: string[] = [];\n\tfor (const pattern of packages) {\n\t\tconst matches = fg.sync(pattern, {\n\t\t\tcwd: workspaceRoot,\n\t\t\tonlyDirectories: true,\n\t\t\tabsolute: true,\n\t\t});\n\t\tdirs.push(...matches);\n\t}\n\treturn [...new Set(dirs)];\n}\n\n/**\n * Read package.json and return name and version. Returns null if invalid.\n */\nfunction readPackageInfo(dir: string): WorkspacePackage | null {\n\tconst pkgPath = join(dir, 'package.json');\n\tif (!existsSync(pkgPath)) return null;\n\ttry {\n\t\tconst content = fs.readFileSync(pkgPath, 'utf-8');\n\t\tconst pkg = JSON.parse(content) as { name?: string; version?: string };\n\t\tif (typeof pkg.name === 'string' && typeof pkg.version === 'string') {\n\t\t\treturn { name: pkg.name, version: pkg.version };\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Build a map of package name -> version from workspace packages.\n */\nexport function buildWorkspacePackageMap(\n\tworkspaceRoot: string,\n\tpackages: string[]\n): Map<string, string> {\n\tconst map = new Map<string, string>();\n\tconst dirs = expandWorkspacePackages(workspaceRoot, packages);\n\n\tfor (const dir of dirs) {\n\t\tconst info = readPackageInfo(dir);\n\t\tif (info) {\n\t\t\tmap.set(info.name, info.version);\n\t\t}\n\t}\n\n\treturn map;\n}\n"
  },
  {
    "path": "packages/pnpm/tests/fixtures/pnpm-workspace/packages/pkg-a/package.json",
    "content": "{\n\t\"name\": \"pkg-a\",\n\t\"version\": \"1.2.3\"\n}\n"
  },
  {
    "path": "packages/pnpm/tests/fixtures/pnpm-workspace/packages/pkg-b/package.json",
    "content": "{\n\t\"name\": \"pkg-b\",\n\t\"version\": \"2.0.0\"\n}\n"
  },
  {
    "path": "packages/pnpm/tests/fixtures/pnpm-workspace/packages/pkg-c/package.json",
    "content": "{\n\t\"name\": \"pkg-c\",\n\t\"version\": \"3.0.0\"\n}\n"
  },
  {
    "path": "packages/pnpm/tests/fixtures/pnpm-workspace/pnpm-workspace.yaml",
    "content": "packages:\n  - \"packages/*\"\n\ncatalog:\n  lodash: \"^4.17.21\"\n\ncatalogs:\n  deps:\n    react: \"^18.0.0\"\n"
  },
  {
    "path": "packages/pnpm/tests/pnpm.test.ts",
    "content": "import type { AbsolutePath } from 'jsrepo/utils';\nimport path from 'pathe';\nimport { describe, expect, it } from 'vitest';\nimport { pnpm } from '../src/index.js';\n\nconst fixturesDir = path.join(\n\tpath.dirname(new URL(import.meta.url).pathname),\n\t'fixtures',\n\t'pnpm-workspace'\n) as AbsolutePath;\n\ndescribe('pnpm resolver', () => {\n\tconst cwd = fixturesDir;\n\tconst resolver = pnpm();\n\n\tit('should resolve workspace:* to exact version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'pkg-a', version: 'workspace:*' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'pkg-a', version: '1.2.3' });\n\t});\n\n\tit('should resolve workspace:^ to ^version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'pkg-a', version: 'workspace:^' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'pkg-a', version: '^1.2.3' });\n\t});\n\n\tit('should resolve workspace:~ to ~version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'pkg-b', version: 'workspace:~' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'pkg-b', version: '~2.0.0' });\n\t});\n\n\tit('should resolve workspace:1.0.0 to literal version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'pkg-a', version: 'workspace:1.0.0' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'pkg-a', version: '1.0.0' });\n\t});\n\n\tit('should resolve catalog: (default) to catalog version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'lodash', version: 'catalog:' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'lodash', version: '^4.17.21' });\n\t});\n\n\tit('should resolve catalog:deps (named) to named catalog version', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'react', version: 'catalog:deps' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'react', version: '^18.0.0' });\n\t});\n\n\tit('should return dep unchanged when version is not workspace/catalog', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'lodash', version: '^1.0.0' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual(dep);\n\t});\n\n\tit('should return dep unchanged when version is undefined', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'lodash' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual(dep);\n\t});\n\n\tit('should throw when workspace package not found', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'nonexistent', version: 'workspace:*' };\n\t\tawait expect(resolver(dep, { cwd })).rejects.toThrow(\n\t\t\t/Workspace package \"nonexistent\" not found/\n\t\t);\n\t});\n\n\tit('should throw when catalog entry not found', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'nonexistent', version: 'catalog:' };\n\t\tawait expect(resolver(dep, { cwd })).rejects.toThrow(\n\t\t\t/Package \"nonexistent\" not found in catalog/\n\t\t);\n\t});\n\n\tit('should use options.cwd for discovery', async () => {\n\t\tconst dep = { ecosystem: 'js' as const, name: 'pkg-a', version: 'workspace:*' };\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved.version).toBe('1.2.3');\n\t});\n\n\tit('should cache: multiple resolutions with same cwd do not re-parse', async () => {\n\t\tconst dep1 = { ecosystem: 'js' as const, name: 'pkg-a', version: 'workspace:*' };\n\t\tconst dep2 = { ecosystem: 'js' as const, name: 'pkg-b', version: 'workspace:*' };\n\t\tconst r1 = await resolver(dep1, { cwd });\n\t\tconst r2 = await resolver(dep2, { cwd });\n\t\texpect(r1.version).toBe('1.2.3');\n\t\texpect(r2.version).toBe('2.0.0');\n\t});\n\n\tit('should resolve workspace:../path to version from package at path', async () => {\n\t\tconst dep = {\n\t\t\tecosystem: 'js' as const,\n\t\t\tname: 'pkg-a',\n\t\t\tversion: 'workspace:packages/pkg-a',\n\t\t};\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({ ecosystem: 'js', name: 'pkg-a', version: '1.2.3' });\n\t});\n\n\tit('should resolve workspace:alias@* to npm:package@version', async () => {\n\t\tconst dep = {\n\t\t\tecosystem: 'js' as const,\n\t\t\tname: 'my-alias',\n\t\t\tversion: 'workspace:pkg-a@*',\n\t\t};\n\t\tconst resolved = await resolver(dep, { cwd });\n\t\texpect(resolved).toEqual({\n\t\t\tecosystem: 'js',\n\t\t\tname: 'my-alias',\n\t\t\tversion: 'npm:pkg-a@1.2.3',\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "packages/pnpm/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\", \"tests/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/pnpm/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/shadcn/CHANGELOG.md",
    "content": "# @jsrepo/shadcn\n\n## 7.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 6.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 6.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 6.0.0\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 5.0.2\n### Patch Changes\n\n\n- chore: bump deps ([#776](https://github.com/jsrepojs/jsrepo/pull/776))\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 5.0.1\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 5.0.0\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 4.0.0\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 3.0.0\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 2.0.1\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 1.0.1\n### Patch Changes\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n\n## 1.0.0\n### Patch Changes\n\n- Updated dependencies [[`fcbb4b6`](https://github.com/jsrepojs/jsrepo/commit/fcbb4b69d684844a99f403e6880071302a176a27), [`43584f1`](https://github.com/jsrepojs/jsrepo/commit/43584f102a7e085195a4b738a56cb9a8aac65383)]:\n  - jsrepo@3.1.0\n\n## 0.0.12\n### Patch Changes\n\n- Updated dependencies [[`0bf74a4`](https://github.com/jsrepojs/jsrepo/commit/0bf74a448c0ff526aa353a0924646b3d647fd1bd), [`069cc97`](https://github.com/jsrepojs/jsrepo/commit/069cc979f6a4ecaac71cb5beece5c83860fbb915)]:\n  - jsrepo@3.0.11\n\n## 0.0.11\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n- Updated dependencies [[`07d0399`](https://github.com/jsrepojs/jsrepo/commit/07d039908e241f7bdcc084c5697a9534fd2e4788), [`2362f8e`](https://github.com/jsrepojs/jsrepo/commit/2362f8e90ad79ba6df0fe6cfa4ceb82362ee5b62)]:\n  - jsrepo@3.0.10\n\n## 0.0.10\n### Patch Changes\n\n- Updated dependencies [[`7d4e98a`](https://github.com/jsrepojs/jsrepo/commit/7d4e98a5e1588d430f5195181b30e59303e25efb)]:\n  - jsrepo@3.0.9\n\n## 0.0.9\n### Patch Changes\n\n- Updated dependencies [[`a704bd0`](https://github.com/jsrepojs/jsrepo/commit/a704bd09d678ff608c5c2e61c8b91f8c09717012)]:\n  - jsrepo@3.0.8\n\n## 0.0.8\n### Patch Changes\n\n- Updated dependencies [[`da29502`](https://github.com/jsrepojs/jsrepo/commit/da29502db16dccd2761b88f33e3a921ff7fd21ec)]:\n  - jsrepo@3.0.7\n\n## 0.0.7\n### Patch Changes\n\n- Updated dependencies [[`129abaf`](https://github.com/jsrepojs/jsrepo/commit/129abafd8b560e8240c6edb414ce3b74d4c7b3a5)]:\n  - jsrepo@3.0.6\n\n## 0.0.6\n### Patch Changes\n\n- Updated dependencies [[`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5)]:\n  - jsrepo@3.0.5\n\n## 0.0.5\n### Patch Changes\n\n- Updated dependencies [[`ed6cd82`](https://github.com/jsrepojs/jsrepo/commit/ed6cd82054a39a74513d39631d406dad829be9a9)]:\n  - jsrepo@3.0.4\n\n## 0.0.4\n### Patch Changes\n\n- Updated dependencies [[`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0)]:\n  - jsrepo@3.0.3\n\n## 0.0.3\n### Patch Changes\n\n- Updated dependencies [[`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59), [`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59)]:\n  - jsrepo@3.0.2\n\n## 0.0.2\n### Patch Changes\n\n- Updated dependencies [[`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad)]:\n  - jsrepo@3.0.1\n\n## 0.0.1\n### Patch Changes\n\n\n- fix: Add `title` to `ShadcnRegistryItem` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- feat: add provider for registry directory ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- chore: update docs links ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- fix provider export ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- initial beta release ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n- Updated dependencies [[`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843)]:\n  - jsrepo@3.0.0\n\n## 0.0.1-beta.31\n### Patch Changes\n\n- Updated dependencies [[`9256a16`](https://github.com/jsrepojs/jsrepo/commit/9256a16d6ae360840097a7824dcf89743f7d9c69)]:\n  - jsrepo@3.0.0-beta.29\n\n## 0.0.1-beta.30\n### Patch Changes\n\n- Updated dependencies [[`db08900`](https://github.com/jsrepojs/jsrepo/commit/db08900353eac423d359b437362802c3f4b5b331)]:\n  - jsrepo@3.0.0-beta.28\n\n## 0.0.1-beta.29\n### Patch Changes\n\n- Updated dependencies [[`a5b3f2f`](https://github.com/jsrepojs/jsrepo/commit/a5b3f2f9c18539b7816af385c5b477cfdf991261)]:\n  - jsrepo@3.0.0-beta.27\n\n## 0.0.1-beta.28\n### Patch Changes\n\n- Updated dependencies [[`2d20f97`](https://github.com/jsrepojs/jsrepo/commit/2d20f97614e90bdc2b99e0347e13612687830466)]:\n  - jsrepo@3.0.0-beta.26\n\n## 0.0.1-beta.27\n### Patch Changes\n\n\n- fix provider export ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 0.0.1-beta.26\n### Patch Changes\n\n\n- feat: add provider for registry directory ([#682](https://github.com/jsrepojs/jsrepo/pull/682))\n\n- Updated dependencies [[`44724ef`](https://github.com/jsrepojs/jsrepo/commit/44724ef4fbf15315b420328ee3dd5a158124fc94), [`dc4b4c9`](https://github.com/jsrepojs/jsrepo/commit/dc4b4c9892ede2e8f619b9af8af479fc0e5f72bc)]:\n  - jsrepo@3.0.0-beta.25\n\n## 0.0.1-beta.25\n### Patch Changes\n\n- Updated dependencies [[`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc), [`eb5f7c0`](https://github.com/jsrepojs/jsrepo/commit/eb5f7c03bf2d68ebcf1b72d9950aa2549acb0873), [`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc)]:\n  - jsrepo@3.0.0-beta.24\n\n## 0.0.1-beta.24\n### Patch Changes\n\n- Updated dependencies [[`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4), [`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4)]:\n  - jsrepo@3.0.0-beta.23\n\n## 0.0.1-beta.23\n### Patch Changes\n\n- Updated dependencies [[`01e6c72`](https://github.com/jsrepojs/jsrepo/commit/01e6c7261b29ab1d3a6b29b728fb4fc92e43e8ce)]:\n  - jsrepo@3.0.0-beta.22\n\n## 0.0.1-beta.22\n### Patch Changes\n\n- Updated dependencies [[`cbb1656`](https://github.com/jsrepojs/jsrepo/commit/cbb165612ec61d9e3e2c0277fbd3e7e684dc881e)]:\n  - jsrepo@3.0.0-beta.21\n\n## 0.0.1-beta.21\n### Patch Changes\n\n- Updated dependencies [[`b378550`](https://github.com/jsrepojs/jsrepo/commit/b378550b23ebc4df17770176b0ec74a6a69d2f18)]:\n  - jsrepo@3.0.0-beta.20\n\n## 0.0.1-beta.20\n### Patch Changes\n\n- Updated dependencies [[`86260de`](https://github.com/jsrepojs/jsrepo/commit/86260de53bdf690db170f97baa98c17e551d0d6d)]:\n  - jsrepo@3.0.0-beta.19\n\n## 0.0.1-beta.19\n### Patch Changes\n\n- Updated dependencies [[`fec54f8`](https://github.com/jsrepojs/jsrepo/commit/fec54f88e9a15f5c8d6bcc931c18c52758e1bcb6), [`dc9c06f`](https://github.com/jsrepojs/jsrepo/commit/dc9c06fa996a88d241378b31b5fd218dee592837)]:\n  - jsrepo@3.0.0-beta.18\n\n## 0.0.1-beta.18\n### Patch Changes\n\n- Updated dependencies [[`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563), [`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563)]:\n  - jsrepo@3.0.0-beta.17\n\n## 0.0.1-beta.17\n### Patch Changes\n\n- Updated dependencies [[`e2cfb54`](https://github.com/jsrepojs/jsrepo/commit/e2cfb54e2e7e8149d88ec2b370393ac1a9704e85)]:\n  - jsrepo@3.0.0-beta.16\n\n## 0.0.1-beta.16\n### Patch Changes\n\n- Updated dependencies [[`7541537`](https://github.com/jsrepojs/jsrepo/commit/754153741b409e92e867d306c2b0999a64f74b6d)]:\n  - jsrepo@3.0.0-beta.15\n\n## 0.0.1-beta.15\n### Patch Changes\n\n- Updated dependencies [[`dc0a14d`](https://github.com/jsrepojs/jsrepo/commit/dc0a14ddbf1da96705ad72e55f839675bc232dab)]:\n  - jsrepo@3.0.0-beta.14\n\n## 0.0.1-beta.14\n### Patch Changes\n\n- Updated dependencies [[`c168a33`](https://github.com/jsrepojs/jsrepo/commit/c168a33477541ff026a7247736a14c6dad4efa53), [`9f93eae`](https://github.com/jsrepojs/jsrepo/commit/9f93eae1cad08f934f4c50c45f5a70538c487a63)]:\n  - jsrepo@3.0.0-beta.13\n\n## 0.0.1-beta.13\n### Patch Changes\n\n- Updated dependencies [[`bef071f`](https://github.com/jsrepojs/jsrepo/commit/bef071fde5f7d8143346abdffd766ed8dec866f3)]:\n  - jsrepo@3.0.0-beta.12\n\n## 0.0.1-beta.12\n### Patch Changes\n\n- Updated dependencies [[`f9d2e1f`](https://github.com/jsrepojs/jsrepo/commit/f9d2e1f906f0d39b8c341c2aa678ac31ce0a2f3f)]:\n  - jsrepo@3.0.0-beta.11\n\n## 0.0.1-beta.11\n### Patch Changes\n\n- Updated dependencies [[`709a24d`](https://github.com/jsrepojs/jsrepo/commit/709a24d734ed712fd60add5f2b4b70243908e94b)]:\n  - jsrepo@3.0.0-beta.10\n\n## 0.0.1-beta.10\n### Patch Changes\n\n\n- fix: Add `title` to `ShadcnRegistryItem` ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n## 0.0.1-beta.9\n### Patch Changes\n\n- Updated dependencies [[`4c664ed`](https://github.com/jsrepojs/jsrepo/commit/4c664ed08947f4a1906f80655b8fd12b7b6ec63b)]:\n  - jsrepo@3.0.0-beta.9\n\n## 0.0.1-beta.8\n### Patch Changes\n\n- Updated dependencies [[`0c33653`](https://github.com/jsrepojs/jsrepo/commit/0c336530ead46f05e85ece847a3dc76a51149c82)]:\n  - jsrepo@3.0.0-beta.8\n\n## 0.0.1-beta.7\n### Patch Changes\n\n- Updated dependencies [[`5e5759b`](https://github.com/jsrepojs/jsrepo/commit/5e5759be0e35f7b46664198ab3156b6998532c28)]:\n  - jsrepo@3.0.0-beta.7\n\n## 0.0.1-beta.6\n### Patch Changes\n\n- Updated dependencies [[`bfaabbb`](https://github.com/jsrepojs/jsrepo/commit/bfaabbb7ae29e0b511e9e84f61fdfb922af6b413)]:\n  - jsrepo@3.0.0-beta.6\n\n## 0.0.1-beta.5\n### Patch Changes\n\n- Updated dependencies [[`95fd7da`](https://github.com/jsrepojs/jsrepo/commit/95fd7da66287c6595ad1fd3de25664719aa6c9b4)]:\n  - jsrepo@3.0.0-beta.5\n\n## 0.0.1-beta.4\n### Patch Changes\n\n- Updated dependencies [[`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a)]:\n  - jsrepo@3.0.0-beta.4\n\n## 0.0.1-beta.3\n### Patch Changes\n\n- Updated dependencies [[`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857), [`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857)]:\n  - jsrepo@3.0.0-beta.3\n\n## 0.0.1-beta.2\n### Patch Changes\n\n- Updated dependencies [[`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4), [`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4)]:\n  - jsrepo@3.0.0-beta.2\n\n## 0.0.1-beta.1\n### Patch Changes\n\n\n- chore: update docs links ([#637](https://github.com/jsrepojs/jsrepo/pull/637))\n\n- Updated dependencies [[`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2b9e4be`](https://github.com/jsrepojs/jsrepo/commit/2b9e4be45964bf2cde16f36515bf760e3ddf66a7)]:\n  - jsrepo@3.0.0-beta.1\n\n## 0.0.1-beta.0\n### Patch Changes\n\n\n- initial beta release ([#635](https://github.com/jsrepojs/jsrepo/pull/635))\n\n- Updated dependencies [[`4416209`](https://github.com/jsrepojs/jsrepo/commit/44162092b38171d68817f4d0598f3ec8ddcc795d)]:\n  - jsrepo@3.0.0-beta.0\n"
  },
  {
    "path": "packages/shadcn/README.md",
    "content": "# @jsrepo/shadcn\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/shadcn)](https://npmjs.com/package/@jsrepo/shadcn)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/shadcn)](https://npmjs.com/package/@jsrepo/shadcn)\n\nA package to help you distribute your jsrepo registry as a shadcn registry.\n\n## Usage\n\nInstall the package:\n\n```sh\npnpm install @jsrepo/shadcn -D\n```\n\nUse the output to output a shadcn registry:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport { output } from \"@jsrepo/shadcn/output\";\n\nexport default defineConfig({\n\tregistry: {\n        // ...\n\t\toutputs: [output({ dir: \"./public/r\" })],\n\t},\n});\n```\n\nIf you want to ensure compatibility with the shadcn registry while losing access to some of the features of the jsrepo registry you can use the `defineShadcnRegistry` function. This can also be useful for incremental adoption as it closely matches the shadcn registry schema.\n\n```ts\nimport { defineShadcnRegistry, output } from \"@jsrepo/shadcn\";\n\nexport default defineConfig({\n\tregistry: defineShadcnRegistry({\n\t\t// ...\n        // make sure you still include the output\n        outputs: [output({ dir: \"./public/r\" })],\n\t}),\n});\n```"
  },
  {
    "path": "packages/shadcn/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/shadcn/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/shadcn\",\n\t\"description\": \"A package to help you distribute your jsrepo registry as a shadcn registry.\",\n\t\"version\": \"7.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev/docs/outputs/shadcn\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"shadcn\",\n\t\t\"registry\",\n\t\t\"output\",\n\t\t\"adapter\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t},\n\t\t\"./output\": {\n\t\t\t\"types\": \"./dist/output.d.mts\",\n\t\t\t\"default\": \"./dist/output.mjs\"\n\t\t},\n\t\t\"./provider\": {\n\t\t\t\"types\": \"./dist/provider.d.mts\",\n\t\t\t\"default\": \"./dist/provider.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"shadcn\": \"^3.8.5\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"zod\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/shadcn/src/index.ts",
    "content": "import {\n\tJsrepoError,\n\ttype Output,\n\ttype RegistryConfig,\n\ttype RegistryItem,\n\ttype RemoteDependency,\n} from 'jsrepo';\nimport type { registryItemTypeSchema } from 'shadcn/schema';\nimport { parsePackageName } from './utils';\n\nexport * from './output';\n\nexport { provider as default } from './provider';\n\nexport type ShadcnRegistryItemType = (typeof registryItemTypeSchema.options)[number];\n\nexport type ShadcnRegistry = {\n\tname: string;\n\thomepage: string;\n\texcludeDeps: string[];\n\titems: ShadcnRegistryItem[];\n\toutputs: Output[];\n};\n\nexport type ShadcnRegistryItem = {\n\tname: string;\n\t/** Human readable title of the item */\n\ttitle?: string;\n\ttype: ShadcnRegistryItemType;\n\tdescription?: string;\n\tfiles: Array<\n\t\t| {\n\t\t\t\tpath: string;\n\t\t\t\ttype: 'registry:file' | 'registry:page';\n\t\t\t\ttarget: string;\n\t\t  }\n\t\t| {\n\t\t\t\tpath: string;\n\t\t\t\ttype: Exclude<ShadcnRegistryItemType, 'registry:file' | 'registry:page'>;\n\t\t\t\ttarget?: string;\n\t\t  }\n\t>;\n\t/**\n\t * Requires that all dependencies of the item must be part of the registry.\n\t *\n\t * @default true\n\t */\n\tstrict?: boolean;\n\t/** Whether the dependency resolution should be automatic or manual. @default \"auto\" */\n\tdependencyResolution?: 'auto' | 'manual';\n\tregistryDependencies?: string[];\n\tdependencies?: string[];\n\t/**\n\t * Environment variables that are required for the item to work. These will be added to the users `.env` or `.env.local` file. NEVER ADD SECRETS HERE.\n\t */\n\tenvVars?: Record<string, string>;\n\t/**\n\t * Organize your registry item.\n\t */\n\tcategories?: string[];\n\t/**\n\t * Add additional metadata to your registry item.\n\t */\n\tmeta?: Record<string, string>;\n};\n\nexport function defineShadcnRegistry(registry: ShadcnRegistry): RegistryConfig {\n\treturn {\n\t\t...registry,\n\t\titems: registry.items.map((item) => {\n\t\t\treturn {\n\t\t\t\t...item,\n\t\t\t\tdependencies: item.dependencies?.map((dependency) => {\n\t\t\t\t\tconst parsed = parsePackageName(dependency);\n\t\t\t\t\tif (parsed === undefined) {\n\t\t\t\t\t\tthrow new JsrepoError(`Invalid package name: ${dependency}`, {\n\t\t\t\t\t\t\tsuggestion: 'Please provide a valid package name.',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tecosystem: 'js',\n\t\t\t\t\t\tname: parsed.name,\n\t\t\t\t\t\tversion: parsed.version,\n\t\t\t\t\t} satisfies RemoteDependency;\n\t\t\t\t}),\n\t\t\t} satisfies RegistryItem;\n\t\t}),\n\t};\n}\n"
  },
  {
    "path": "packages/shadcn/src/output.ts",
    "content": "import fs from 'node:fs';\nimport path from 'node:path';\nimport { JsrepoError } from 'jsrepo';\nimport type { Output } from 'jsrepo/outputs';\nimport {\n\ttype Registry,\n\ttype RegistryItem,\n\tregistryItemSchema,\n\tregistryItemTypeSchema,\n\tregistrySchema,\n} from 'shadcn/schema';\n\nexport type OutputOptions = {\n\tformat?: boolean;\n\tdir: string;\n};\n\nexport function output(options: OutputOptions): Output {\n\treturn {\n\t\toutput: async (buildResult, { cwd }) => {\n\t\t\tif (buildResult.homepage === undefined) {\n\t\t\t\tthrow new JsrepoError(`No homepage was provided for ${buildResult.name}`, {\n\t\t\t\t\tsuggestion: 'Please provide a homepage in your config.',\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const item of buildResult.items) {\n\t\t\t\tconst type = getType(item.type);\n\t\t\t\tif (type === 'registry:base' || type === 'registry:font') {\n\t\t\t\t\tthrow new JsrepoError(\n\t\t\t\t\t\t`${item.name} is of type ${type} and cannot be added to the registry.`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsuggestion: 'Please remove the item from the registry.',\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst parseResult = registryItemTypeSchema.safeParse(type);\n\t\t\t\tif (!parseResult.success) {\n\t\t\t\t\tthrow new JsrepoError(\n\t\t\t\t\t\t`Invalid item type: ${type} for ${\n\t\t\t\t\t\t\titem.name\n\t\t\t\t\t\t}. Expected one of: ${registryItemTypeSchema.options.join(', ')}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsuggestion: 'Please use a valid item type.',\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst registryJson: Registry & { $schema: string } = {\n\t\t\t\t$schema: 'https://ui.shadcn.com/schema/registry.json',\n\t\t\t\tname: buildResult.name,\n\t\t\t\thomepage: buildResult.homepage,\n\t\t\t\titems: buildResult.items.map((item) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: item.name,\n\t\t\t\t\t\ttitle: item.title,\n\t\t\t\t\t\tdescription: item.description,\n\t\t\t\t\t\t// validated above\n\t\t\t\t\t\ttype: getType(item.type) as Exclude<\n\t\t\t\t\t\t\tRegistry['items'][number]['type'],\n\t\t\t\t\t\t\t'registry:base' | 'registry:font'\n\t\t\t\t\t\t>,\n\t\t\t\t\t\tenvVars: item.envVars,\n\t\t\t\t\t\tdependencies: item.dependencies?.map(\n\t\t\t\t\t\t\t(dependency) =>\n\t\t\t\t\t\t\t\t`${dependency.name}${dependency.version ? `@${dependency.version}` : ''}`\n\t\t\t\t\t\t),\n\t\t\t\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\t\t\t\tfiles: item.files.map((file) => {\n\t\t\t\t\t\t\tconst type = getType(file.type);\n\t\t\t\t\t\t\tif (type === 'registry:page' || type === 'registry:file') {\n\t\t\t\t\t\t\t\tif (file.target === undefined) {\n\t\t\t\t\t\t\t\t\tthrow new JsrepoError(\n\t\t\t\t\t\t\t\t\t\t`Target is required for registry items with type ${type}. Please provide a target for ${file.path} on ${item.name}`,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tsuggestion: 'Please provide a target for the file.',\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: already checked it\n\t\t\t\t\t\t\t\ttype: type as any,\n\t\t\t\t\t\t\t\tpath: file.path,\n\t\t\t\t\t\t\t\ttarget: file.target,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}),\n\t\t\t\t\t} satisfies Registry['items'][number];\n\t\t\t\t}),\n\t\t\t};\n\n\t\t\tconst items: RegistryItem[] = buildResult.items.map((item) => {\n\t\t\t\treturn {\n\t\t\t\t\t$schema: 'https://ui.shadcn.com/schema/registry-item.json',\n\t\t\t\t\tname: item.name,\n\t\t\t\t\ttitle: item.title,\n\t\t\t\t\tdescription: item.description,\n\t\t\t\t\ttype: getType(item.type) as Exclude<\n\t\t\t\t\t\tRegistryItem['type'],\n\t\t\t\t\t\t'registry:base' | 'registry:font'\n\t\t\t\t\t>,\n\t\t\t\t\tfiles: item.files.map((file) => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: already checked it\n\t\t\t\t\t\t\ttype: getType(file.type) as any,\n\t\t\t\t\t\t\tpath: file.path,\n\t\t\t\t\t\t\ttarget: file.target,\n\t\t\t\t\t\t\tcontent: file.content,\n\t\t\t\t\t\t};\n\t\t\t\t\t}),\n\t\t\t\t\tenvVars: item.envVars,\n\t\t\t\t\tregistryDependencies: item.registryDependencies,\n\t\t\t\t\tdependencies: item.dependencies?.map(\n\t\t\t\t\t\t(dependency) =>\n\t\t\t\t\t\t\t`${dependency.name}${dependency.version ? `@${dependency.version}` : ''}`\n\t\t\t\t\t),\n\t\t\t\t} satisfies RegistryItem;\n\t\t\t});\n\n\t\t\t// contain errors to this plugin so there aren't issues for users\n\t\t\tconst parsedRegistryResult = registrySchema.safeParse(registryJson);\n\t\t\tif (!parsedRegistryResult.success) {\n\t\t\t\tthrow new JsrepoError(\n\t\t\t\t\t`Invalid registry ${registryJson.name}: ${parsedRegistryResult.error.message}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t\t\"This one's on us. Please file an issue at https://github.com/jsrepojs/jsrepo/issues\",\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tfor (const item of items) {\n\t\t\t\tconst parsedItemResult = registryItemSchema.safeParse(item);\n\t\t\t\tif (!parsedItemResult.success) {\n\t\t\t\t\tthrow new JsrepoError(\n\t\t\t\t\t\t`Invalid item ${item.name}: ${parsedItemResult.error.message}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsuggestion:\n\t\t\t\t\t\t\t\t\"This one's on us. Please file an issue at https://github.com/jsrepojs/jsrepo/issues\",\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst outDir = path.join(cwd, options.dir);\n\t\t\tif (!fs.existsSync(outDir)) {\n\t\t\t\tfs.mkdirSync(outDir, { recursive: true });\n\t\t\t}\n\n\t\t\tfs.writeFileSync(\n\t\t\t\tpath.join(outDir, 'registry.json'),\n\t\t\t\tstringify(registryJson, { format: options.format })\n\t\t\t);\n\t\t\tfor (const item of items) {\n\t\t\t\tfs.writeFileSync(\n\t\t\t\t\tpath.join(outDir, `${item.name}.json`),\n\t\t\t\t\tstringify(item, { format: options.format })\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\tclean: async ({ cwd }) => {\n\t\t\tconst outDir = path.join(cwd, options.dir);\n\t\t\tif (!fs.existsSync(outDir)) return;\n\t\t\tfs.rmSync(path.join(cwd, options.dir), { recursive: true });\n\t\t},\n\t};\n}\n\nfunction getType(type?: string) {\n\t// this way we don't require target by default\n\tif (!type) return 'registry:ui';\n\treturn type.startsWith('registry:') ? type : `registry:${type}`;\n}\n\nfunction stringify(data: unknown, options: { format?: boolean } = {}): string {\n\treturn JSON.stringify(data, null, options.format ? '\\t' : undefined);\n}\n"
  },
  {
    "path": "packages/shadcn/src/provider.ts",
    "content": "import { JsrepoError, ProviderFetchError } from 'jsrepo/errors';\nimport type { CreateOptions, FetchOptions, Provider, ProviderFactory } from 'jsrepo/providers';\n\nexport type ShadcnOptions = {\n\t/**\n\t * The base url of the registry index.\n\t *\n\t * @default https://ui.shadcn.com/r/registries.json\n\t */\n\tregistryIndexUrl?: string;\n};\n\n/**\n * A provider for the shadcn registry index.\n * @param options\n * @returns\n *\n * @urlFormat\n * ```\n * shadcn:@react-bits\n * ```\n *\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo/config\";\n * import shadcn from \"@jsrepo/shadcn\";\n *\n * export default defineConfig({\n * \tproviders: [shadcn()],\n * });\n * ```\n */\nexport function provider(options: ShadcnOptions = {}): ProviderFactory {\n\treturn {\n\t\tname: 'shadcn',\n\t\tmatches: (url: string) => url.startsWith('shadcn:'),\n\t\tcreate: (url: string, createOpts: CreateOptions) => Shadcn.create(url, options, createOpts),\n\t};\n}\n\ntype ShadcnState = {\n\tregistryUrl: string;\n};\n\nclass Shadcn implements Provider {\n\tconstructor(\n\t\treadonly state: ShadcnState,\n\t\treadonly opts: ShadcnOptions\n\t) {}\n\n\tasync fetch(resourcePath: string, { fetch: f = fetch }: FetchOptions): Promise<string> {\n\t\tconst url = this.state.registryUrl.replace('{name}', resourcePath.replace('.json', ''));\n\t\ttry {\n\t\t\tconst response = await f(url.toString());\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst isJson = response.headers.get('content-type')?.includes('application/json');\n\t\t\t\tif (isJson) {\n\t\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t\t`${response.status} ${(await response.json()).message ?? response.statusText}`,\n\t\t\t\t\t\turl.toString()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new ProviderFetchError(\n\t\t\t\t\t`${response.status} ${response.statusText}`,\n\t\t\t\t\turl.toString()\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn await response.text();\n\t\t} catch (error) {\n\t\t\tif (error instanceof ProviderFetchError) {\n\t\t\t\tthrow new ProviderFetchError(error.originalMessage, url.toString());\n\t\t\t}\n\t\t\tthrow new ProviderFetchError(\n\t\t\t\t`${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\turl.toString()\n\t\t\t);\n\t\t}\n\t}\n\n\tstatic async create(\n\t\turl: string,\n\t\topts: ShadcnOptions,\n\t\t{ fetch: f = fetch }: CreateOptions\n\t): Promise<Provider> {\n\t\tconst registry = url.slice('shadcn:'.length);\n\t\tconst indexUrl = opts.registryIndexUrl ?? 'https://ui.shadcn.com/r/registries.json';\n\t\ttry {\n\t\t\tconst response = await f(indexUrl);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new JsrepoError(\n\t\t\t\t\t`Failed to fetch registry index ${response.status} ${response.statusText}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestion: 'Please try again',\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst res = (await response.json()) as Record<string, string>;\n\n\t\t\tconst registryUrl = res[registry];\n\t\t\tif (!registryUrl) {\n\t\t\t\tthrow new JsrepoError(`Registry ${registry} not found in registry index`, {\n\t\t\t\t\tsuggestion: 'Please check the registry name',\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn new Shadcn({ registryUrl }, opts);\n\t\t} catch {\n\t\t\tthrow new JsrepoError(`Failed to fetch registry index at ${indexUrl}`, {\n\t\t\t\tsuggestion: 'Please try again',\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "packages/shadcn/src/utils.ts",
    "content": "/**\n * Adapted from https://github.com/egoist/parse-package-name/blob/main/src/index.ts\n * @module\n */\n\n// Parsed a scoped package name into name, version, and path.\nconst RE_SCOPED = /^(@[^/]+\\/[^@/]+)(?:@([^/]+))?(\\/.*)?$/;\n// Parsed a non-scoped package name into name, version, path\nconst RE_NON_SCOPED = /^([^@/]+)(?:@([^/]+))?(\\/.*)?$/;\n\nexport type Package = {\n\t/** Name of the package as it would be installed from npm */\n\tname: string;\n\t/** Version of the package */\n\tversion: string;\n\tpath: string;\n};\n\nexport function parsePackageName(\n\tinput: string\n): { name: string; version: string | undefined; path: string } | undefined {\n\tconst m = RE_SCOPED.exec(input) || RE_NON_SCOPED.exec(input);\n\n\tif (!m) return undefined;\n\n\treturn {\n\t\tname: m[1] || '',\n\t\tversion: m[2] || undefined,\n\t\tpath: m[3] || '',\n\t};\n}\n"
  },
  {
    "path": "packages/shadcn/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/shadcn/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts', 'src/output.ts', 'src/provider.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/transform-biome/CHANGELOG.md",
    "content": "# @jsrepo/transform-biome\n\n## 7.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 6.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 6.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 6.0.0\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 5.0.2\n### Patch Changes\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 5.0.1\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 5.0.0\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 4.0.0\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 3.0.0\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 2.0.1\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 1.0.1\n### Patch Changes\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n\n## 1.0.0\n### Patch Changes\n\n- Updated dependencies [[`fcbb4b6`](https://github.com/jsrepojs/jsrepo/commit/fcbb4b69d684844a99f403e6880071302a176a27), [`43584f1`](https://github.com/jsrepojs/jsrepo/commit/43584f102a7e085195a4b738a56cb9a8aac65383)]:\n  - jsrepo@3.1.0\n\n## 0.0.12\n### Patch Changes\n\n- Updated dependencies [[`0bf74a4`](https://github.com/jsrepojs/jsrepo/commit/0bf74a448c0ff526aa353a0924646b3d647fd1bd), [`069cc97`](https://github.com/jsrepojs/jsrepo/commit/069cc979f6a4ecaac71cb5beece5c83860fbb915)]:\n  - jsrepo@3.0.11\n\n## 0.0.11\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n- Updated dependencies [[`07d0399`](https://github.com/jsrepojs/jsrepo/commit/07d039908e241f7bdcc084c5697a9534fd2e4788), [`2362f8e`](https://github.com/jsrepojs/jsrepo/commit/2362f8e90ad79ba6df0fe6cfa4ceb82362ee5b62)]:\n  - jsrepo@3.0.10\n\n## 0.0.10\n### Patch Changes\n\n- Updated dependencies [[`7d4e98a`](https://github.com/jsrepojs/jsrepo/commit/7d4e98a5e1588d430f5195181b30e59303e25efb)]:\n  - jsrepo@3.0.9\n\n## 0.0.9\n### Patch Changes\n\n- Updated dependencies [[`a704bd0`](https://github.com/jsrepojs/jsrepo/commit/a704bd09d678ff608c5c2e61c8b91f8c09717012)]:\n  - jsrepo@3.0.8\n\n## 0.0.8\n### Patch Changes\n\n- Updated dependencies [[`da29502`](https://github.com/jsrepojs/jsrepo/commit/da29502db16dccd2761b88f33e3a921ff7fd21ec)]:\n  - jsrepo@3.0.7\n\n## 0.0.7\n### Patch Changes\n\n- Updated dependencies [[`129abaf`](https://github.com/jsrepojs/jsrepo/commit/129abafd8b560e8240c6edb414ce3b74d4c7b3a5)]:\n  - jsrepo@3.0.6\n\n## 0.0.6\n### Patch Changes\n\n- Updated dependencies [[`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5)]:\n  - jsrepo@3.0.5\n\n## 0.0.5\n### Patch Changes\n\n- Updated dependencies [[`ed6cd82`](https://github.com/jsrepojs/jsrepo/commit/ed6cd82054a39a74513d39631d406dad829be9a9)]:\n  - jsrepo@3.0.4\n\n## 0.0.4\n### Patch Changes\n\n- Updated dependencies [[`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0)]:\n  - jsrepo@3.0.3\n\n## 0.0.3\n### Patch Changes\n\n- Updated dependencies [[`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59), [`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59)]:\n  - jsrepo@3.0.2\n\n## 0.0.2\n### Patch Changes\n\n- Updated dependencies [[`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad)]:\n  - jsrepo@3.0.1\n\n## 0.0.1\n### Patch Changes\n\n\n- chore: bump deps ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n\n- initial beta release ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n- Updated dependencies [[`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843)]:\n  - jsrepo@3.0.0\n\n## 0.0.1-beta.29\n### Patch Changes\n\n- Updated dependencies [[`9256a16`](https://github.com/jsrepojs/jsrepo/commit/9256a16d6ae360840097a7824dcf89743f7d9c69)]:\n  - jsrepo@3.0.0-beta.29\n\n## 0.0.1-beta.28\n### Patch Changes\n\n- Updated dependencies [[`db08900`](https://github.com/jsrepojs/jsrepo/commit/db08900353eac423d359b437362802c3f4b5b331)]:\n  - jsrepo@3.0.0-beta.28\n\n## 0.0.1-beta.27\n### Patch Changes\n\n- Updated dependencies [[`a5b3f2f`](https://github.com/jsrepojs/jsrepo/commit/a5b3f2f9c18539b7816af385c5b477cfdf991261)]:\n  - jsrepo@3.0.0-beta.27\n\n## 0.0.1-beta.26\n### Patch Changes\n\n- Updated dependencies [[`2d20f97`](https://github.com/jsrepojs/jsrepo/commit/2d20f97614e90bdc2b99e0347e13612687830466)]:\n  - jsrepo@3.0.0-beta.26\n\n## 0.0.1-beta.25\n### Patch Changes\n\n- Updated dependencies [[`44724ef`](https://github.com/jsrepojs/jsrepo/commit/44724ef4fbf15315b420328ee3dd5a158124fc94), [`dc4b4c9`](https://github.com/jsrepojs/jsrepo/commit/dc4b4c9892ede2e8f619b9af8af479fc0e5f72bc)]:\n  - jsrepo@3.0.0-beta.25\n\n## 0.0.1-beta.24\n### Patch Changes\n\n- Updated dependencies [[`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc), [`eb5f7c0`](https://github.com/jsrepojs/jsrepo/commit/eb5f7c03bf2d68ebcf1b72d9950aa2549acb0873), [`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc)]:\n  - jsrepo@3.0.0-beta.24\n\n## 0.0.1-beta.23\n### Patch Changes\n\n- Updated dependencies [[`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4), [`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4)]:\n  - jsrepo@3.0.0-beta.23\n\n## 0.0.1-beta.22\n### Patch Changes\n\n- Updated dependencies [[`01e6c72`](https://github.com/jsrepojs/jsrepo/commit/01e6c7261b29ab1d3a6b29b728fb4fc92e43e8ce)]:\n  - jsrepo@3.0.0-beta.22\n\n## 0.0.1-beta.21\n### Patch Changes\n\n- Updated dependencies [[`cbb1656`](https://github.com/jsrepojs/jsrepo/commit/cbb165612ec61d9e3e2c0277fbd3e7e684dc881e)]:\n  - jsrepo@3.0.0-beta.21\n\n## 0.0.1-beta.20\n### Patch Changes\n\n- Updated dependencies [[`b378550`](https://github.com/jsrepojs/jsrepo/commit/b378550b23ebc4df17770176b0ec74a6a69d2f18)]:\n  - jsrepo@3.0.0-beta.20\n\n## 0.0.1-beta.19\n### Patch Changes\n\n- Updated dependencies [[`86260de`](https://github.com/jsrepojs/jsrepo/commit/86260de53bdf690db170f97baa98c17e551d0d6d)]:\n  - jsrepo@3.0.0-beta.19\n\n## 0.0.1-beta.18\n### Patch Changes\n\n- Updated dependencies [[`fec54f8`](https://github.com/jsrepojs/jsrepo/commit/fec54f88e9a15f5c8d6bcc931c18c52758e1bcb6), [`dc9c06f`](https://github.com/jsrepojs/jsrepo/commit/dc9c06fa996a88d241378b31b5fd218dee592837)]:\n  - jsrepo@3.0.0-beta.18\n\n## 0.0.1-beta.17\n### Patch Changes\n\n- Updated dependencies [[`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563), [`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563)]:\n  - jsrepo@3.0.0-beta.17\n\n## 0.0.1-beta.16\n### Patch Changes\n\n- Updated dependencies [[`e2cfb54`](https://github.com/jsrepojs/jsrepo/commit/e2cfb54e2e7e8149d88ec2b370393ac1a9704e85)]:\n  - jsrepo@3.0.0-beta.16\n\n## 0.0.1-beta.15\n### Patch Changes\n\n- Updated dependencies [[`7541537`](https://github.com/jsrepojs/jsrepo/commit/754153741b409e92e867d306c2b0999a64f74b6d)]:\n  - jsrepo@3.0.0-beta.15\n\n## 0.0.1-beta.14\n### Patch Changes\n\n- Updated dependencies [[`dc0a14d`](https://github.com/jsrepojs/jsrepo/commit/dc0a14ddbf1da96705ad72e55f839675bc232dab)]:\n  - jsrepo@3.0.0-beta.14\n\n## 0.0.1-beta.13\n### Patch Changes\n\n- Updated dependencies [[`c168a33`](https://github.com/jsrepojs/jsrepo/commit/c168a33477541ff026a7247736a14c6dad4efa53), [`9f93eae`](https://github.com/jsrepojs/jsrepo/commit/9f93eae1cad08f934f4c50c45f5a70538c487a63)]:\n  - jsrepo@3.0.0-beta.13\n\n## 0.0.1-beta.12\n### Patch Changes\n\n- Updated dependencies [[`bef071f`](https://github.com/jsrepojs/jsrepo/commit/bef071fde5f7d8143346abdffd766ed8dec866f3)]:\n  - jsrepo@3.0.0-beta.12\n\n## 0.0.1-beta.11\n### Patch Changes\n\n- Updated dependencies [[`f9d2e1f`](https://github.com/jsrepojs/jsrepo/commit/f9d2e1f906f0d39b8c341c2aa678ac31ce0a2f3f)]:\n  - jsrepo@3.0.0-beta.11\n\n## 0.0.1-beta.10\n### Patch Changes\n\n- Updated dependencies [[`709a24d`](https://github.com/jsrepojs/jsrepo/commit/709a24d734ed712fd60add5f2b4b70243908e94b)]:\n  - jsrepo@3.0.0-beta.10\n\n## 0.0.1-beta.9\n### Patch Changes\n\n- Updated dependencies [[`4c664ed`](https://github.com/jsrepojs/jsrepo/commit/4c664ed08947f4a1906f80655b8fd12b7b6ec63b)]:\n  - jsrepo@3.0.0-beta.9\n\n## 0.0.1-beta.8\n### Patch Changes\n\n- Updated dependencies [[`0c33653`](https://github.com/jsrepojs/jsrepo/commit/0c336530ead46f05e85ece847a3dc76a51149c82)]:\n  - jsrepo@3.0.0-beta.8\n\n## 0.0.1-beta.7\n### Patch Changes\n\n- Updated dependencies [[`5e5759b`](https://github.com/jsrepojs/jsrepo/commit/5e5759be0e35f7b46664198ab3156b6998532c28)]:\n  - jsrepo@3.0.0-beta.7\n\n## 0.0.1-beta.6\n### Patch Changes\n\n- Updated dependencies [[`bfaabbb`](https://github.com/jsrepojs/jsrepo/commit/bfaabbb7ae29e0b511e9e84f61fdfb922af6b413)]:\n  - jsrepo@3.0.0-beta.6\n\n## 0.0.1-beta.5\n### Patch Changes\n\n- Updated dependencies [[`95fd7da`](https://github.com/jsrepojs/jsrepo/commit/95fd7da66287c6595ad1fd3de25664719aa6c9b4)]:\n  - jsrepo@3.0.0-beta.5\n\n## 0.0.1-beta.4\n### Patch Changes\n\n- Updated dependencies [[`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a)]:\n  - jsrepo@3.0.0-beta.4\n\n## 0.0.1-beta.3\n### Patch Changes\n\n- Updated dependencies [[`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857), [`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857)]:\n  - jsrepo@3.0.0-beta.3\n\n## 0.0.1-beta.2\n### Patch Changes\n\n\n- chore: bump deps ([#641](https://github.com/jsrepojs/jsrepo/pull/641))\n\n- Updated dependencies [[`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4), [`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4)]:\n  - jsrepo@3.0.0-beta.2\n\n## 0.0.1-beta.1\n### Patch Changes\n\n- Updated dependencies [[`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2b9e4be`](https://github.com/jsrepojs/jsrepo/commit/2b9e4be45964bf2cde16f36515bf760e3ddf66a7)]:\n  - jsrepo@3.0.0-beta.1\n\n## 0.0.1-beta.0\n### Patch Changes\n\n\n- initial beta release ([#635](https://github.com/jsrepojs/jsrepo/pull/635))\n\n- Updated dependencies [[`4416209`](https://github.com/jsrepojs/jsrepo/commit/44162092b38171d68817f4d0598f3ec8ddcc795d)]:\n  - jsrepo@3.0.0-beta.0\n"
  },
  {
    "path": "packages/transform-biome/README.md",
    "content": "# @jsrepo/transform-biome\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/transform-biome)](https://npmjs.com/package/@jsrepo/transform-biome)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/transform-biome)](https://npmjs.com/package/@jsrepo/transform-biome)\n\nA transform plugin for formatting registry items with Biome using your local biome configuration before they are added to your project.\n\n## Usage\n\nRun the following command to install and add the transform to your config:\n\n```sh\njsrepo config transform @jsrepo/transform-biome\n```\n\n### Manual Configuration\n\nInstall the transform plugin:\n\n```sh\npnpm install @jsrepo/transform-biome -D\n```\n\nAdd the transform to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport biome from \"@jsrepo/transform-biome\";\n\nexport default defineConfig({\n\ttransforms: [biome()],\n});\n```"
  },
  {
    "path": "packages/transform-biome/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/transform-biome/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/transform-biome\",\n\t\"description\": \"A transform plugin for jsrepo to format code with biome.\",\n\t\"version\": \"7.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"transform\",\n\t\t\"biome\",\n\t\t\"plugin\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"dependencies\": {\n\t\t\"@biomejs/js-api\": \"^4.0.0\",\n\t\t\"@biomejs/wasm-nodejs\": \"^2.3.13\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/transform-biome/src/index.ts",
    "content": "import { Biome, Distribution } from '@biomejs/js-api';\nimport type { Transform } from 'jsrepo';\n\n/**\n * A transform plugin for jsrepo to format code with prettier.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import biome from \"@jsrepo/transform-biome\";\n *\n * export default defineConfig({\n *  // ...\n *  transforms: [biome()],\n * });\n * ```\n *\n * @param options - The options for the transform plugin.\n */\nexport default function (): Transform {\n\treturn {\n\t\ttransform: async ({ code, fileName, options }) => {\n\t\t\treturn {\n\t\t\t\tcode: await tryFormat(code, { fileName: fileName, cwd: options.cwd }),\n\t\t\t};\n\t\t},\n\t};\n}\n\nasync function tryFormat(code: string, { fileName, cwd }: { fileName: string; cwd: string }) {\n\ttry {\n\t\tconst biome = await Biome.create({\n\t\t\tdistribution: Distribution.NODE,\n\t\t});\n\n\t\tconst { projectKey } = biome.openProject(cwd);\n\n\t\treturn biome.formatContent(projectKey, code, { filePath: fileName }).content;\n\t} catch (err) {\n\t\tconsole.error(err);\n\t\treturn undefined;\n\t}\n}\n"
  },
  {
    "path": "packages/transform-biome/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/transform-biome/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/transform-filecasing/CHANGELOG.md",
    "content": "# @jsrepo/transform-filecasing\n\n## 6.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 5.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 5.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 5.0.0\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 4.0.2\n### Patch Changes\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 4.0.1\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 4.0.0\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 3.0.0\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 1.1.0\n### Minor Changes\n\n\n- feat: 🎉 Add `@jsrepo/transform-filecasing` package for filename case transformations ([#745](https://github.com/jsrepojs/jsrepo/pull/745))\n"
  },
  {
    "path": "packages/transform-filecasing/README.md",
    "content": "# @jsrepo/transform-filecasing\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/transform-filecasing)](https://npmjs.com/package/@jsrepo/transform-filecasing)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/transform-filecasing)](https://npmjs.com/package/@jsrepo/transform-filecasing)\n\nA transform plugin for transforming file and folder names to different case formats before they are added to your project.\n\n## Usage\n\nRun the following command to install and add the transform to your config:\n\n```sh\njsrepo config transform filecasing\n```\n\n### Manual Configuration\n\nInstall the transform plugin:\n\n```sh\npnpm install @jsrepo/transform-filecasing -D\n```\n\nAdd the transform to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport fileCasing from \"@jsrepo/transform-filecasing\";\n\nexport default defineConfig({\n    transforms: [fileCasing({ to: \"camel\" })],\n});\n```\n\n### Options\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `to` | `\"kebab\" \\| \"camel\" \\| \"snake\" \\| \"pascal\"` | The target case format for file and folder names |\n| `transformDirectories` | `boolean` | Whether to transform directory segments in the path. When `false`, only the filename baseName is transformed. Defaults to `true` |\n\n### Examples\n\nTransform both directories and filenames (default behavior):\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport fileCasing from \"@jsrepo/transform-filecasing\";\n\nexport default defineConfig({\n    // Config path: 'src/lib/components/ui'\n    // Item path: 'button/index.ts'\n    transforms: [fileCasing({ to: \"pascal\" })],\n});\n// Result: 'src/lib/components/ui/Button/Index.ts'\n// Only the item's relative path is transformed, not the config path\n```\n\nTransform only filenames, preserve directory names:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport fileCasing from \"@jsrepo/transform-filecasing\";\n\nexport default defineConfig({\n    // Config path: 'src/lib/components/ui'\n    // Item path: 'my-components/use-hook.ts'\n    transforms: [fileCasing({ to: \"camel\", transformDirectories: false })],\n});\n// Result: 'src/lib/components/ui/my-components/useHook.ts'\n// Only the filename is transformed, directories in the item path are preserved\n```\n"
  },
  {
    "path": "packages/transform-filecasing/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/transform-filecasing/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/transform-filecasing\",\n\t\"description\": \"A transform plugin for jsrepo to transform file and folder name cases.\",\n\t\"version\": \"6.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aleksei Gurianov\",\n\t\t\"url\": \"https://github.com/guria\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"transform\",\n\t\t\"change-case\",\n\t\t\"filecasing\",\n\t\t\"filename\",\n\t\t\"plugin\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"test\": \"vitest\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"dependencies\": {\n\t\t\"change-case\": \"^5.4.4\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"vitest\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/transform-filecasing/src/index.ts",
    "content": "import { camelCase, kebabCase, pascalCase, snakeCase } from 'change-case';\nimport type { Transform } from 'jsrepo';\nimport type { ItemRelativePath } from 'jsrepo/utils';\n\nexport type CaseType = 'kebab' | 'camel' | 'snake' | 'pascal';\n\nexport type Options = {\n\t/** The target case format to transform file and folder names to. */\n\tto: CaseType;\n\t/** Whether to transform directory segments in the path. When `false`, only the filename baseName is transformed. @default true */\n\ttransformDirectories?: boolean;\n};\n\nconst caseTransformers: Record<CaseType, (input: string) => string> = {\n\tkebab: kebabCase,\n\tcamel: camelCase,\n\tsnake: snakeCase,\n\tpascal: pascalCase,\n};\n\n/**\n * A transform plugin for jsrepo to transform file and folder name cases.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import fileCasing from \"@jsrepo/transform-filecasing\";\n *\n * export default defineConfig({\n *  // ...\n *  transforms: [fileCasing({ to: \"camel\" })],\n * });\n * ```\n *\n * @param options - The options for the transform plugin.\n */\nexport default function ({\n\tto = 'kebab',\n\ttransformDirectories = true,\n}: Partial<Options> = {}): Transform {\n\tconst transformer = caseTransformers[to];\n\tif (!transformer) {\n\t\tthrow new Error(`Invalid case type: \"${to}\". Expected one of: kebab, camel, snake, pascal`);\n\t}\n\n\treturn {\n\t\ttransform: async ({ fileName }) => {\n\t\t\tconst parts = fileName.split('/');\n\n\t\t\tconst transformedParts = parts.map((part, index) => {\n\t\t\t\t// Handle filename (last segment)\n\t\t\t\tif (index === parts.length - 1) {\n\t\t\t\t\t// Extract the base name (first part before any dot) and extensions\n\t\t\t\t\t// example: my-file.test.ts -> my-file & .test.ts\n\t\t\t\t\tconst firstDotIndex = part.indexOf('.');\n\t\t\t\t\tconst baseName = firstDotIndex >= 0 ? part.slice(0, firstDotIndex) : part;\n\t\t\t\t\tconst extensions = firstDotIndex >= 0 ? part.slice(firstDotIndex) : '';\n\t\t\t\t\tconst transformedBaseName = transformer(baseName);\n\n\t\t\t\t\treturn `${transformedBaseName}${extensions}`;\n\t\t\t\t}\n\n\t\t\t\t// Transform directory segments only if transformDirectories is true\n\t\t\t\treturn transformDirectories ? transformer(part) : part;\n\t\t\t});\n\n\t\t\tconst newFileName = transformedParts.join('/') as ItemRelativePath;\n\n\t\t\tif (newFileName === fileName) {\n\t\t\t\treturn {};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tfileName: newFileName,\n\t\t\t};\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "packages/transform-filecasing/tests/filecasing.test.ts",
    "content": "import type { AbsolutePath, ItemRelativePath } from 'jsrepo/utils';\nimport { describe, expect, it } from 'vitest';\nimport fileCasing from '../src';\n\nconst transformOptions = {\n\tcwd: '' as AbsolutePath,\n\titem: { name: 'test', type: 'component' as const },\n\tregistryUrl: 'https://example.com',\n};\n\ndescribe('fileCasing', () => {\n\tit('should allow being called with no options', async () => {\n\t\tconst plugin = fileCasing();\n\t\tconst result = await plugin.transform({\n\t\t\tcode: 'const x = 1;',\n\t\t\tfileName: 'myComponent.ts' as ItemRelativePath,\n\t\t\toptions: transformOptions,\n\t\t});\n\t\texpect(result.fileName).toBe('my-component.ts');\n\t});\n\n\tdescribe('kebab-case to camelCase', () => {\n\t\tit('should convert kebab-case filename to camelCase', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myComponent.ts');\n\t\t});\n\t});\n\n\tdescribe('camelCase to kebab-case', () => {\n\t\tit('should convert camelCase filename to kebab-case', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'kebab' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'myComponent.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('my-component.ts');\n\t\t});\n\t});\n\n\tdescribe('to snake_case', () => {\n\t\tit('should convert kebab-case filename to snake_case', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'snake' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('my_component.ts');\n\t\t});\n\n\t\tit('should convert camelCase filename to snake_case', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'snake' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'myComponent.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('my_component.ts');\n\t\t});\n\t});\n\n\tdescribe('to PascalCase', () => {\n\t\tit('should convert kebab-case filename to PascalCase', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'pascal' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('MyComponent.ts');\n\t\t});\n\n\t\tit('should convert camelCase filename to PascalCase', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'pascal' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'myComponent.tsx' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('MyComponent.tsx');\n\t\t});\n\t});\n\n\tdescribe('preserves file extension', () => {\n\t\tit('should preserve .ts extension', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-file.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myFile.ts');\n\t\t});\n\n\t\tit('should preserve .tsx extension', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-file.tsx' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myFile.tsx');\n\t\t});\n\n\t\tit('should preserve .js extension', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-file.js' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myFile.js');\n\t\t});\n\n\t\tit('should preserve .svelte extension', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'pascal' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-component.svelte' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('MyComponent.svelte');\n\t\t});\n\n\t\tit('should preserve .d.ts extension', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-types.d.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myTypes.d.ts');\n\t\t});\n\t});\n\n\tdescribe('handles filenames with directories', () => {\n\t\tit('should transform directory segments', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-components/use-hook.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myComponents/useHook.ts');\n\t\t});\n\n\t\tit('should handle deeply nested paths', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'src/my-components/ui/my-button.tsx' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('src/myComponents/ui/myButton.tsx');\n\t\t});\n\n\t\tit('should still return a new path when only directories change', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-components/index.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myComponents/index.ts');\n\t\t});\n\t});\n\n\tdescribe('no transformation when case matches target', () => {\n\t\tit('should return undefined fileName when already camelCase and target is camel', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'myComponent.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBeUndefined();\n\t\t});\n\n\t\tit('should return undefined fileName when already kebab-case and target is kebab', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'kebab' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBeUndefined();\n\t\t});\n\n\t\tit('should return undefined fileName when already PascalCase and target is pascal', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'pascal' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'MyComponent.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBeUndefined();\n\t\t});\n\n\t\tit('should return undefined fileName when already snake_case and target is snake', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'snake' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my_component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBeUndefined();\n\t\t});\n\t});\n\n\tdescribe('edge cases', () => {\n\t\tit('should throw error for invalid case type', () => {\n\t\t\t// @ts-expect-error - testing invalid input\n\t\t\texpect(() => fileCasing({ to: 'invalid' })).toThrow(\n\t\t\t\t'Invalid case type: \"invalid\". Expected one of: kebab, camel, snake, pascal'\n\t\t\t);\n\t\t});\n\n\t\tit('should handle multi-dot filenames (e.g., Angular-style my.component.tsx)', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-button.component.tsx' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\t// Only the first segment (before first dot) is transformed\n\t\t\texpect(result.fileName).toBe('myButton.component.tsx');\n\t\t});\n\n\t\tit('should handle single word filename', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBeUndefined();\n\t\t});\n\n\t\tit('should handle single word with PascalCase target', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'pascal' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'component.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('Component.ts');\n\t\t});\n\n\t\tit('should handle file with multiple dots in name', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-component.test.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myComponent.test.ts');\n\t\t});\n\n\t\tit('should handle index files', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'pascal' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'index.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('Index.ts');\n\t\t});\n\n\t\tit('should handle file without extension', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-file' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myFile');\n\t\t});\n\t});\n\n\tdescribe('transformDirectories option', () => {\n\t\tit('should default to true (transform directories)', async () => {\n\t\t\tconst plugin = fileCasing({ to: 'camel' });\n\t\t\tconst result = await plugin.transform({\n\t\t\t\tcode: 'const x = 1;',\n\t\t\t\tfileName: 'my-components/use-hook.ts' as ItemRelativePath,\n\t\t\t\toptions: transformOptions,\n\t\t\t});\n\t\t\texpect(result.fileName).toBe('myComponents/useHook.ts');\n\t\t});\n\n\t\tdescribe('transformDirectories: true', () => {\n\t\t\tit('should transform both directories and filename', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'camel', transformDirectories: true });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'my-components/use-hook.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('myComponents/useHook.ts');\n\t\t\t});\n\n\t\t\tit('should transform deeply nested directories', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'camel', transformDirectories: true });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'src/my-components/ui/my-button.tsx' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('src/myComponents/ui/myButton.tsx');\n\t\t\t});\n\n\t\t\tit('should transform directories even when filename matches target case', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'camel', transformDirectories: true });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'my-components/myComponent.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('myComponents/myComponent.ts');\n\t\t\t});\n\n\t\t\tit('should transform all directory segments, not just the first', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'pascal', transformDirectories: true });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'math/util/calculator.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('Math/Util/Calculator.ts');\n\t\t\t});\n\t\t});\n\n\t\tdescribe('transformDirectories: false', () => {\n\t\t\tit('should only transform filename, preserve directory names', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'camel', transformDirectories: false });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'my-components/use-hook.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('my-components/useHook.ts');\n\t\t\t});\n\n\t\t\tit('should preserve deeply nested directory names', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'camel', transformDirectories: false });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'src/my-components/ui/my-button.tsx' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('src/my-components/ui/myButton.tsx');\n\t\t\t});\n\n\t\t\tit('should still transform filename when directories are preserved', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'pascal', transformDirectories: false });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'components/my-button.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('components/MyButton.ts');\n\t\t\t});\n\n\t\t\tit('should work with files without directories', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'camel', transformDirectories: false });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'my-component.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('myComponent.ts');\n\t\t\t});\n\n\t\t\tit('should preserve directory names even if they match target case', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'kebab', transformDirectories: false });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'my-components/myComponent.ts' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('my-components/my-component.ts');\n\t\t\t});\n\n\t\t\tit('should preserve multiple directory segments', async () => {\n\t\t\t\tconst plugin = fileCasing({ to: 'snake', transformDirectories: false });\n\t\t\t\tconst result = await plugin.transform({\n\t\t\t\t\tcode: 'const x = 1;',\n\t\t\t\t\tfileName: 'src/components/ui/my-button.tsx' as ItemRelativePath,\n\t\t\t\t\toptions: transformOptions,\n\t\t\t\t});\n\t\t\t\texpect(result.fileName).toBe('src/components/ui/my_button.tsx');\n\t\t\t});\n\t\t});\n\t});\n});\n"
  },
  {
    "path": "packages/transform-filecasing/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/transform-filecasing/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/transform-javascript/CHANGELOG.md",
    "content": "# @jsrepo/transform-javascript\n\n## 7.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 6.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 6.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 6.0.0\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 5.0.2\n### Patch Changes\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 5.0.1\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 5.0.0\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 4.0.0\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 3.0.0\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 2.0.1\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 1.0.1\n### Patch Changes\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n\n## 1.0.0\n### Patch Changes\n\n- Updated dependencies [[`fcbb4b6`](https://github.com/jsrepojs/jsrepo/commit/fcbb4b69d684844a99f403e6880071302a176a27), [`43584f1`](https://github.com/jsrepojs/jsrepo/commit/43584f102a7e085195a4b738a56cb9a8aac65383)]:\n  - jsrepo@3.1.0\n\n## 0.0.12\n### Patch Changes\n\n- Updated dependencies [[`0bf74a4`](https://github.com/jsrepojs/jsrepo/commit/0bf74a448c0ff526aa353a0924646b3d647fd1bd), [`069cc97`](https://github.com/jsrepojs/jsrepo/commit/069cc979f6a4ecaac71cb5beece5c83860fbb915)]:\n  - jsrepo@3.0.11\n\n## 0.0.11\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n- Updated dependencies [[`07d0399`](https://github.com/jsrepojs/jsrepo/commit/07d039908e241f7bdcc084c5697a9534fd2e4788), [`2362f8e`](https://github.com/jsrepojs/jsrepo/commit/2362f8e90ad79ba6df0fe6cfa4ceb82362ee5b62)]:\n  - jsrepo@3.0.10\n\n## 0.0.10\n### Patch Changes\n\n- Updated dependencies [[`7d4e98a`](https://github.com/jsrepojs/jsrepo/commit/7d4e98a5e1588d430f5195181b30e59303e25efb)]:\n  - jsrepo@3.0.9\n\n## 0.0.9\n### Patch Changes\n\n- Updated dependencies [[`a704bd0`](https://github.com/jsrepojs/jsrepo/commit/a704bd09d678ff608c5c2e61c8b91f8c09717012)]:\n  - jsrepo@3.0.8\n\n## 0.0.8\n### Patch Changes\n\n- Updated dependencies [[`da29502`](https://github.com/jsrepojs/jsrepo/commit/da29502db16dccd2761b88f33e3a921ff7fd21ec)]:\n  - jsrepo@3.0.7\n\n## 0.0.7\n### Patch Changes\n\n- Updated dependencies [[`129abaf`](https://github.com/jsrepojs/jsrepo/commit/129abafd8b560e8240c6edb414ce3b74d4c7b3a5)]:\n  - jsrepo@3.0.6\n\n## 0.0.6\n### Patch Changes\n\n- Updated dependencies [[`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5)]:\n  - jsrepo@3.0.5\n\n## 0.0.5\n### Patch Changes\n\n- Updated dependencies [[`ed6cd82`](https://github.com/jsrepojs/jsrepo/commit/ed6cd82054a39a74513d39631d406dad829be9a9)]:\n  - jsrepo@3.0.4\n\n## 0.0.4\n### Patch Changes\n\n- Updated dependencies [[`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0)]:\n  - jsrepo@3.0.3\n\n## 0.0.3\n### Patch Changes\n\n- Updated dependencies [[`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59), [`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59)]:\n  - jsrepo@3.0.2\n\n## 0.0.2\n### Patch Changes\n\n- Updated dependencies [[`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad)]:\n  - jsrepo@3.0.1\n\n## 0.0.1\n### Patch Changes\n\n- Updated dependencies [[`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843)]:\n  - jsrepo@3.0.0\n\n## 0.0.1-beta.22\n### Patch Changes\n\n- Updated dependencies [[`9256a16`](https://github.com/jsrepojs/jsrepo/commit/9256a16d6ae360840097a7824dcf89743f7d9c69)]:\n  - jsrepo@3.0.0-beta.29\n\n## 0.0.1-beta.21\n### Patch Changes\n\n- Updated dependencies [[`db08900`](https://github.com/jsrepojs/jsrepo/commit/db08900353eac423d359b437362802c3f4b5b331)]:\n  - jsrepo@3.0.0-beta.28\n\n## 0.0.1-beta.20\n### Patch Changes\n\n- Updated dependencies [[`a5b3f2f`](https://github.com/jsrepojs/jsrepo/commit/a5b3f2f9c18539b7816af385c5b477cfdf991261)]:\n  - jsrepo@3.0.0-beta.27\n\n## 0.0.1-beta.19\n### Patch Changes\n\n- Updated dependencies [[`2d20f97`](https://github.com/jsrepojs/jsrepo/commit/2d20f97614e90bdc2b99e0347e13612687830466)]:\n  - jsrepo@3.0.0-beta.26\n\n## 0.0.1-beta.18\n### Patch Changes\n\n- Updated dependencies [[`44724ef`](https://github.com/jsrepojs/jsrepo/commit/44724ef4fbf15315b420328ee3dd5a158124fc94), [`dc4b4c9`](https://github.com/jsrepojs/jsrepo/commit/dc4b4c9892ede2e8f619b9af8af479fc0e5f72bc)]:\n  - jsrepo@3.0.0-beta.25\n\n## 0.0.1-beta.17\n### Patch Changes\n\n- Updated dependencies [[`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc), [`eb5f7c0`](https://github.com/jsrepojs/jsrepo/commit/eb5f7c03bf2d68ebcf1b72d9950aa2549acb0873), [`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc)]:\n  - jsrepo@3.0.0-beta.24\n\n## 0.0.1-beta.16\n### Patch Changes\n\n- Updated dependencies [[`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4), [`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4)]:\n  - jsrepo@3.0.0-beta.23\n\n## 0.0.1-beta.15\n### Patch Changes\n\n- Updated dependencies [[`01e6c72`](https://github.com/jsrepojs/jsrepo/commit/01e6c7261b29ab1d3a6b29b728fb4fc92e43e8ce)]:\n  - jsrepo@3.0.0-beta.22\n\n## 0.0.1-beta.14\n### Patch Changes\n\n- Updated dependencies [[`cbb1656`](https://github.com/jsrepojs/jsrepo/commit/cbb165612ec61d9e3e2c0277fbd3e7e684dc881e)]:\n  - jsrepo@3.0.0-beta.21\n\n## 0.0.1-beta.13\n### Patch Changes\n\n- Updated dependencies [[`b378550`](https://github.com/jsrepojs/jsrepo/commit/b378550b23ebc4df17770176b0ec74a6a69d2f18)]:\n  - jsrepo@3.0.0-beta.20\n\n## 0.0.1-beta.12\n### Patch Changes\n\n- Updated dependencies [[`86260de`](https://github.com/jsrepojs/jsrepo/commit/86260de53bdf690db170f97baa98c17e551d0d6d)]:\n  - jsrepo@3.0.0-beta.19\n\n## 0.0.1-beta.11\n### Patch Changes\n\n- Updated dependencies [[`fec54f8`](https://github.com/jsrepojs/jsrepo/commit/fec54f88e9a15f5c8d6bcc931c18c52758e1bcb6), [`dc9c06f`](https://github.com/jsrepojs/jsrepo/commit/dc9c06fa996a88d241378b31b5fd218dee592837)]:\n  - jsrepo@3.0.0-beta.18\n\n## 0.0.1-beta.10\n### Patch Changes\n\n- Updated dependencies [[`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563), [`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563)]:\n  - jsrepo@3.0.0-beta.17\n\n## 0.0.1-beta.9\n### Patch Changes\n\n- Updated dependencies [[`e2cfb54`](https://github.com/jsrepojs/jsrepo/commit/e2cfb54e2e7e8149d88ec2b370393ac1a9704e85)]:\n  - jsrepo@3.0.0-beta.16\n\n## 0.0.1-beta.8\n### Patch Changes\n\n- Updated dependencies [[`7541537`](https://github.com/jsrepojs/jsrepo/commit/754153741b409e92e867d306c2b0999a64f74b6d)]:\n  - jsrepo@3.0.0-beta.15\n\n## 0.0.1-beta.7\n### Patch Changes\n\n- Updated dependencies [[`dc0a14d`](https://github.com/jsrepojs/jsrepo/commit/dc0a14ddbf1da96705ad72e55f839675bc232dab)]:\n  - jsrepo@3.0.0-beta.14\n\n## 0.0.1-beta.6\n### Patch Changes\n\n- Updated dependencies [[`c168a33`](https://github.com/jsrepojs/jsrepo/commit/c168a33477541ff026a7247736a14c6dad4efa53), [`9f93eae`](https://github.com/jsrepojs/jsrepo/commit/9f93eae1cad08f934f4c50c45f5a70538c487a63)]:\n  - jsrepo@3.0.0-beta.13\n\n## 0.0.1-beta.5\n### Patch Changes\n\n- Updated dependencies [[`bef071f`](https://github.com/jsrepojs/jsrepo/commit/bef071fde5f7d8143346abdffd766ed8dec866f3)]:\n  - jsrepo@3.0.0-beta.12\n\n## 0.0.1-beta.4\n### Patch Changes\n\n- Updated dependencies [[`f9d2e1f`](https://github.com/jsrepojs/jsrepo/commit/f9d2e1f906f0d39b8c341c2aa678ac31ce0a2f3f)]:\n  - jsrepo@3.0.0-beta.11\n\n## 0.0.1-beta.3\n### Patch Changes\n\n- Updated dependencies [[`709a24d`](https://github.com/jsrepojs/jsrepo/commit/709a24d734ed712fd60add5f2b4b70243908e94b)]:\n  - jsrepo@3.0.0-beta.10\n\n## 0.0.1-beta.2\n### Patch Changes\n\n- Updated dependencies [[`4c664ed`](https://github.com/jsrepojs/jsrepo/commit/4c664ed08947f4a1906f80655b8fd12b7b6ec63b)]:\n  - jsrepo@3.0.0-beta.9\n\n## 0.0.1-beta.1\n### Patch Changes\n\n- Updated dependencies [[`0c33653`](https://github.com/jsrepojs/jsrepo/commit/0c336530ead46f05e85ece847a3dc76a51149c82)]:\n  - jsrepo@3.0.0-beta.8\n\n## 0.0.1-beta.0\n### Patch Changes\n\n- Updated dependencies [[`5e5759b`](https://github.com/jsrepojs/jsrepo/commit/5e5759be0e35f7b46664198ab3156b6998532c28)]:\n  - jsrepo@3.0.0-beta.7\n"
  },
  {
    "path": "packages/transform-javascript/README.md",
    "content": "# @jsrepo/transform-prettier\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/transform-javascript)](https://npmjs.com/package/@jsrepo/transform-javascript)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/transform-javascript)](https://npmjs.com/package/@jsrepo/transform-javascript)\n\nA transform plugin for transforming TypeScript registry items into JavaScript before adding them to your project.\n\n> [!NOTE]\n> You might consider using this alongside a formatting plugin like [@jsrepo/transform-prettier](https://npmjs.com/package/@jsrepo/transform-prettier) or [@jsrepo/transform-biome](https://npmjs.com/package/@jsrepo/transform-biome) to ensure the code is formatted correctly.\n\n## Usage\n\nRun the following command to install and add the transform to your config:\n\n```sh\njsrepo config transform javascript\n# or initialize any jsrepo project with the --js flag\njsrepo init @ieedan/std --js\n```\n\n### Manual Configuration\n\nInstall the transform plugin:\n\n```sh\npnpm install @jsrepo/transform-javascript -D\n```\n\nAdd the transform to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport javascript from \"@jsrepo/transform-javascript\";\n\nexport default defineConfig({\n\ttransforms: [javascript()],\n});\n```"
  },
  {
    "path": "packages/transform-javascript/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/transform-javascript/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/transform-javascript\",\n\t\"description\": \"A transform plugin for jsrepo to strip types from TypeScript code.\",\n\t\"version\": \"7.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"transform\",\n\t\t\"types\",\n\t\t\"javascript\",\n\t\t\"plugin\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"test\": \"vitest\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 1000000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@svecosystem/strip-types\": \"^0.0.4\",\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"sucrase\": \"^3.35.1\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\",\n\t\t\"vitest\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/transform-javascript/src/index.ts",
    "content": "import { strip } from '@svecosystem/strip-types';\nimport type { Transform } from 'jsrepo';\nimport { Unreachable } from 'jsrepo/errors';\nimport type { ItemRelativePath } from 'jsrepo/utils';\nimport { transform } from 'sucrase';\n\nexport type FileExtension = {\n\t/** The TypeScript extension. */\n\tts: string;\n\t/** The JavaScript extension. */\n\tjs?: string;\n};\n\nexport type Options = {\n\tsupportedExtensions?: FileExtension[];\n};\n\nexport const SUPPORTED_EXTENSIONS: FileExtension[] = [\n\t{ ts: 'ts', js: 'js' },\n\t{ ts: 'mts', js: 'mjs' },\n\t{ ts: 'tsx', js: 'jsx' },\n\t{ ts: 'svelte', js: 'svelte' },\n];\n\n/**\n * A transform plugin for jsrepo to strip types from TypeScript code. It also renames TypeScript files to JavaScript files.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import stripTypes from \"@jsrepo/transform-javascript\";\n *\n * export default defineConfig({\n *  // ...\n *  transforms: [stripTypes()],\n * });\n * ```\n *\n * @param options - The options for the transform plugin.\n */\nexport default function ({ supportedExtensions = SUPPORTED_EXTENSIONS }: Options = {}): Transform {\n\treturn {\n\t\ttransform: async ({ code, fileName }) => {\n\t\t\tif (\n\t\t\t\t!endsWithOneOf(\n\t\t\t\t\tfileName,\n\t\t\t\t\tsupportedExtensions.map((extension) => extension.ts)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn { code };\n\t\t\t}\n\n\t\t\tif (fileName.endsWith('.svelte')) {\n\t\t\t\treturn {\n\t\t\t\t\tcode: strip(code),\n\t\t\t\t\tfileName: updateFileExtension(fileName, supportedExtensions),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcode: transform(code, {\n\t\t\t\t\ttransforms: ['typescript', 'jsx'],\n\t\t\t\t\tdisableESTransforms: true,\n\t\t\t\t\tfilePath: fileName,\n\t\t\t\t\tjsxRuntime: 'preserve',\n\t\t\t\t}).code,\n\t\t\t\tfileName: updateFileExtension(fileName, supportedExtensions),\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction endsWithOneOf(fileName: string, extensions: string[]): boolean {\n\treturn extensions.some((extension) => fileName.endsWith(extension));\n}\n\nfunction updateFileExtension(\n\tfileName: ItemRelativePath,\n\textensions: FileExtension[]\n): ItemRelativePath {\n\tfor (const extension of extensions) {\n\t\tif (fileName.endsWith(extension.ts)) {\n\t\t\treturn fileName.replace(\n\t\t\t\t`.${extension.ts}`,\n\t\t\t\t`.${extension.js ?? extension.ts}`\n\t\t\t) as ItemRelativePath;\n\t\t}\n\t}\n\tthrow new Unreachable();\n}\n"
  },
  {
    "path": "packages/transform-javascript/tests/strip-types.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport stripTypes from '../src';\n\ndescribe('stripTypes', () => {\n\tit('should strip types from TypeScript code and rename the file', async () => {\n\t\tconst plugin = stripTypes();\n\t\tconst code = 'const a: string = \"hello\";';\n\t\tconst result = await plugin.transform({\n\t\t\tcode,\n\t\t\tfileName: 'test.ts',\n\t\t\toptions: {\n\t\t\t\tcwd: '',\n\t\t\t\titem: { name: 'test', type: 'component' },\n\t\t\t\tregistryUrl: 'https://example.com',\n\t\t\t},\n\t\t});\n\t\texpect(result.code).toBe('const a = \"hello\";');\n\t\texpect(result.fileName).toBe('test.js');\n\t});\n\n\tit('should not strip types from non-TypeScript files', async () => {\n\t\tconst plugin = stripTypes();\n\t\tconst code = 'const a: string = \"hello\";';\n\t\tconst result = await plugin.transform({\n\t\t\tcode,\n\t\t\tfileName: 'test.js',\n\t\t\toptions: {\n\t\t\t\tcwd: '',\n\t\t\t\titem: { name: 'test', type: 'component' },\n\t\t\t\tregistryUrl: 'https://example.com',\n\t\t\t},\n\t\t});\n\t\texpect(result.code).toBe(code);\n\t\texpect(result.fileName).toBe(undefined);\n\t});\n\n\tit('should allow for custom supported extensions', async () => {\n\t\tconst plugin = stripTypes({ supportedExtensions: [{ ts: 'rts', js: 'rjs' }] });\n\t\tconst code = 'const a: string = \"hello\";';\n\t\tconst result = await plugin.transform({\n\t\t\tcode,\n\t\t\tfileName: 'test.rts',\n\t\t\toptions: {\n\t\t\t\tcwd: '',\n\t\t\t\titem: { name: 'test', type: 'component' },\n\t\t\t\tregistryUrl: 'https://example.com',\n\t\t\t},\n\t\t});\n\t\texpect(result.code).toBe('const a = \"hello\";');\n\t\texpect(result.fileName).toBe('test.rjs');\n\t});\n});\n"
  },
  {
    "path": "packages/transform-javascript/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/transform-javascript/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/transform-oxfmt/CHANGELOG.md",
    "content": "# @jsrepo/transform-oxfmt\n\n## 7.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 6.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 6.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 6.0.0\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 5.0.2\n### Patch Changes\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 5.0.1\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 5.0.0\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 4.0.0\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 3.0.0\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 2.0.1\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 1.0.1\n### Patch Changes\n\n\n- feat: 🎉 New plugin @jsrepo/transform-oxfmt for formatting code with oxfmt ([#743](https://github.com/jsrepojs/jsrepo/pull/743))\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n"
  },
  {
    "path": "packages/transform-oxfmt/README.md",
    "content": "# @jsrepo/transform-oxfmt\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/transform-oxfmt)](https://npmjs.com/package/@jsrepo/transform-oxfmt)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/transform-oxfmt)](https://npmjs.com/package/@jsrepo/transform-oxfmt)\n\nA transform plugin for formatting registry items with oxfmt before they are added to your project.\n\n## Usage\n\nRun the following command to install and add the transform to your config:\n\n```sh\njsrepo config transform @jsrepo/transform-oxfmt\n```\n\n### Manual Configuration\n\nInstall the transform plugin:\n\n```sh\npnpm install @jsrepo/transform-oxfmt -D\n```\n\nAdd the transform to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport oxfmt from \"@jsrepo/transform-oxfmt\";\n\nexport default defineConfig({\n\ttransforms: [oxfmt()],\n});\n```\n\n### Options\n\nThe transform accepts oxfmt's `FormatOptions` to customize formatting behavior:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport oxfmt from \"@jsrepo/transform-oxfmt\";\n\nexport default defineConfig({\n\ttransforms: [oxfmt({ semi: false })],\n});\n```\n"
  },
  {
    "path": "packages/transform-oxfmt/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/transform-oxfmt/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/transform-oxfmt\",\n\t\"description\": \"A transform plugin for jsrepo to format code with oxfmt.\",\n\t\"version\": \"7.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aleksei Gurianov\",\n\t\t\"url\": \"https://github.com/guria\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"transform\",\n\t\t\"oxfmt\",\n\t\t\"plugin\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\",\n\t\t\"oxfmt\": \"0.x\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/transform-oxfmt/src/index.ts",
    "content": "import type { Transform } from 'jsrepo';\nimport { type FormatOptions, format } from 'oxfmt';\n\n/**\n * A transform plugin for jsrepo to format code with oxfmt.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import oxfmt from \"@jsrepo/transform-oxfmt\";\n *\n * export default defineConfig({\n *  // ...\n *  transforms: [oxfmt()],\n * });\n * ```\n *\n * @param options - The options for the transform plugin.\n */\nexport default function (options: FormatOptions = {}): Transform {\n\treturn {\n\t\ttransform: async ({ code, fileName }) => {\n\t\t\treturn { code: await tryFormat(fileName, code, options) };\n\t\t},\n\t};\n}\n\nasync function tryFormat(\n\tfileName: string,\n\tcode: string,\n\toptions: FormatOptions\n): Promise<string | undefined> {\n\ttry {\n\t\tconst result = await format(fileName, code, options);\n\t\tif (result.errors.length > 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn result.code;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"
  },
  {
    "path": "packages/transform-oxfmt/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/transform-oxfmt/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "packages/transform-prettier/CHANGELOG.md",
    "content": "# @jsrepo/transform-prettier\n\n## 7.0.0\n### Patch Changes\n\n- Updated dependencies [[`84c436e`](https://github.com/jsrepojs/jsrepo/commit/84c436ea2a98ededf10e0c13ab2fc882ed6f632b)]:\n  - jsrepo@3.7.0\n\n## 6.0.2\n### Patch Changes\n\n- Updated dependencies [[`a28e22c`](https://github.com/jsrepojs/jsrepo/commit/a28e22c8b4dc36ee82e0c2714d11c4f6fb448f48)]:\n  - jsrepo@3.6.2\n\n## 6.0.1\n### Patch Changes\n\n- Updated dependencies [[`3dbbb34`](https://github.com/jsrepojs/jsrepo/commit/3dbbb34ab81f8a66eda4615b90c43784363daaf6)]:\n  - jsrepo@3.6.1\n\n## 6.0.0\n### Patch Changes\n\n- Updated dependencies [[`733d152`](https://github.com/jsrepojs/jsrepo/commit/733d152373571372599b7a9ddeaa5e5c9adef013)]:\n  - jsrepo@3.6.0\n\n## 5.0.2\n### Patch Changes\n\n- Updated dependencies [[`d591b14`](https://github.com/jsrepojs/jsrepo/commit/d591b14231af6d9cd8109343ba6617faccdeb010)]:\n  - jsrepo@3.5.2\n\n## 5.0.1\n### Patch Changes\n\n- Updated dependencies [[`58869f7`](https://github.com/jsrepojs/jsrepo/commit/58869f7a85be9ab45b1dbc88ff983625569b9f70)]:\n  - jsrepo@3.5.1\n\n## 5.0.0\n### Patch Changes\n\n- Updated dependencies [[`ac14e0d`](https://github.com/jsrepojs/jsrepo/commit/ac14e0d3248b4e73152fbdcae847dce96d4f05f2), [`c7f7703`](https://github.com/jsrepojs/jsrepo/commit/c7f770317f75406db6da84d7504c988b615af359)]:\n  - jsrepo@3.5.0\n\n## 4.0.0\n### Patch Changes\n\n- Updated dependencies [[`e620ca9`](https://github.com/jsrepojs/jsrepo/commit/e620ca9839ff4256fc03981b824a564b35ef5e63)]:\n  - jsrepo@3.4.0\n\n## 3.0.0\n### Patch Changes\n\n- Updated dependencies [[`9b396cb`](https://github.com/jsrepojs/jsrepo/commit/9b396cb22f4c96dde53f2f5f2efa295ff9c9eada)]:\n  - jsrepo@3.3.0\n\n## 2.0.1\n### Patch Changes\n\n\n- chore: bump deps ([#751](https://github.com/jsrepojs/jsrepo/pull/751))\n\n- Updated dependencies [[`e8e4289`](https://github.com/jsrepojs/jsrepo/commit/e8e4289d1045bc1a9cb89c6c55efd7108583ff01)]:\n  - jsrepo@3.2.1\n\n## 2.0.0\n### Patch Changes\n\n- Updated dependencies [[`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43), [`7f56df2`](https://github.com/jsrepojs/jsrepo/commit/7f56df2b9837104ff2acd7bc4051731b6143fa43)]:\n  - jsrepo@3.2.0\n\n## 1.0.1\n### Patch Changes\n\n- Updated dependencies [[`d249eff`](https://github.com/jsrepojs/jsrepo/commit/d249eff3d9f0a6b3fc557351568fc7e9f60f12c3)]:\n  - jsrepo@3.1.1\n\n## 1.0.0\n### Patch Changes\n\n- Updated dependencies [[`fcbb4b6`](https://github.com/jsrepojs/jsrepo/commit/fcbb4b69d684844a99f403e6880071302a176a27), [`43584f1`](https://github.com/jsrepojs/jsrepo/commit/43584f102a7e085195a4b738a56cb9a8aac65383)]:\n  - jsrepo@3.1.0\n\n## 0.0.12\n### Patch Changes\n\n- Updated dependencies [[`0bf74a4`](https://github.com/jsrepojs/jsrepo/commit/0bf74a448c0ff526aa353a0924646b3d647fd1bd), [`069cc97`](https://github.com/jsrepojs/jsrepo/commit/069cc979f6a4ecaac71cb5beece5c83860fbb915)]:\n  - jsrepo@3.0.11\n\n## 0.0.11\n### Patch Changes\n\n\n- chore: bump deps ([#730](https://github.com/jsrepojs/jsrepo/pull/730))\n\n- Updated dependencies [[`07d0399`](https://github.com/jsrepojs/jsrepo/commit/07d039908e241f7bdcc084c5697a9534fd2e4788), [`2362f8e`](https://github.com/jsrepojs/jsrepo/commit/2362f8e90ad79ba6df0fe6cfa4ceb82362ee5b62)]:\n  - jsrepo@3.0.10\n\n## 0.0.10\n### Patch Changes\n\n- Updated dependencies [[`7d4e98a`](https://github.com/jsrepojs/jsrepo/commit/7d4e98a5e1588d430f5195181b30e59303e25efb)]:\n  - jsrepo@3.0.9\n\n## 0.0.9\n### Patch Changes\n\n- Updated dependencies [[`a704bd0`](https://github.com/jsrepojs/jsrepo/commit/a704bd09d678ff608c5c2e61c8b91f8c09717012)]:\n  - jsrepo@3.0.8\n\n## 0.0.8\n### Patch Changes\n\n- Updated dependencies [[`da29502`](https://github.com/jsrepojs/jsrepo/commit/da29502db16dccd2761b88f33e3a921ff7fd21ec)]:\n  - jsrepo@3.0.7\n\n## 0.0.7\n### Patch Changes\n\n- Updated dependencies [[`129abaf`](https://github.com/jsrepojs/jsrepo/commit/129abafd8b560e8240c6edb414ce3b74d4c7b3a5)]:\n  - jsrepo@3.0.6\n\n## 0.0.6\n### Patch Changes\n\n- Updated dependencies [[`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5), [`211c93a`](https://github.com/jsrepojs/jsrepo/commit/211c93a0eea809002575be90229f6542f2696cb5)]:\n  - jsrepo@3.0.5\n\n## 0.0.5\n### Patch Changes\n\n- Updated dependencies [[`ed6cd82`](https://github.com/jsrepojs/jsrepo/commit/ed6cd82054a39a74513d39631d406dad829be9a9)]:\n  - jsrepo@3.0.4\n\n## 0.0.4\n### Patch Changes\n\n- Updated dependencies [[`ccbab08`](https://github.com/jsrepojs/jsrepo/commit/ccbab0840fc4fdf103e1cbab0d7edd9a18c43cc0)]:\n  - jsrepo@3.0.3\n\n## 0.0.3\n### Patch Changes\n\n- Updated dependencies [[`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59), [`ca63e1b`](https://github.com/jsrepojs/jsrepo/commit/ca63e1b07bab114b9ad9c998bd3d97403e12bc59)]:\n  - jsrepo@3.0.2\n\n## 0.0.2\n### Patch Changes\n\n- Updated dependencies [[`218b395`](https://github.com/jsrepojs/jsrepo/commit/218b39550e8a338bb4b792b1384a018563da8dad)]:\n  - jsrepo@3.0.1\n\n## 0.0.1\n### Patch Changes\n\n\n- initial beta release ([#634](https://github.com/jsrepojs/jsrepo/pull/634))\n\n- Updated dependencies [[`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843), [`183a68e`](https://github.com/jsrepojs/jsrepo/commit/183a68e2f162d1e867a6db5d79fbfea8b6be2843)]:\n  - jsrepo@3.0.0\n\n## 0.0.1-beta.29\n### Patch Changes\n\n- Updated dependencies [[`9256a16`](https://github.com/jsrepojs/jsrepo/commit/9256a16d6ae360840097a7824dcf89743f7d9c69)]:\n  - jsrepo@3.0.0-beta.29\n\n## 0.0.1-beta.28\n### Patch Changes\n\n- Updated dependencies [[`db08900`](https://github.com/jsrepojs/jsrepo/commit/db08900353eac423d359b437362802c3f4b5b331)]:\n  - jsrepo@3.0.0-beta.28\n\n## 0.0.1-beta.27\n### Patch Changes\n\n- Updated dependencies [[`a5b3f2f`](https://github.com/jsrepojs/jsrepo/commit/a5b3f2f9c18539b7816af385c5b477cfdf991261)]:\n  - jsrepo@3.0.0-beta.27\n\n## 0.0.1-beta.26\n### Patch Changes\n\n- Updated dependencies [[`2d20f97`](https://github.com/jsrepojs/jsrepo/commit/2d20f97614e90bdc2b99e0347e13612687830466)]:\n  - jsrepo@3.0.0-beta.26\n\n## 0.0.1-beta.25\n### Patch Changes\n\n- Updated dependencies [[`44724ef`](https://github.com/jsrepojs/jsrepo/commit/44724ef4fbf15315b420328ee3dd5a158124fc94), [`dc4b4c9`](https://github.com/jsrepojs/jsrepo/commit/dc4b4c9892ede2e8f619b9af8af479fc0e5f72bc)]:\n  - jsrepo@3.0.0-beta.25\n\n## 0.0.1-beta.24\n### Patch Changes\n\n- Updated dependencies [[`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc), [`eb5f7c0`](https://github.com/jsrepojs/jsrepo/commit/eb5f7c03bf2d68ebcf1b72d9950aa2549acb0873), [`1153a29`](https://github.com/jsrepojs/jsrepo/commit/1153a2988550f1376f3772046f504d18563439bc)]:\n  - jsrepo@3.0.0-beta.24\n\n## 0.0.1-beta.23\n### Patch Changes\n\n- Updated dependencies [[`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4), [`0d84cfd`](https://github.com/jsrepojs/jsrepo/commit/0d84cfd58270b4001abf44e1496634687499abe4)]:\n  - jsrepo@3.0.0-beta.23\n\n## 0.0.1-beta.22\n### Patch Changes\n\n- Updated dependencies [[`01e6c72`](https://github.com/jsrepojs/jsrepo/commit/01e6c7261b29ab1d3a6b29b728fb4fc92e43e8ce)]:\n  - jsrepo@3.0.0-beta.22\n\n## 0.0.1-beta.21\n### Patch Changes\n\n- Updated dependencies [[`cbb1656`](https://github.com/jsrepojs/jsrepo/commit/cbb165612ec61d9e3e2c0277fbd3e7e684dc881e)]:\n  - jsrepo@3.0.0-beta.21\n\n## 0.0.1-beta.20\n### Patch Changes\n\n- Updated dependencies [[`b378550`](https://github.com/jsrepojs/jsrepo/commit/b378550b23ebc4df17770176b0ec74a6a69d2f18)]:\n  - jsrepo@3.0.0-beta.20\n\n## 0.0.1-beta.19\n### Patch Changes\n\n- Updated dependencies [[`86260de`](https://github.com/jsrepojs/jsrepo/commit/86260de53bdf690db170f97baa98c17e551d0d6d)]:\n  - jsrepo@3.0.0-beta.19\n\n## 0.0.1-beta.18\n### Patch Changes\n\n- Updated dependencies [[`fec54f8`](https://github.com/jsrepojs/jsrepo/commit/fec54f88e9a15f5c8d6bcc931c18c52758e1bcb6), [`dc9c06f`](https://github.com/jsrepojs/jsrepo/commit/dc9c06fa996a88d241378b31b5fd218dee592837)]:\n  - jsrepo@3.0.0-beta.18\n\n## 0.0.1-beta.17\n### Patch Changes\n\n- Updated dependencies [[`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563), [`a26528c`](https://github.com/jsrepojs/jsrepo/commit/a26528c3eba37e79724d06edefc2349d0b5d2563)]:\n  - jsrepo@3.0.0-beta.17\n\n## 0.0.1-beta.16\n### Patch Changes\n\n- Updated dependencies [[`e2cfb54`](https://github.com/jsrepojs/jsrepo/commit/e2cfb54e2e7e8149d88ec2b370393ac1a9704e85)]:\n  - jsrepo@3.0.0-beta.16\n\n## 0.0.1-beta.15\n### Patch Changes\n\n- Updated dependencies [[`7541537`](https://github.com/jsrepojs/jsrepo/commit/754153741b409e92e867d306c2b0999a64f74b6d)]:\n  - jsrepo@3.0.0-beta.15\n\n## 0.0.1-beta.14\n### Patch Changes\n\n- Updated dependencies [[`dc0a14d`](https://github.com/jsrepojs/jsrepo/commit/dc0a14ddbf1da96705ad72e55f839675bc232dab)]:\n  - jsrepo@3.0.0-beta.14\n\n## 0.0.1-beta.13\n### Patch Changes\n\n- Updated dependencies [[`c168a33`](https://github.com/jsrepojs/jsrepo/commit/c168a33477541ff026a7247736a14c6dad4efa53), [`9f93eae`](https://github.com/jsrepojs/jsrepo/commit/9f93eae1cad08f934f4c50c45f5a70538c487a63)]:\n  - jsrepo@3.0.0-beta.13\n\n## 0.0.1-beta.12\n### Patch Changes\n\n- Updated dependencies [[`bef071f`](https://github.com/jsrepojs/jsrepo/commit/bef071fde5f7d8143346abdffd766ed8dec866f3)]:\n  - jsrepo@3.0.0-beta.12\n\n## 0.0.1-beta.11\n### Patch Changes\n\n- Updated dependencies [[`f9d2e1f`](https://github.com/jsrepojs/jsrepo/commit/f9d2e1f906f0d39b8c341c2aa678ac31ce0a2f3f)]:\n  - jsrepo@3.0.0-beta.11\n\n## 0.0.1-beta.10\n### Patch Changes\n\n- Updated dependencies [[`709a24d`](https://github.com/jsrepojs/jsrepo/commit/709a24d734ed712fd60add5f2b4b70243908e94b)]:\n  - jsrepo@3.0.0-beta.10\n\n## 0.0.1-beta.9\n### Patch Changes\n\n- Updated dependencies [[`4c664ed`](https://github.com/jsrepojs/jsrepo/commit/4c664ed08947f4a1906f80655b8fd12b7b6ec63b)]:\n  - jsrepo@3.0.0-beta.9\n\n## 0.0.1-beta.8\n### Patch Changes\n\n- Updated dependencies [[`0c33653`](https://github.com/jsrepojs/jsrepo/commit/0c336530ead46f05e85ece847a3dc76a51149c82)]:\n  - jsrepo@3.0.0-beta.8\n\n## 0.0.1-beta.7\n### Patch Changes\n\n- Updated dependencies [[`5e5759b`](https://github.com/jsrepojs/jsrepo/commit/5e5759be0e35f7b46664198ab3156b6998532c28)]:\n  - jsrepo@3.0.0-beta.7\n\n## 0.0.1-beta.6\n### Patch Changes\n\n- Updated dependencies [[`bfaabbb`](https://github.com/jsrepojs/jsrepo/commit/bfaabbb7ae29e0b511e9e84f61fdfb922af6b413)]:\n  - jsrepo@3.0.0-beta.6\n\n## 0.0.1-beta.5\n### Patch Changes\n\n- Updated dependencies [[`95fd7da`](https://github.com/jsrepojs/jsrepo/commit/95fd7da66287c6595ad1fd3de25664719aa6c9b4)]:\n  - jsrepo@3.0.0-beta.5\n\n## 0.0.1-beta.4\n### Patch Changes\n\n- Updated dependencies [[`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a), [`6a96746`](https://github.com/jsrepojs/jsrepo/commit/6a96746cde82a677434d554ea9b51cc8ac76c30a)]:\n  - jsrepo@3.0.0-beta.4\n\n## 0.0.1-beta.3\n### Patch Changes\n\n- Updated dependencies [[`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857), [`17c4338`](https://github.com/jsrepojs/jsrepo/commit/17c4338304da5661e9a8587c9f82401464693857)]:\n  - jsrepo@3.0.0-beta.3\n\n## 0.0.1-beta.2\n### Patch Changes\n\n- Updated dependencies [[`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4), [`ab13bc5`](https://github.com/jsrepojs/jsrepo/commit/ab13bc5e460964f377b7914e7e8bf815b323ccd4)]:\n  - jsrepo@3.0.0-beta.2\n\n## 0.0.1-beta.1\n### Patch Changes\n\n- Updated dependencies [[`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2452f64`](https://github.com/jsrepojs/jsrepo/commit/2452f648b1a2be2fc636adb7b161a42a139f1e74), [`2b9e4be`](https://github.com/jsrepojs/jsrepo/commit/2b9e4be45964bf2cde16f36515bf760e3ddf66a7)]:\n  - jsrepo@3.0.0-beta.1\n\n## 0.0.1-beta.0\n### Patch Changes\n\n\n- initial beta release ([#635](https://github.com/jsrepojs/jsrepo/pull/635))\n\n- Updated dependencies [[`4416209`](https://github.com/jsrepojs/jsrepo/commit/44162092b38171d68817f4d0598f3ec8ddcc795d)]:\n  - jsrepo@3.0.0-beta.0\n"
  },
  {
    "path": "packages/transform-prettier/README.md",
    "content": "# @jsrepo/transform-prettier\n\n[![npm version](https://flat.badgen.net/npm/v/@jsrepo/transform-prettier)](https://npmjs.com/package/@jsrepo/transform-prettier)\n[![npm downloads](https://flat.badgen.net/npm/dm/@jsrepo/transform-prettier)](https://npmjs.com/package/@jsrepo/transform-prettier)\n\nA transform plugin for formatting registry items with Prettier using your local Prettier configuration before they are added to your project.\n\n## Usage\n\nRun the following command to install and add the transform to your config:\n\n```sh\njsrepo config transform @jsrepo/transform-prettier\n```\n\n### Manual Configuration\n\nInstall the transform plugin:\n\n```sh\npnpm install @jsrepo/transform-prettier -D\n```\n\nAdd the transform to your config:\n\n```ts\nimport { defineConfig } from \"jsrepo\";\nimport prettier from \"@jsrepo/transform-prettier\";\n\nexport default defineConfig({\n\ttransforms: [prettier()],\n});\n```"
  },
  {
    "path": "packages/transform-prettier/biome.json",
    "content": "{\n\t\"root\": false,\n\t\"extends\": \"//\",\n\t\"files\": {\n\t\t\"includes\": [\"!dist/**/*\"]\n\t}\n}\n"
  },
  {
    "path": "packages/transform-prettier/package.json",
    "content": "{\n\t\"name\": \"@jsrepo/transform-prettier\",\n\t\"description\": \"A transform plugin for jsrepo to format code with prettier.\",\n\t\"version\": \"7.0.0\",\n\t\"license\": \"MIT\",\n\t\"homepage\": \"https://jsrepo.dev\",\n\t\"author\": {\n\t\t\"name\": \"Aidan Bleser\",\n\t\t\"url\": \"https://github.com/ieedan\"\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/jsrepojs/jsrepo\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/jsrepojs/jsrepo/issues\"\n\t},\n\t\"keywords\": [\n\t\t\"jsrepo\",\n\t\t\"transform\",\n\t\t\"prettier\",\n\t\t\"plugin\"\n\t],\n\t\"type\": \"module\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.mts\",\n\t\t\t\"default\": \"./dist/index.mjs\"\n\t\t}\n\t},\n\t\"main\": \"./dist/index.mjs\",\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"!dist/**/*.map\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"dev\": \"tsdown --watch\",\n\t\t\"check\": \"tsc --noEmit\",\n\t\t\"bundle-analyze\": \"FORCE_COLOR=1 pnpx bundle-analyze . --fail-if-exceeds-bytes 500000\"\n\t},\n\t\"packageManager\": \"pnpm@10.28.2\",\n\t\"peerDependencies\": {\n\t\t\"jsrepo\": \"workspace:*\",\n\t\t\"prettier\": \"3.x\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@types/node\": \"catalog:\",\n\t\t\"tsdown\": \"catalog:\",\n\t\t\"typescript\": \"catalog:\"\n\t}\n}\n"
  },
  {
    "path": "packages/transform-prettier/src/index.ts",
    "content": "import path from 'node:path';\nimport type { Transform } from 'jsrepo';\nimport { type Config, format, resolveConfig } from 'prettier';\n\nexport type Options = {\n\t/** The name of the prettier config file to use. @default \".prettierrc\" */\n\tconfigFile?: string;\n};\n\n/**\n * A transform plugin for jsrepo to format code with prettier.\n * @example\n * ```ts\n * import { defineConfig } from \"jsrepo\";\n * import prettier from \"@jsrepo/transform-prettier\";\n *\n * export default defineConfig({\n *  // ...\n *  transforms: [prettier()],\n * });\n * ```\n *\n * @param options - The options for the transform plugin.\n */\nexport default function (options: Options = {}): Transform {\n\tconst configPromise = resolveConfig(\n\t\tpath.join(process.cwd(), options.configFile ?? '.prettierrc')\n\t);\n\n\treturn {\n\t\ttransform: async ({ code, fileName }) => {\n\t\t\tconst config = await configPromise;\n\n\t\t\treturn { code: await tryFormat(code, { fileName: fileName, config }) };\n\t\t},\n\t};\n}\n\nfunction tryFormat(\n\tcode: string,\n\t{ fileName, config }: { fileName: string; config: Config | null }\n) {\n\ttry {\n\t\treturn format(code, { ...config, filepath: fileName });\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"
  },
  {
    "path": "packages/transform-prettier/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"isolatedModules\": true,\n\t\t\"moduleResolution\": \"Bundler\",\n\t\t\"module\": \"ES2022\",\n\t\t\"target\": \"ES2022\",\n\t\t\"skipLibCheck\": true,\n\t\t\"strict\": true,\n\t\t\"noEmit\": true,\n\t\t\"noUncheckedIndexedAccess\": true\n\t},\n\t\"include\": [\"src/**/*.ts\"]\n}\n"
  },
  {
    "path": "packages/transform-prettier/tsdown.config.ts",
    "content": "import { defineConfig } from 'tsdown';\n\nexport default defineConfig({\n\tentry: ['src/index.ts'],\n\tformat: ['esm'],\n\tminify: true,\n\tdts: {\n\t\tsourcemap: true,\n\t},\n});\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - \"packages/*\"\n  - \"examples/*\"\n  - \"apps/*\"\n\ncatalog:\n  vitest: ^4.0.18\n  \"@types/node\": ^25.3.0\n  zod: ^4.3.6\n  tsdown: 0.19.0-beta.3\n  picocolors: ^1.1.1\n  typescript: ^5.9.3\n  concurrently: ^9.2.1\n  commander: ^14.0.3\n  dedent: ^1.7.1\n"
  },
  {
    "path": "task.config.json",
    "content": "{\n\t\"$schema\": \"https://getbizi.dev/schemas/task.config.json\",\n\t\"tasks\": {\n\t\t\"dev\": {\n\t\t\t\"tasks\": {\n\t\t\t\t\"packages\": {\n\t\t\t\t\t\"color\": \"blue\",\n\t\t\t\t\t\"command\": \"pnpm dev:packages\"\n\t\t\t\t},\n\t\t\t\t\"apps\": {\n\t\t\t\t\t\"color\": \"green\",\n\t\t\t\t\t\"command\": \"pnpm wait-on packages/jsrepo/dist/api/index.mjs && pnpm dev:apps\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"format\": {\n\t\t\t\"command\": \"pnpm format\"\n\t\t},\n\t\t\"lint\": {\n\t\t\t\"command\": \"pnpm lint\"\n\t\t},\n\t\t\"check\": {\n\t\t\t\"command\": \"pnpm check\"\n\t\t},\n\t\t\"test\": {\n\t\t\t\"command\": \"pnpm test\"\n\t\t}\n\t}\n}\n"
  }
]