[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: manchenkoff\nthanks_dev: manchenkoff\nbuy_me_a_coffee: manchenkoff\ntidelift: npm/nuxt-auth-sanctum\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "content": "name: Bug Report\ndescription: Report a bug or incorrect behaviour\ntitle: \"[bug] name\"\nlabels: bug\nassignees: manchenkoff\nbody:\n  - id: problem_statement\n    type: textarea\n    attributes:\n      label: Describe the bug\n      description: Provide a clear and concise description of the bug\n      placeholder: The feature X might be broken when I do Y\n    validations:\n      required: true\n  - id: expected_behaviour\n    type: textarea\n    attributes:\n      label: Expected behaviour\n      description: Provide a clear description of what you expected to happen\n      placeholder: When I do X, I expect Y to happen\n    validations:\n      required: true\n  - id: actual_behaviour\n    type: textarea\n    attributes:\n      label: Actual behaviour\n      description: Provide a concise description of what actually happened\n      placeholder: When I do X, Y happens instead\n    validations:\n      required: true\n  - id: runtime\n    type: dropdown\n    attributes:\n      label: Runtime\n      description: Choose affected runtimes\n      options:\n        - Client side (CSR)\n        - Server side (SSR)\n        - Both\n      default: 0\n    validations:\n      required: true\n  - id: environment\n    type: dropdown\n    attributes:\n      label: Environment\n      description: Choose affected environments\n      options:\n        - Development (local)\n        - Production\n        - Both\n      default: 1\n    validations:\n      required: true\n  - id: reproduction\n    type: textarea\n    attributes:\n      label: Reproduction steps\n      description: |\n        Please explain how to reproduce the bug,\n        ideally with a minimal code example or repository\n      placeholder: |\n        1. Do X\n        2. Do Y\n        3. See Z happening\n    validations:\n      required: false\n  - id: nuxt_version\n    type: input\n    attributes:\n      label: Nuxt version\n      description: Version of the Nuxt framework\n      placeholder: 4.2.1\n    validations:\n      required: true\n  - id: module_version\n    type: input\n    attributes:\n      label: Module version\n      description: Version of the module installed\n      placeholder: 1.0.1\n    validations:\n      required: true\n  - id: config_example\n    type: textarea\n    attributes:\n      label: Module config\n      description: Provide your `nuxt.config.ts` contents\n      render: typescript\n    validations:\n      required: false\n  - type: upload\n    id: file_upload\n    attributes:\n      label: File Upload\n      description: Attach a file to this issue (screenshot, log file, etc.)\n    validations:\n      required: false\n  - id: investigation\n    type: checkboxes\n    attributes:\n      label: Initial investigation\n      options:\n        - label: I have checked the documentation, no solution found\n          required: true\n        - label: I have checked the troubleshooting guide, did not help\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.yml",
    "content": "name: Feature Request\ndescription: Suggest an idea for this project\ntitle: \"[feature] name\"\nlabels: enhancement\nassignees: manchenkoff\nbody:\n  - id: feature_description\n    type: textarea\n    attributes:\n      label: Describe the feature\n      description: Provide a detailed description of the functionality\n      value: |\n        It would be nice to have a way to ...\n    validations:\n      required: true\n  - id: proposed_solution\n    type: textarea\n    attributes:\n      label: Proposed solution\n      description: Describe the solution you would like to see\n      placeholder: I think this can be implemented in the way...\n    validations:\n      required: false\n  - id: alternative_solution\n    type: textarea\n    attributes:\n      label: Alternatives\n      description: Describe alternatives you have considered\n      placeholder: Another option would be...\n    validations:\n      required: false\n  - id: ready_to_contribute\n    type: checkboxes\n    attributes:\n      label: Do you want to contribute?\n      description: |\n        Check this box if you are happy to implement this yourself\n        once we have a plan\n      options:\n        - label: \"Yes\"\n          required: false\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"npm\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "**Describe the problem and solution**\n\nCloses #XXX.\n\nREPLACE_THIS_WITH_YOUR_DESCRIPTION\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Nuxt [Docs]\nenv:\n  node_version: 24\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: false\non:\n  push:\n    branches: [\"main\"]\n    paths: [\"docs/**\"]\n  workflow_dispatch:\npermissions:\n  contents: read\n  pages: write\n  id-token: write\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          cache: pnpm\n      - name: Install dependencies\n        working-directory: ./docs\n        run: pnpm install\n      - name: Validate\n        working-directory: ./docs\n        run: pnpm lint\n      - name: Generate documentation\n        run: pnpm generate\n        working-directory: ./docs\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@v4\n        with:\n          path: ./docs/.output/public\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    needs: build\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v5\n"
  },
  {
    "path": ".github/workflows/prerelease.yml",
    "content": "name: Nuxt [Pre-Release]\nenv:\n  node_version: 24\n  node_registry: https://registry.npmjs.org/\n  changelog_user: Github CI\n  changelog_email: artem@manchenkoff.me\n  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\nconcurrency:\n  group: nuxt-auth-sanctum-prerelease\n  cancel-in-progress: false\npermissions:\n  contents: write\n  id-token: write\non: workflow_dispatch\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          cache: pnpm\n      - name: Install dependencies\n        run: pnpm install\n      - name: Prepare stubs\n        run: pnpm dev:prepare\n      - name: Validate package\n        run: pnpm validate\n  publish:\n    runs-on: ubuntu-latest\n    needs: lint\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          registry-url: ${{ env.node_registry }}\n          cache: pnpm\n      - name: Install dependencies\n        run: pnpm install\n      - name: Prepare stubs\n        run: pnpm dev:prepare\n      - name: Build\n        run: pnpm prepack\n      - name: Generate changelog and publish release\n        run: |-\n          git config --global user.name \"${{ env.changelog_user }}\"\n          git config --global user.email \"${{ env.changelog_email }}\"\n          pnpm changelogen --release --push \\\n            --publish --prerelease --publishTag beta\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Nuxt [Release]\nenv:\n  node_version: 24\n  node_registry: https://registry.npmjs.org/\n  changelog_user: Github CI\n  changelog_email: artem@manchenkoff.me\n  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\nconcurrency:\n  group: nuxt-auth-sanctum-release\n  cancel-in-progress: false\npermissions:\n  contents: write\n  id-token: write\non: workflow_dispatch\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          cache: pnpm\n      - name: Install dependencies\n        run: pnpm install\n      - name: Prepare stubs\n        run: pnpm dev:prepare\n      - name: Validate package\n        run: pnpm validate\n  publish:\n    runs-on: ubuntu-latest\n    needs: lint\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          registry-url: ${{ env.node_registry }}\n          cache: pnpm\n      - name: Install dependencies\n        run: pnpm install\n      - name: Prepare stubs\n        run: pnpm dev:prepare\n      - name: Build\n        run: pnpm prepack\n      - name: Generate changelog and publish release\n        run: |-\n          git config --global user.name \"${{ env.changelog_user }}\"\n          git config --global user.email \"${{ env.changelog_email }}\"\n          pnpm changelogen --release --push --publish\n"
  },
  {
    "path": ".github/workflows/upgrade.yml",
    "content": "name: Upgrade Dependencies\n\nenv:\n  node_version: 24\n  git_email: artem@manchenkoff.me\n  git_name: manchenkoff\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 0 * * 0\"\n\njobs:\n  upgrade:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n    strategy:\n      fail-fast: false\n      matrix:\n        project: [\".\", \"docs\", \"playground\"]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ env.node_version }}\n\n      - name: Enable corepack\n        run: corepack enable\n\n      - name: Install dependencies\n        working-directory: ${{ matrix.project }}\n        run: npx nypm@latest i\n\n      - name: Upgrade\n        working-directory: ${{ matrix.project }}\n        run: pnpm upgrade\n\n      - name: Check for changes\n        id: changed\n        run: |\n          echo \"DATE=$(date +%Y-%m-%d)\" >> $GITHUB_ENV\n          PROJECT_NAME=\"${{ matrix.project }}\"\n          if [ \"$PROJECT_NAME\" = \".\" ]; then\n            PROJECT_NAME=\"root\"\n          fi\n          echo \"project_name=$PROJECT_NAME\" >> $GITHUB_ENV\n          if git diff --quiet; then\n            echo \"changed=false\" >> $GITHUB_OUTPUT\n          else\n            echo \"changed=true\" >> $GITHUB_OUTPUT\n          fi\n\n      - name: Configure git\n        if: steps.changed.outputs.changed == 'true'\n        run: |\n          git config --global user.email \"${{ env.git_email }}\"\n          git config --global user.name \"${{ env.git_name }}\"\n\n      - name: Create branch and commit\n        if: steps.changed.outputs.changed == 'true'\n        run: |\n          BRANCH=\"deps/bump-${DATE}-${{ env.project_name }}\"\n          git checkout -b \"$BRANCH\"\n          git add -A\n          git commit -m \"chore: upgrade ${{ env.project_name }} dependencies\"\n\n      - name: Push changes\n        if: steps.changed.outputs.changed == 'true'\n        run: |\n          BRANCH=\"deps/bump-${DATE}-${{ env.project_name }}\"\n          git push origin :$BRANCH || true\n          git push -u origin HEAD\n\n      - name: Create Pull Request\n        if: steps.changed.outputs.changed == 'true'\n        run: |\n          gh pr create \\\n            --base main \\\n            --head deps/bump-${DATE}-${{ env.project_name }} \\\n            --title \"deps: ${{ env.project_name }} weekly dependency bump\" \\\n            --body \"Automated dependency upgrade for ${{ env.project_name }}. Please review before merging.\" \\\n            --assignee manchenkoff\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/validation.yml",
    "content": "name: Nuxt [Validate]\nenv:\n  node_version: 24\nconcurrency:\n  group: nuxt-auth-sanctum-ci\n  cancel-in-progress: false\non:\n  workflow_dispatch:\n  pull_request:\n    branches: [main]\npermissions:\n  contents: read\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          cache: pnpm\n      - name: Install dependencies\n        run: pnpm install\n      - name: Build stubs\n        run: pnpm dev:prepare\n      - name: Lint\n        run: pnpm lint\n      - name: Type check\n        run: pnpm test:types\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Install pnpm\n        uses: pnpm/action-setup@v5\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ env.node_version }}\n          cache: pnpm\n      - name: Install dependencies\n        run: pnpm install\n      - name: Build stubs\n        run: pnpm dev:prepare\n      - name: Test\n        run: pnpm test\n"
  },
  {
    "path": ".gitignore",
    "content": "# Dependencies\nnode_modules\n\n# Logs\n*.log\n\n# Temp directories\n.temp\n.tmp\n.cache\n\n# Yarn\n**/.yarn/cache\n**/.yarn/*state*\n\n# Generated dirs\ndist\n\n# Nuxt\n.nuxt\n.output\n.data\n.vercel_build_output\n.build-*\n.netlify\n\n# Env\n.env\n\n# Testing\nreports\ncoverage\n*.lcov\n.nyc_output\n\n# VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n!.vscode/*.code-snippets\n\n# Intellij idea\n*.iml\n.idea\n\n# OSX\n.DS_Store\n.AppleDouble\n.LSOverride\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n"
  },
  {
    "path": ".nuxtrc",
    "content": "setups.@nuxt/test-utils=\"4.0.2\""
  },
  {
    "path": ".yamlfmt.yaml",
    "content": "# https://github.com/google/yamlfmt/blob/main/docs/config-file.md\nformatter:\n  type: basic\n  include_document_start: false\n"
  },
  {
    "path": ".yamllint.yaml",
    "content": "# https://yamllint.readthedocs.io/en/stable/rules.html\nextends: default\nrules:\n  document-start:\n    present: false\n  truthy: false\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n\n## v2.3.4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.3.3...v2.3.4)\n\n### 🩹 Fixes\n\n- Use correct config in logger and improve docs about env overrides ([#605](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/605))\n\n### 📖 Documentation\n\n- Clarify SSR environment variable requirements for baseUrl ([#595](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/595))\n\n### 🏡 Chore\n\n- **test:** Added module unit tests ([#602](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/602))\n\n### ❤️ Contributors\n\n- Artem Manchenkov ([@manchenkoff](https://github.com/manchenkoff))\n- Derrick Obedgiu <derrickobedgiu@gmail.com>\n\n## v2.3.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.3.2...v2.3.3)\n\n### 🩹 Fixes\n\n- **proxy:** Use runtime-agnostic readRawBody for multipart file uploads ([#582](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/582))\n\n### 🏡 Chore\n\n- **ci:** Upgraded action versions ([daffcee](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/daffcee))\n\n### ❤️ Contributors\n\n- Derrick Obedgiu <derrickobedgiu@gmail.com>\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.3.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.3.1...v2.3.2)\n\n### 🩹 Fixes\n\n- Fallback to default token storage in both csr and ssr ([#580](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/580))\n- Allow partial override for runtime config ([#579](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/579))\n- Deduplicate fetch requests using key ([#581](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/581))\n\n### 🏡 Chore\n\n- Upgraded nuxt and content setup ([ad62713](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ad62713))\n- Upgraded nuxt and content setup ([bb7e9a2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bb7e9a2))\n\n### ❤️ Contributors\n\n- Artem Manchenkov ([@manchenkoff](https://github.com/manchenkoff))\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.3.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.3.0...v2.3.1)\n\n### 🩹 Fixes\n\n- Typo in Configuration.md ([#569](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/569))\n\n### 🏡 Chore\n\n- Updated issue templates ([53a4323](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/53a4323))\n- Added error logs for debugging ([#570](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/570))\n\n### ❤️ Contributors\n\n- Artem Manchenkov ([@manchenkoff](https://github.com/manchenkoff))\n- EtienneHosman ([@EtienneHosman](https://github.com/EtienneHosman))\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.3.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.2.0...v2.3.0)\n\n### 🚀 Enhancements\n\n- Added token storage hook ([#553](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/553))\n- Split server and client configs ([#552](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/552))\n\n### ❤️ Contributors\n\n- Artem Manchenkov ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.2.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.1.3...v2.2.0)\n\n### 🚀 Enhancements\n\n- Sanitize proxy request headers to exclude hop-by-hop ([#525](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/525))\n\n### 📖 Documentation\n\n- Added plugin-based approach for token storage ([#539](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/539))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @types/node from 25.1.0 to 25.3.3 ([#534](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/534))\n- **deps-dev:** Bump @nuxt/kit from 4.2.2 to 4.3.1 ([#537](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/537))\n- **deps-dev:** Bump nuxt from 4.3.0 to 4.3.1 ([#538](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/538))\n- **deps-dev:** Bump @nuxt/test-utils from 3.23.0 to 4.0.0 ([#535](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/535))\n- **deps-dev:** Bump @nuxt/devtools from 3.1.1 to 3.2.2 ([#536](https://github.com/manchenkoff/nuxt-auth-sanctum/pull/536))\n\n### ❤️ Contributors\n\n- Ahoiroman <roman.lossin@icloud.com>\n- Artem Manchenkov ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.1.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.1.2...v2.1.3)\n\n### 🩹 Fixes\n\n- **proxy:** Prevent readBody crash on empty DELETE requests ([2ef0977](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2ef0977))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @types/node from 25.0.9 to 25.1.0 ([888afbe](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/888afbe))\n- **deps-dev:** Bump @nuxt/schema from 4.2.2 to 4.3.0 ([dadb7ab](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/dadb7ab))\n- **deps-dev:** Bump vitest from 4.0.17 to 4.0.18 ([2b8df57](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2b8df57))\n- **deps-dev:** Bump vue-tsc from 3.2.2 to 3.2.4 ([14f3b99](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/14f3b99))\n- **deps-dev:** Bump nuxt from 4.2.2 to 4.3.0 ([327ac03](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/327ac03))\n- Fix lints ([7d13b94](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/7d13b94))\n\n### ❤️ Contributors\n\n- Derrick Obedgiu <derrickobedgiu@gmail.com>\n\n## v2.1.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.1.1...v2.1.2)\n\n### 🩹 Fixes\n\n- Do not read body on multipart requests ([fcbff01](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fcbff01))\n\n### 🏡 Chore\n\n- Bump packages ([dda16b8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/dda16b8))\n- Bump packages ([15dc3b5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/15dc3b5))\n- Added certificated.dev banner ([e951e56](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e951e56))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.1.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.1.0...v2.1.1)\n\n### 🩹 Fixes\n\n- Use new release pipeline workflow ([0beadd5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0beadd5))\n- **proxy:** Use runtime-agnostic readBody for v8-engine compatibility ([47d6523](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/47d6523))\n\n### ❤️ Contributors\n\n- Derrick Obedgiu <derrickobedgiu@gmail.com>\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.1.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v2.0.0...v2.1.0)\n\n### 🚀 Enhancements\n\n- Added server hook for proxy request ([1ac5481](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1ac5481))\n\n### 🏡 Chore\n\n- Minor refactoring for proxy endpoint ([8074e14](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8074e14))\n- Added playground example for nitro hook plugin ([dbd2b85](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/dbd2b85))\n- **docs:** Added description for proxy hook ([dd2d1ce](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/dd2d1ce))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v2.0.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.3...v2.0.0)\n\n### 🚀 Enhancements\n\n- Add optional fetch options to login/logout methods ([981d5cc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/981d5cc))\n- ⚠️  Migrate to useFetch composable ([25f9af5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/25f9af5))\n- ⚠️  Removed checkSession from public scope ([ac34929](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ac34929))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump vue from 3.5.24 to 3.5.25 ([7700fa7](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/7700fa7))\n- **deps-dev:** Bump vitest from 4.0.13 to 4.0.14 ([f9474dd](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f9474dd))\n- **deps-dev:** Bump @nuxt/eslint-config from 1.10.0 to 1.11.0 ([3a24c01](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3a24c01))\n- **deps-dev:** Bump @nuxt/devtools from 3.1.0 to 3.1.1 ([d7e0f2b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d7e0f2b))\n- **docs:** Updated info about useSanctumAuth composable ([34c54c7](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/34c54c7))\n- **docs:** Updated info about useSanctumAuth composable ([f092852](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f092852))\n- **docs:** Adjusted composables description ([8c958e2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8c958e2))\n\n#### ⚠️ Breaking Changes\n\n- ⚠️  Migrate to useFetch composable ([25f9af5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/25f9af5))\n- ⚠️  Removed checkSession from public scope ([ac34929](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ac34929))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.2...v1.4.3)\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @nuxt/devtools from 2.7.0 to 3.0.1 ([ae873cf](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ae873cf))\n- **docs:** Updated readme ([a68d9a6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/a68d9a6))\n- Remove outdated type augmentation ([5df0223](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5df0223))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.1...v1.4.2)\n\n## v1.4.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.1-4...v1.4.1)\n\n### 🩹 Fixes\n\n- Avoid memory leak on session check and prevent infinite loop ([0d7ca68](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0d7ca68))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.1-4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.1-3...v1.4.1-4)\n\n### 🩹 Fixes\n\n- Move session check outside of composable ([5d7e52a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5d7e52a))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.1-3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.1-2...v1.4.1-3)\n\n### 🩹 Fixes\n\n- Disable session check ([745ed5a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/745ed5a))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.1-2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.1-1...v1.4.1-2)\n\n### 🩹 Fixes\n\n- Enable reactive resolver for fetch key ([6daa5d4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6daa5d4))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.1-1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.1-0...v1.4.1-1)\n\n### 🩹 Fixes\n\n- Enable cookie check without reactivity ([68e13fd](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/68e13fd))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.1-0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.4.0...v1.4.1-0)\n\n### 🩹 Fixes\n\n- Use raw data for serialization and disable session check ([11523c2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/11523c2))\n\n### 🏡 Chore\n\n- **docs:** Updated website url ([848e0f9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/848e0f9))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.4.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.3.1...v1.4.0)\n\n### 🚀 Enhancements\n\n- Added reactive for useAsyncData behind sanctumFetch ([bd94165](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bd94165))\n- Added reactive url for useAsyncData behind sanctumFetch ([b471100](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b471100))\n\n### 📖 Documentation\n\n- Improved nuxt content configuration and updated docs ([bf7d2e8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bf7d2e8))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump vue-tsc from 2.2.12 to 3.1.0 ([6401d22](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6401d22))\n- **docs:** Fixed typo ([90b6565](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/90b6565))\n- Added yaml linter configs ([19589d0](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/19589d0))\n- **ci:** Trigger docs deployment only if changed ([c202bc3](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c202bc3))\n- **docs:** Added section abour reactivity for fetch key/url ([81c2ca2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/81c2ca2))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.3.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.3.0...v1.3.1)\n\n### 🩹 Fixes\n\n- Resolve reactive options in useAsyncData ([95b3166](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/95b3166))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.3.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.2.0...v1.3.0)\n\n### 🚀 Enhancements\n\n- Check session existence in middleware ([217c60e](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/217c60e))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump nuxt in the npm_and_yarn group across 1 directory ([146faed](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/146faed))\n\n### 🤖 CI\n\n- Simplify publish release ([e2c8fe5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e2c8fe5))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.2.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.1.2...v1.2.0)\n\n### 🚀 Enhancements\n\n- Support user-defined fetch key ([b124fa8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b124fa8))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.1.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.1.1...v1.1.2)\n\n### 🩹 Fixes\n\n- Handle no connection error ([5b9bb04](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5b9bb04))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @nuxt/schema from 4.0.1 to 4.0.2 ([e0f12bd](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e0f12bd))\n- **deps-dev:** Bump eslint from 9.31.0 to 9.32.0 ([66f5de9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/66f5de9))\n- **deps-dev:** Bump @nuxt/module-builder from 1.0.1 to 1.0.2 ([f6f6d01](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f6f6d01))\n- **deps-dev:** Bump @nuxt/eslint-config from 1.7.0 to 1.7.1 ([e48a4be](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e48a4be))\n- **deps-dev:** Bump @nuxt/kit from 4.0.1 to 4.0.2 ([206a7c5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/206a7c5))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.1.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.1.0...v1.1.1)\n\n### 🩹 Fixes\n\n- Upgraded vulnerable package ([fd4acf1](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fd4acf1))\n\n### 🏡 Chore\n\n- Upgraded to nuxt 4 ([34450d7](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/34450d7))\n- Bump packages ([2155122](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2155122))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.1.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.0.1...v1.1.0)\n\n### 🚀 Enhancements\n\n- Allow login without fetching an identity ([55df80c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/55df80c))\n- Hide serverProxy endpoint from runtime config ([a730c4c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/a730c4c))\n\n### 🩹 Fixes\n\n- Proxy request without reading ([18b6105](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/18b6105))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @types/node from 22.15.32 to 24.0.8 ([c193a81](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c193a81))\n- **deps-dev:** Bump @nuxt/devtools from 2.5.0 to 2.6.0 ([5a284b4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5a284b4))\n- **deps-dev:** Bump eslint from 9.29.0 to 9.30.0 ([0a5e885](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0a5e885))\n- Bumped packages and apply formatting ([c16c843](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c16c843))\n- Added approved build ([c75bd7d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c75bd7d))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.0.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v1.0.0...v1.0.1)\n\n### 🩹 Fixes\n\n- Ignore reprocessed response content headers ([861fdfd](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/861fdfd))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v1.0.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.7...v1.0.0)\n\n### 🚀 Enhancements\n\n- **types:** ⚠️  Added explicit types for fetch client ([6054510](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6054510))\n- **hooks:** ⚠️  Migrated interceptors to hooks ([3d23818](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3d23818))\n- Added nuxt server proxy endpoint ([4a32314](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4a32314))\n\n### 🩹 Fixes\n\n- **types:** ⚠️  Drop old app types augmentation ([4b54597](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4b54597))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump nuxt from 3.17.3 to 3.17.4 ([ce65d87](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ce65d87))\n\n#### ⚠️ Breaking Changes\n\n- **types:** ⚠️  Added explicit types for fetch client ([6054510](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6054510))\n- **hooks:** ⚠️  Migrated interceptors to hooks ([3d23818](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3d23818))\n- **types:** ⚠️  Drop old app types augmentation ([4b54597](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4b54597))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.7\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.6...v0.6.7)\n\n### 🩹 Fixes\n\n- Use proper typing for lazy fetch ([9c124dc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9c124dc))\n- Use github action permissions ([1ced700](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1ced700))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @types/node from 22.14.0 to 22.15.3 ([5de3e8f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5de3e8f))\n- **deps-dev:** Bump @nuxt/kit from 3.16.2 to 3.17.1 ([6bf9aa1](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6bf9aa1))\n- **deps-dev:** Bump nuxt from 3.16.2 to 3.17.1 ([1caa2ce](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1caa2ce))\n- **deps-dev:** Bump @nuxt/schema from 3.16.2 to 3.17.1 ([95e2233](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/95e2233))\n- Upgraded dependencies ([427ced5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/427ced5))\n- Upgraded nuxt module builder ([511d6fc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/511d6fc))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.6\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.5...v0.6.6)\n\n### 🩹 Fixes\n\n- Removed redundant logging ([29fb5ba](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/29fb5ba))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.5\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.4...v0.6.5)\n\n### 🩹 Fixes\n\n- Avoid mutable list of interceptors ([333f7c8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/333f7c8))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.3...v0.6.4)\n\n### 🚀 Enhancements\n\n- **hook:** Added onRequestError event `sanctum:error:request` hook ([b5095e5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b5095e5))\n\n### 🏡 Chore\n\n- Restructured project ([161dc4e](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/161dc4e))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.2...v0.6.3)\n\n### 🚀 Enhancements\n\n- **client:** Allow overriding of Accept header per request ([871fcae](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/871fcae))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.1...v0.6.2)\n\n### 🚀 Enhancements\n\n- Return login response ([d341431](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d341431))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @nuxt/devtools from 2.3.1 to 2.3.2 ([5d8442f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5d8442f))\n- **deps-dev:** Bump vitest from 3.0.9 to 3.1.1 ([71f7049](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/71f7049))\n- **deps-dev:** Bump @types/node from 22.13.13 to 22.13.16 ([4273f0f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4273f0f))\n\n### ❤️ Contributors\n\n- SwiTool <switooldev@gmail.com>\n\n## v0.6.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.6.0...v0.6.1)\n\n### 🚀 Enhancements\n\n- **logs:** Added more details into logs for troubleshooting ([b7331c9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b7331c9))\n- **tests:** Added test fixture project to simulate remote API ([27a7976](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/27a7976))\n\n### 🩹 Fixes\n\n- **init:** Use raw fetch to handle errors on identity init ([c1321bf](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c1321bf))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.6.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.8...v0.6.0)\n\n### 🚀 Enhancements\n\n- **hooks:** Added sanctum:error hook ([413989f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/413989f))\n- **hooks:** Added sanctum:redirect hook ([c7bf355](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c7bf355))\n- **hooks:** Added sanctum:init and sanctum:refresh hooks ([fa5b592](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fa5b592))\n- **hooks:** Added sanctum:login hook ([ad67393](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ad67393))\n- **hooks:** Added sanctum:logout hook ([dd496cf](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/dd496cf))\n- **fetch:** ⚠️  Support AsyncDataOptions in sanctum fetch composables ([9f23351](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9f23351))\n\n### 🏡 Chore\n\n- Upgraded nuxt packages ([0df572b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0df572b))\n- Upgraded changelogen package ([03c8b31](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/03c8b31))\n- Added troubleshooting guide link ([2bd41a1](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2bd41a1))\n- Upgraded nuxt dependencies ([5a34273](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5a34273))\n\n#### ⚠️ Breaking Changes\n\n- **fetch:** ⚠️  Support AsyncDataOptions in sanctum fetch composables ([9f23351](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9f23351))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](https://github.com/manchenkoff))\n\n## v0.5.8\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.7...v0.5.8)\n\n### 🩹 Fixes\n\n- Use unique key for sanctum fetch across csr/ssr ([0d43ea6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0d43ea6))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.7\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.6...v0.5.7)\n\n### 🩹 Fixes\n\n- Migrated from useFetch to useAsyncData ([79cb27d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/79cb27d))\n\n### 🏡 Chore\n\n- Dependencies upgrade ([2bbe90c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2bbe90c))\n- **dev-deps:** Upgraded dev dependencies ([6a629df](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6a629df))\n- **dev-deps:** Upgraded nuxt devtools ([bc3b601](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bc3b601))\n- **security:** Upgraded esbuild to avoid security issues ([82ad135](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/82ad135))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.6\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.5...v0.5.6)\n\n### 🚀 Enhancements\n\n- Added new composables to make requests ([a6bfc2f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/a6bfc2f))\n\n### 🩹 Fixes\n\n- Added type hints for fetch composables ([bbb9e04](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bbb9e04))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.5\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.4...v0.5.5)\n\n### 🩹 Fixes\n\n- Use npm keys in actions ([9bc984f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9bc984f))\n- Deduplicate `set-cookie` headers on SSR response ([9d957e2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9d957e2))\n- Downgraded typescript to fix module-builder ([f28daf8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f28daf8))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @nuxt/devtools from 1.6.4 to 1.7.0 ([b58ebc4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b58ebc4))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.7.3 to 0.7.4 ([0f79c42](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0f79c42))\n- **deps-dev:** Bump @typescript-eslint/eslint-plugin ([0cae715](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0cae715))\n- **deps-dev:** Bump @nuxt/schema from 3.14.1592 to 3.15.0 ([77c79f6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/77c79f6))\n- **deps-dev:** Bump @nuxt/test-utils from 3.15.1 to 3.15.4 ([2cc446b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2cc446b))\n- **deps-dev:** Bump vitest in the npm_and_yarn group ([b64a354](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b64a354))\n- **deps-dev:** Bump @nuxt/schema from 3.15.2 to 3.15.4 ([c2dd453](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c2dd453))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.7.4 to 1.0.0 ([a435516](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/a435516))\n- Upgraded nuxt packages ([e0c5bb9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e0c5bb9))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.3...v0.5.4)\n\n### 🩹 Fixes\n\n- Use relative paths for injected types ([63696ff](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/63696ff))\n\n### ❤️ Contributors\n\n- Denis Mamaev ([@ExileofAranei](http://github.com/ExileofAranei))\n\n## v0.5.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.2...v0.5.3)\n\n### 🩹 Fixes\n\n- Expose plugin name to be used in dependsOn ([c2d930b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c2d930b))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @nuxt/schema from 3.14.159 to 3.14.1592 ([83be062](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/83be062))\n- **deps-dev:** Bump nuxt from 3.14.159 to 3.14.1592 ([0469f64](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0469f64))\n- **deps-dev:** Bump @typescript-eslint/eslint-plugin ([f619be8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f619be8))\n- **deps-dev:** Bump eslint from 9.15.0 to 9.16.0 ([464c8ed](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/464c8ed))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.7.0 to 0.7.2 ([38c1c46](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/38c1c46))\n- **deps-dev:** Bump @nuxt/devtools from 1.6.1 to 1.6.3 ([31a8027](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/31a8027))\n- **deps-dev:** Bump @typescript-eslint/eslint-plugin ([1d47a17](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1d47a17))\n- **deps-dev:** Bump vitest from 2.1.5 to 2.1.8 ([63fa544](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/63fa544))\n- **deps-dev:** Bump @nuxt/test-utils from 3.14.4 to 3.15.1 ([7c21b35](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/7c21b35))\n- **deps-dev:** Bump @types/node from 22.10.1 to 22.10.2 ([b1d429b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b1d429b))\n- **deps-dev:** Bump @typescript-eslint/eslint-plugin ([3067b61](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3067b61))\n- **deps-dev:** Bump eslint from 9.16.0 to 9.17.0 ([ce17914](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ce17914))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.7.2 to 0.7.3 ([2ff5e99](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2ff5e99))\n- Upgraded playground to nuxt v4 structure ([056b488](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/056b488))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.1...v0.5.2)\n\n### 🩹 Fixes\n\n- Support insecure cookies for auth token ([5e59064](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5e59064))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @types/node from 22.7.7 to 22.8.1 ([979c480](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/979c480))\n- **deps-dev:** Bump vue-tsc from 2.1.6 to 2.1.8 ([b3a6b5a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b3a6b5a))\n- **deps-dev:** Bump vue-tsc from 2.1.8 to 2.1.10 ([73259c8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/73259c8))\n- **deps-dev:** Bump vitest from 2.1.3 to 2.1.4 ([ce299ac](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ce299ac))\n- **deps-dev:** Bump eslint from 9.13.0 to 9.14.0 ([2840859](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2840859))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.6.0 to 0.6.1 ([5f4e34c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5f4e34c))\n- **deps-dev:** Bump @types/node from 22.8.7 to 22.9.0 ([cbe9b6f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/cbe9b6f))\n- **deps-dev:** Bump nuxt from 3.13.2 to 3.14.159 ([79ccffc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/79ccffc))\n- **deps-dev:** Bump @nuxt/schema from 3.13.2 to 3.14.159 ([4dbd21a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4dbd21a))\n- **deps-dev:** Bump vitest from 2.1.4 to 2.1.5 ([1bd218b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1bd218b))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.6.1 to 0.7.0 ([1303a9a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1303a9a))\n- **deps-dev:** Bump eslint from 9.14.0 to 9.15.0 ([b71d6e8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b71d6e8))\n- Install typescript-eslint with a fix ([0e6b0e5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0e6b0e5))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.5.0...v0.5.1)\n\n### 🚀 Enhancements\n\n- Add option to prepend the global middleware ([94c3346](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/94c3346))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump eslint from 9.11.1 to 9.12.0 ([b593bd7](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b593bd7))\n- **deps-dev:** Bump @nuxt/test-utils from 3.14.2 to 3.14.3 ([e775417](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e775417))\n- **deps-dev:** Bump typescript from 5.5.4 to 5.6.3 ([0963a0b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0963a0b))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.5.7 to 0.6.0 ([c5bc900](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c5bc900))\n- Updated bug report issue template ([f8534f1](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f8534f1))\n- **deps-dev:** Bump @nuxt/test-utils from 3.14.3 to 3.14.4 ([e89441f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e89441f))\n- **deps-dev:** Bump vitest from 2.1.2 to 2.1.3 ([e563484](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e563484))\n- **deps-dev:** Bump eslint from 9.12.0 to 9.13.0 ([412f36f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/412f36f))\n\n### ❤️ Contributors\n\n- Jonian Guveli <jonian@hardpixel.eu>\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.5.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.18...v0.5.0)\n\n### 🚀 Enhancements\n\n- ⚠️  Use ofetch as actual dependency ([07f0213](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/07f0213))\n\n### 🩹 Fixes\n\n- Expect object instead of headers to log ([aeeddb8](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/aeeddb8))\n\n#### ⚠️ Breaking Changes\n\n- ⚠️  Use ofetch as actual dependency ([07f0213](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/07f0213))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.18\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.17...v0.4.18)\n\n### 🚀 Enhancements\n\n- Improve headers logging readability ([8160e8c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8160e8c))\n\n### 🩹 Fixes\n\n- Use normalized Headers object for interceptors ([22602b6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/22602b6))\n\n### 🏡 Chore\n\n- Use specific ofetch version for development ([6edf22d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6edf22d))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.17\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.16...v0.4.17)\n\n### 🚀 Enhancements\n\n- Added headers validation and more control on config values ([c08c778](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c08c778))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.16\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.15...v0.4.16)\n\n### 🩹 Fixes\n\n- Prevent concurrent update of request headers ([c5af530](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c5af530))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.15\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.14...v0.4.15)\n\n### 🚀 Enhancements\n\n- Add request and response headers logging ([fed1d16](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fed1d16))\n\n### 🩹 Fixes\n\n- Prevent object merging with HeadersList ([393ddca](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/393ddca))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump @nuxt/devtools from 1.5.0 to 1.5.1 ([c564ab3](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c564ab3))\n- **deps-dev:** Bump @types/node from 22.5.5 to 22.7.4 ([9a2f385](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9a2f385))\n- **deps-dev:** Bump eslint from 9.11.0 to 9.11.1 ([4b4ab3f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4b4ab3f))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.14\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.13...v0.4.14)\n\n### 🩹 Fixes\n\n- Use nuxt app context for default token storage ([1f7fdb2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1f7fdb2))\n\n### 🏡 Chore\n\n- Updated issue templates ([8647cdc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8647cdc))\n- Updated bug report issue template ([fca3627](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fca3627))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.13\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.12...v0.4.13)\n\n### 🩹 Fixes\n\n- Keep same session on SSR requests ([fc96f34](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fc96f34))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.12\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.11...v0.4.12)\n\n### 🚀 Enhancements\n\n- Control plugin load order ([fa1e46d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fa1e46d))\n- Control over initial user request ([5f3d8d4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5f3d8d4))\n\n### 🏡 Chore\n\n- **deps-dev:** Bump nuxt from 3.13.0 to 3.13.1 ([33e6323](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/33e6323))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.5.5 to 0.5.6 ([f88ecb9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f88ecb9))\n- **deps-dev:** Bump vue-tsc from 2.1.4 to 2.1.6 ([11661f9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/11661f9))\n- **deps-dev:** Bump eslint from 9.9.1 to 9.10.0 ([95cd069](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/95cd069))\n- **deps-dev:** Bump @nuxt/module-builder from 0.8.3 to 0.8.4 ([51fd807](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/51fd807))\n- **deps-dev:** Bump @types/node from 22.5.2 to 22.5.5 ([abe3846](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/abe3846))\n- **deps-dev:** Bump vitest from 2.0.5 to 2.1.1 ([bbcb244](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bbcb244))\n- Upgraded nuxt dependencies ([6b28a2a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6b28a2a))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.11\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.10...v0.4.11)\n\n### 🏡 Chore\n\n- **deps-dev:** Bump vue-tsc from 2.1.2 to 2.1.4 ([35efc7b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/35efc7b))\n- **deps-dev:** Bump @types/node from 22.5.1 to 22.5.2 ([7a7e912](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/7a7e912))\n- **deps-dev:** Bump @nuxt/eslint-config from 0.5.4 to 0.5.5 ([84e836d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/84e836d))\n\n### 🤖 CI\n\n- Added prerelease pipeline ([deff64a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/deff64a))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.10\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.9...v0.4.10)\n\n### 🩹 Fixes\n\n- Improved nullable schema fields ([879fdee](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/879fdee))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.9\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.8...v0.4.9)\n\n### 🚀 Enhancements\n\n- Simplified module config ([b3bad10](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b3bad10))\n\n### 🩹 Fixes\n\n- Added augmented types for PageMeta ([e784f88](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e784f88))\n\n### 🏡 Chore\n\n- Migrated from yarn to pnpm ([bad9820](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bad9820))\n- Simplified pull request template ([8f8955b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8f8955b))\n- Applied eslint formatter ([1b2670d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1b2670d))\n\n### 🤖 CI\n\n- Upgraded pipelines to work with pnpm ([1a041da](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1a041da))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.8\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.7...v0.4.8)\n\n### 🩹 Fixes\n\n- Update to latest `@nuxt/module-builder` ([fdeae82](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fdeae82))\n\n### ❤️ Contributors\n\n- Daniel Roe ([@danielroe](http://github.com/danielroe))\n\n## v0.4.7\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.6...v0.4.7)\n\n### 🩹 Fixes\n\n- Use new augmented types from nuxt 3.13 ([197f1c3](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/197f1c3))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.6\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.5...v0.4.6)\n\n### 🩹 Fixes\n\n- Do not trim single slash in the url ([81c87d0](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/81c87d0))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.5\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.4...v0.4.5)\n\n### 🩹 Fixes\n\n- Trim trailing slash on redirects ([d24014d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d24014d))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.3...v0.4.4)\n\n### 🩹 Fixes\n\n- Fallback to #app augmented type for page meta ([cbf8a6c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/cbf8a6c))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.2...v0.4.3)\n\n### 🩹 Fixes\n\n- Use resolved path for augmented types when using pnpm ([050c006](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/050c006))\n\n### 🏡 Chore\n\n- Upgraded nuxt version ([0cd2d51](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0cd2d51))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.1...v0.4.2)\n\n### 🚀 Enhancements\n\n- Configure redirect if unauthenticated ([9574818](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9574818))\n\n### 🩹 Fixes\n\n- Use updated csrf token on first login ([14f5c63](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/14f5c63))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.4.0...v0.4.1)\n\n### 🩹 Fixes\n\n- Experimental support for cloudflare workers ([9146cba](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9146cba))\n\n### 🏡 Chore\n\n- Updated github templates ([932fd0f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/932fd0f))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.4.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.14...v0.4.0)\n\n### 🚀 Enhancements\n\n- ⚠️  Dropped support for excludeFromSanctum page meta ([5d09e71](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5d09e71))\n- Added token storage support ([2ebcfbf](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2ebcfbf))\n\n### 🏡 Chore\n\n- Updated playgorund config to breeze-api ([5644023](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5644023))\n- Indicate compatibility with new v4 major ([54869f7](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/54869f7))\n\n#### ⚠️ Breaking Changes\n\n- ⚠️  Dropped support for excludeFromSanctum page meta ([5d09e71](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5d09e71))\n\n### ❤️ Contributors\n\n- Daniel Roe ([@danielroe](http://github.com/danielroe))\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.14\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.13...v0.3.14)\n\n### 🩹 Fixes\n\n- Changed path with typo ([1c9c963](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1c9c963))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.13\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.12...v0.3.13)\n\n### 🩹 Fixes\n\n- Hardcode absolute path to node_modules for page meta ([eb0d555](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/eb0d555))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.12\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.11...v0.3.12)\n\n### 🩹 Fixes\n\n- Adjusted path for page meta type ([c0ddc35](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c0ddc35))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.11\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.10...v0.3.11)\n\n### 🩹 Fixes\n\n- Expose page meta augmented type ([b49fdfb](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b49fdfb))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.10\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.9...v0.3.10)\n\n### 🩹 Fixes\n\n- Removed workspace from dependencies ([a648ae9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/a648ae9))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.9\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.8...v0.3.9)\n\n### 🩹 Fixes\n\n- Expose type via templates ([d1ed3a4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d1ed3a4))\n- Export public runtime config as part of module ([43fd15c](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/43fd15c))\n\n### 🏡 Chore\n\n- Added typing for interceptors ([8446f6a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8446f6a))\n- Added transpile build option ([40fe0b2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/40fe0b2))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.8\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.7...v0.3.8)\n\n### 🩹 Fixes\n\n- Updated exporting nuxt schemas ([fa608f6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/fa608f6))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.7\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.6...v0.3.7)\n\n### 🩹 Fixes\n\n- Use common request header ([2536f21](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2536f21))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.6\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.5...v0.3.6)\n\n### 🩹 Fixes\n\n- Export module types ([b1a513b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/b1a513b))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.5\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.4...v0.3.5)\n\n### 🚀 Enhancements\n\n- Added support for custom interceptors ([2795998](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2795998))\n\n### 🩹 Fixes\n\n- Disabled typecheck in the playground ([cd77036](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/cd77036))\n\n### 🏡 Chore\n\n- Upgraded module build dependencies ([16cdde6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/16cdde6))\n- Extracted default module options ([e0f2c7a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e0f2c7a))\n- Applied formatting to system files ([ae7918f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/ae7918f))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.3...v0.3.4)\n\n### 🩹 Fixes\n\n- Dropped extenal cookie parser to use h3 ([1410179](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1410179))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.2...v0.3.3)\n\n### 🩹 Fixes\n\n- Fixed cookie parses dependency ([bd6ef34](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/bd6ef34))\n\n### 🏡 Chore\n\n- Upgraded yarn to 4.2.2 ([007a7e4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/007a7e4))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.1...v0.3.2)\n\n### 🩹 Fixes\n\n- Added github token for automatic release creation ([84fac56](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/84fac56))\n- Request csrf only when not set ([569dbdc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/569dbdc))\n- Added csrf cookie for secure ssr calls ([8b45e06](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8b45e06))\n\n### 🏡 Chore\n\n- Added debug log for user initial request ([409b3ae](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/409b3ae))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.3.0...v0.3.1)\n\n### 🩹 Fixes\n\n- Added npm credentials config ([51d76ef](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/51d76ef))\n- Added id-token permission for pipeline ([72b08ea](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/72b08ea))\n- Keep config type exported in composable ([3862bfb](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3862bfb))\n\n### 🏡 Chore\n\n- Upgraded setup-node to v4 ([3f6226d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3f6226d))\n- Removed temp pipeline for publishing ([3bd6027](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3bd6027))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.3.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.2.3...v0.3.0)\n\n### 🚀 Enhancements\n\n- Added publish workflow ([3c8baef](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/3c8baef))\n- ⚠️  Added guest mode for global middleware ([50f2a10](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/50f2a10))\n\n### 🩹 Fixes\n\n- Include history and write permissions in release pipeline ([220e16b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/220e16b))\n\n### 🏡 Chore\n\n- **release:** V0.2.3 ([0397aa5](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/0397aa5))\n- Dropped support of old versions ([8366255](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8366255))\n\n#### ⚠️ Breaking Changes\n\n- ⚠️  Added guest mode for global middleware ([50f2a10](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/50f2a10))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.2.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.2.2...v0.2.3)\n\n### 🩹 Fixes\n\n- Resolved constant visibility issue ([5cbd7a3](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/5cbd7a3))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.2.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.2.0...v0.2.2)\n\n### 🚀 Enhancements\n\n- Added logger support ([9a62a8f](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/9a62a8f))\n- Added logs into plugin ([79e2558](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/79e2558))\n\n### 🩹 Fixes\n\n- Remove git push with tags ([03b28d4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/03b28d4))\n- Prevent infinite redirect for guests ([20e7cc9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/20e7cc9))\n- Removed redundant empty line ([6426309](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6426309))\n- Reset user only when it expires ([d314b86](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d314b86))\n- Request identity once on plugin init ([c88fbdf](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/c88fbdf))\n- Push main branch after bumping release version ([cc76b0e](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/cc76b0e))\n\n### 📖 Documentation\n\n- Use new `nuxi module add` command in installation ([d74c007](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d74c007))\n- Update second step ([93e365d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/93e365d))\n\n### 🏡 Chore\n\n- **release:** V0.2.1 ([cf80175](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/cf80175))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n- Daniel Roe ([@danielroe](http://github.com/danielroe))\n\n## v0.2.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.2.0...v0.2.1)\n\n### 🩹 Fixes\n\n- Remove git push with tags ([03b28d4](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/03b28d4))\n- Prevent infinite redirect for guests ([20e7cc9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/20e7cc9))\n\n### 📖 Documentation\n\n- Use new `nuxi module add` command in installation ([d74c007](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d74c007))\n- Update second step ([93e365d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/93e365d))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n- Daniel Roe ([@danielroe](http://github.com/danielroe))\n\n## v0.2.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.1.2...v0.2.0)\n\n### 🚀 Enhancements\n\n- Add Origin header to the request ([6387379](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6387379))\n- Added separate error page ([79ce7b2](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/79ce7b2))\n- Implemented global middleware ([6322fd3](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/6322fd3))\n\n### 🩹 Fixes\n\n- Opt in to `import.meta.*` properties ([99e98c9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/99e98c9))\n- Prevent redirects to the same page ([01daa22](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/01daa22))\n- Prevent redirect on 401 response from login page ([380d4a6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/380d4a6))\n- Removed mistaken command from contributing rules ([f4fbf82](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f4fbf82))\n- Adjusted tsc directories to check ([de23cbb](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/de23cbb))\n\n### 📖 Documentation\n\n- Added separate gitbook as module docs ([f7c4edc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f7c4edc))\n- Added toc to readme ([497386b](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/497386b))\n\n### 🏡 Chore\n\n- **release:** V0.1.2 ([f23e51a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/f23e51a))\n- Code style improvements ([32b0a64](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/32b0a64))\n- Simplified middleware checks ([1c186b9](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1c186b9))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n- Daniel Roe ([@danielroe](http://github.com/danielroe))\n\n## v0.1.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.1.1...v0.1.2)\n\n### 🩹 Fixes\n\n- Added required config to fixture env ([09b621d](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/09b621d))\n- Updated nuxt dependencies and fixed test behavior ([e06cb36](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/e06cb36))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.1.1\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.1.0...v0.1.1)\n\n### 🩹 Fixes\n\n- Dumped compatibility version to 3.9 ([1268ffc](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1268ffc))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.1.0\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.17...v0.1.0)\n\n### 🚀 Enhancements\n\n- Upgraded nuxt dependencies ([2c82ae6](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/2c82ae6))\n- Handle cookies in read-only mode ([632abab](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/632abab))\n\n### 🩹 Fixes\n\n- Remove redundant config from playground ([8288cc0](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8288cc0))\n- Revert nuxt 3.10 to 3.9 because of bugs ([1eb3f0a](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/1eb3f0a))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.0.17\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.16...v0.0.17)\n\n## v0.0.16\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.15...v0.0.16)\n\n### 🚀 Enhancements\n\n- **origin:** Set to optional ([dfe0805](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/dfe0805))\n- **origin:** Fix indent ([a50d2c7](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/a50d2c7))\n\n### ❤️ Contributors\n\n- Ugo Mignon <ugomignon@gmail.com>\n\n## v0.0.14\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.13...v0.0.14)\n\n### 🏡 Chore\n\n- Test bundler module resolution ([4dc2cde](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/4dc2cde))\n\n### ❤️ Contributors\n\n- Daniel Roe <daniel@roe.dev>\n\n## v0.0.13\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/list...v0.0.13)\n\n### 🏡 Chore\n\n- **release:** V0.0.12 ([8230041](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/8230041))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.0.12\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/list...v0.0.12)\n\n## v0.0.11\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.10...v0.0.11)\n\n## v0.0.10\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.9...v0.0.10)\n\n## v0.0.9\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.8...v0.0.9)\n\n## v0.0.8\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.7...v0.0.8)\n\n## v0.0.7\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.6...v0.0.7)\n\n## v0.0.6\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.5...v0.0.6)\n\n### 🏡 Chore\n\n- **release:** V0.0.5 ([d52546e](https://github.com/manchenkoff/nuxt-auth-sanctum/commit/d52546e))\n\n### ❤️ Contributors\n\n- Manchenkoff ([@manchenkoff](http://github.com/manchenkoff))\n\n## v0.0.5\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.4...v0.0.5)\n\n## v0.0.4\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.3...v0.0.4)\n\n## v0.0.3\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.2...v0.0.3)\n\n## v0.0.2\n\n[compare changes](https://github.com/manchenkoff/nuxt-auth-sanctum/compare/v0.0.1...v0.0.2)\n\n## v0.0.1\n\n- Initial release\n- Added first implementation of the module\n- Added readme\n- Published NPM package\n\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n-   Demonstrating empathy and kindness toward other people\n-   Being respectful of differing opinions, viewpoints, and experiences\n-   Giving and gracefully accepting constructive feedback\n-   Accepting responsibility and apologizing to those affected by our mistakes,\n    and learning from the experience\n-   Focusing on what is best not just for us as individuals, but for the\n    overall community\n\nExamples of unacceptable behavior include:\n\n-   The use of sexualized language or imagery, and sexual attention or\n    advances of any kind\n-   Trolling, insulting or derogatory comments, and personal or political attacks\n-   Public or private harassment\n-   Publishing others' private information, such as a physical or email\n    address, without their explicit permission\n-   Other conduct which could reasonably be considered inappropriate in a\n    professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nartem@manchenkoff.me.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to contribute\n\nPlease follow the guidelines below if you want to contribute to the project.\n\nIf you are working on a new feature, please create an issue on GitHub first. This will help us to understand what you are working on and to avoid duplication of work.\n\n# Development environment\n\nYou can clone the repo by running the following command:\n\n```bash\ngit clone git@github.com:manchenkoff/nuxt-auth-sanctum.git\n```\n\nThen you should create a new branch with the following name convention:\n\n```bash\ngit checkout -b XXX-feature-name\n```\n\nWhere `XXX` is the issue number on the GitHub.\n\n## Install dependencies\n\nTo setup the development environment, you should install the dependencies first. You can do this by running the following command:\n\n```bash\npnpm install\n```\n\nThen you can start dev server to see the playground app:\n\n```bash\npnpm dev\n```\n\nOr if you want to build the project, you can run one of the following commands:\n\n```bash\n# Generate type stubs\npnpm dev:prepare\n\n# Build the playground\npnpm dev:build\n```\n\n# Testing process\n\nTo test playground app you need to have a Laravel API running with Sanctum package installed.\n\nif there are tests for the feature you are working on, you can run them by executing one of the following commands:\n\n```bash\n# Run Vitest\npnpm test\n\n# Run Vitest in watch mode\npnpm test:watch\n```\n\n# Testing package locally\n\nIf you want to test the package before publishing it to `npm`, \nyou can create an archive and install it as a dependency in your project.\n\nTo do this, run the following command:\n\n```bash\npnpm rc\n```\n\nThis will create a `.tgz` file in the `dist` directory. You can then reference it in your project:\n\n```json\n{\n  \"devDependencies\": {\n    \"nuxt-auth-sanctum\": \"file:/dist/nuxt-auth-sanctum-0.0.0.tgz\"\n  }\n}\n```\n\nThen you can install the package by running:\n\n```bash\npnpm install\n```\n\n# Code Style and Standards\n\nThis project uses ESLint to enforce code style and standards. Please make sure to run the following commands before creating a pull request:\n\n```bash\n# Run ESLint\npnpm lint\n\n# Run Nuxt type check\npnpm test:types\n\n# Run Vitest\npnpm test\n```\n\n# Releasing\n\nOnce all the changes are merged into main branch, run the following command to release a new version:\n\n```bash\npnpm release\n```\n\n# Code of Conduct\n\nAll contributors are expected to adhere to the [Code of Conduct](CODE_OF_CONDUCT.md). Please read it.\n\n# Get in touch\n\nIf you have any questions or need help, feel free to reach out via artem@manchenkoff.me or by opening a new issue on Github.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Artem Manchenkov\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": "# Nuxt Auth Sanctum\n\n[![npm version][npm-version-src]][npm-version-href]\n[![npm downloads][npm-downloads-src]][npm-downloads-href]\n[![License][license-src]][license-href]\n[![Nuxt][nuxt-src]][nuxt-href]\n\nThis module provides a simple way to use Laravel Sanctum with Nuxt by leveraging cookies-based authentication. SSR-ready!\n\n- [Documentation](https://sanctum.manchenkoff.me)\n- [Features](#features)\n- [Quick Setup](#quick-setup)\n\n## Features\n\nThis module includes a range of features designed to streamline authentication:\n\n- `useSanctumAuth` composable for easy access to the current user and authentication methods\n- `useSanctumFetch` and `useLazySanctumFetch` to load data from your API\n- Automated `CSRF` token header and cookie management\n- Automated `Bearer` token header management\n- Both `CSR` and `SSR` modes support\n- Pre-configured middleware for pages that require authentication\n- Cast current user information to any class you want\n- Custom `request` and `response` interceptors\n- Subscribe to `sanctum:*` hooks to react as you want\n- Compatible with default Nuxt `ofetch` client\n- TypeScript support\n- ... and more, check the docs!\n\n**Note:** Before using this module, please make sure that you have already configured Laravel Sanctum on your backend. You can find more information about Laravel Sanctum [here](https://laravel.com/docs/10.x/sanctum#spa-authentication).\n\nComplete documentation - [Nuxt Auth Sanctum docs](https://sanctum.manchenkoff.me)\n\n## Quick Setup\n\n1. Add `nuxt-auth-sanctum` dependency to your project\n\n```bash\nnpx nuxi@latest module add nuxt-auth-sanctum\n```\n\n2. Add any required configuration in your `nuxt.config.ts` file\n\n```js\nexport default defineNuxtConfig({\n  modules: [\"nuxt-auth-sanctum\"],\n\n  sanctum: {\n    baseUrl: \"http://localhost:80\", // Laravel API\n  },\n});\n```\n\nThat's it! You can now use Nuxt Auth Sanctum in your Nuxt app ✨\n\nFor more details, check the documentation [here](https://sanctum.manchenkoff.me)\n\n<!-- Badges -->\n\n[npm-version-src]: https://img.shields.io/npm/v/nuxt-auth-sanctum/latest.svg?style=flat&colorA=18181B&colorB=28CF8D\n[npm-version-href]: https://npmjs.com/package/nuxt-auth-sanctum\n[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-auth-sanctum.svg?style=flat&colorA=18181B&colorB=28CF8D\n[npm-downloads-href]: https://npm.chart.dev/nuxt-auth-sanctum\n[license-src]: https://img.shields.io/npm/l/nuxt-auth-sanctum.svg?style=flat&colorA=18181B&colorB=28CF8D\n[license-href]: https://npmjs.com/package/nuxt-auth-sanctum\n[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js\n[nuxt-href]: https://nuxt.com\n\n### Powered by\n\n[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSource)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\n| Version  | Supported          |\n| -------- | ------------------ |\n| >= 0.3.0 | :white_check_mark: |\n| <= 0.2.0 | :x:                |\n\n## Reporting a Vulnerability\n\nIf you find a vulnerability, please get in touch with me via artem@manchenkoff.me or by opening a new issue in the repository.\n\nUsually, I respond in a few days but sometimes it might take a bit more.\n"
  },
  {
    "path": "TROUBLESHOOTING.md",
    "content": "# Troubleshooting Guide\n\nIf you experience any issues while using this module, please check this link - [Troubleshooting](https://sanctum.manchenkoff.me/advanced/troubleshooting).\n"
  },
  {
    "path": "docs/.env.example",
    "content": "# Public URL, used for OG Image when running nuxt generate\nNUXT_PUBLIC_SITE_URL=https://sanctum.manchenkoff.me\n\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "# Nuxt dev/build outputs\n.output\n.data\n.nuxt\n.nitro\n.cache\ndist\n\n# Node dependencies\nnode_modules\n\n# Logs\nlogs\n*.log\n\n# Misc\n.DS_Store\n.fleet\n.idea\n\n# Local env files\n.env\n.env.*\n!.env.example\n\n# VSC\n.history\n"
  },
  {
    "path": "docs/.npmrc",
    "content": "shamefully-hoist=true\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Nuxt - Laravel Sanctum Docs\n\nThis directory contains Nuxt Content project to deploy\nmodule documentation to Github Pages.\n\n## Setup\n\nMake sure to install the dependencies:\n\n```bash\npnpm install\n```\n\n## Development Server\n\nStart the development server on `http://localhost:3000`:\n\n```bash\npnpm dev\n```\n\n## Production\n\nBuild the application for production:\n\n```bash\npnpm build\n```\n\nOr generate all pages for static hosting via:\n\n```bash\npnpm generate\n```\n\nLocally preview production build:\n\n```bash\npnpm preview\n```\n\n## Validation\n\nTo check code quality, run the following command:\n\n```bash\npnpm validate\n```\n\nor separately:\n\n```bash\npnpm lint\npnpm typecheck\n```\n\n"
  },
  {
    "path": "docs/app/app.config.ts",
    "content": "const siteName = 'Nuxt - Laravel Sanctum'\n\nexport default defineAppConfig({\n  ui: {\n    colors: {\n      primary: 'red',\n      neutral: 'zinc'\n    },\n    footer: {\n      slots: {\n        root: 'border-t border-default',\n        left: 'text-sm text-muted'\n      }\n    }\n  },\n  seo: {\n    siteName: siteName\n  },\n  header: {\n    title: siteName,\n    to: '/',\n    logo: {\n      alt: 'Laravel Sanctum',\n      light: 'logo.svg',\n      dark: 'logo.svg'\n    },\n    search: true,\n    colorMode: true,\n    links: [\n      {\n        'icon': 'i-simple-icons-github',\n        'to': 'https://github.com/manchenkoff/nuxt-auth-sanctum',\n        'target': '_blank',\n        'aria-label': 'GitHub'\n      },\n      {\n        'icon': 'i-simple-icons-nuxt',\n        'to': 'https://nuxt.com/modules/nuxt-auth-sanctum',\n        'target': '_blank',\n        'aria-label': 'Nuxt Module'\n      }\n    ]\n  },\n  footer: {\n    credits: `Artem Manchenkov © ${new Date().getFullYear()}`,\n    colorMode: false,\n    links: [\n      {\n        'icon': 'i-simple-icons-github',\n        'to': 'https://github.com/manchenkoff',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on GitHub'\n      },\n      {\n        'icon': 'i-simple-icons-twitter',\n        'to': 'https://twitter.com/amanchenkov',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on X'\n      },\n      {\n        'icon': 'i-simple-icons-facebook',\n        'to': 'https://fb.com/manchenkoff',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on Facebook'\n      },\n      {\n        'icon': 'i-simple-icons-linkedin',\n        'to': 'https://linkedin.com/in/manchenkoff',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on LinkedIn'\n      },\n      {\n        'icon': 'i-simple-icons-instagram',\n        'to': 'https://instagram.com/manchenkof',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on Instagram'\n      },\n      {\n        'icon': 'i-simple-icons-threads',\n        'to': 'https://threads.net/@manchenkof',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on Threads'\n      },\n      {\n        'icon': 'i-simple-icons-youtube',\n        'to': 'https://youtube.com/@manchenkoff',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on YouTube'\n      },\n      {\n        'icon': 'i-simple-icons-medium',\n        'to': 'https://manchenkoff.medium.com/',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on Medium'\n      },\n      {\n        'icon': 'i-simple-icons-telegram',\n        'to': 'https://t.me/manchenkoff',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on Telegram'\n      },\n      {\n        'icon': 'i-simple-icons-bluesky',\n        'to': 'https://bsky.app/profile/manchenkoff.bsky.social',\n        'target': '_blank',\n        'aria-label': 'manchenkoff on Bluesky'\n      }\n    ]\n  },\n  toc: {\n    title: 'Table of Contents',\n    bottom: {\n      title: 'Ready to contribute?',\n      edit: 'https://github.com/manchenkoff/nuxt-auth-sanctum/edit/main/docs/content',\n      links: [\n        {\n          icon: 'i-lucide-star',\n          label: 'Star on GitHub',\n          to: 'https://github.com/manchenkoff/nuxt-auth-sanctum',\n          target: '_blank'\n        },\n        {\n          icon: 'i-lucide-git-pull-request-create',\n          label: 'Suggest a feature',\n          to: 'https://github.com/manchenkoff/nuxt-auth-sanctum/issues/new?template=feature_request.md',\n          target: '_blank'\n        },\n        {\n          icon: 'i-simple-icons-github',\n          label: 'Support project',\n          to: 'https://github.com/sponsors/manchenkoff?o=esb',\n          target: '_blank'\n        },\n        {\n          icon: 'i-simple-icons-buymeacoffee',\n          label: 'Buy me a coffee',\n          to: 'https://buymeacoffee.com/manchenkoff',\n          target: '_blank'\n        }\n      ]\n    }\n  }\n})\n"
  },
  {
    "path": "docs/app/app.vue",
    "content": "<script setup lang=\"ts\">\nconst { seo } = useAppConfig()\n\nconst { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))\nconst { data: files } = useLazyAsyncData(\n  'search', () => queryCollectionSearchSections('docs'),\n  { server: false }\n)\n\nuseHead({\n  meta: [\n    { name: 'viewport', content: 'width=device-width, initial-scale=1' }\n  ],\n  link: [\n    { rel: 'icon', href: '/logo.svg' }\n  ],\n  htmlAttrs: {\n    lang: 'en'\n  }\n})\n\nuseSeoMeta({\n  titleTemplate: `%s - ${seo?.siteName}`,\n  ogSiteName: seo?.siteName,\n  twitterCard: 'summary_large_image'\n})\n\nprovide('navigation', navigation)\n</script>\n\n<template>\n  <UApp>\n    <NuxtLoadingIndicator />\n\n    <AppHeader />\n\n    <UMain>\n      <NuxtLayout>\n        <NuxtPage />\n      </NuxtLayout>\n    </UMain>\n\n    <AppFooter />\n\n    <ClientOnly>\n      <LazyUContentSearch\n        :files=\"files\"\n        :navigation=\"navigation\"\n      />\n    </ClientOnly>\n  </UApp>\n</template>\n"
  },
  {
    "path": "docs/app/assets/css/main.css",
    "content": "@import \"tailwindcss\";\n@import \"@nuxt/ui\";\n\n@source \"../../../content/**/*\";\n\n@theme static {\n  --container-8xl: 90rem;\n  --font-sans: 'Public Sans', sans-serif;\n}\n\n:root {\n  --ui-container: var(--container-8xl);\n}\n"
  },
  {
    "path": "docs/app/components/AppFooter.vue",
    "content": "<script setup lang=\"ts\">\nconst { footer } = useAppConfig()\n</script>\n\n<template>\n  <UFooter>\n    <template #left>\n      {{ footer.credits }}\n    </template>\n\n    <template #right>\n      <UColorModeButton v-if=\"footer?.colorMode\" />\n\n      <template v-if=\"footer?.links\">\n        <UButton\n          v-for=\"(link, index) of footer?.links\"\n          :key=\"index\"\n          v-bind=\"{ color: 'neutral', variant: 'ghost', ...link }\"\n        />\n      </template>\n    </template>\n  </UFooter>\n</template>\n"
  },
  {
    "path": "docs/app/components/AppHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContentNavigationItem } from '@nuxt/content'\n\nconst navigation = inject<Ref<ContentNavigationItem[]>>('navigation')\n\nconst { header } = useAppConfig()\n</script>\n\n<template>\n  <UHeader\n    :ui=\"{ center: 'flex-1' }\"\n    :to=\"header?.to || '/'\"\n  >\n    <UContentSearchButton\n      v-if=\"header?.search\"\n      :collapsed=\"false\"\n      class=\"w-full\"\n    />\n\n    <template #title>\n      <AppLogo />\n    </template>\n\n    <template #right>\n      <UContentSearchButton\n        v-if=\"header?.search\"\n        class=\"lg:hidden\"\n      />\n\n      <UColorModeButton v-if=\"header?.colorMode\" />\n\n      <template v-if=\"header?.links\">\n        <UButton\n          v-for=\"(link, index) of header.links\"\n          :key=\"index\"\n          v-bind=\"{ color: 'neutral', variant: 'ghost', ...link }\"\n        />\n      </template>\n    </template>\n\n    <template #body>\n      <UContentNavigation\n        highlight\n        :navigation=\"navigation\"\n      />\n    </template>\n  </UHeader>\n</template>\n"
  },
  {
    "path": "docs/app/components/AppLogo.vue",
    "content": "<script lang=\"ts\" setup>\nconst { header } = useAppConfig()\n</script>\n\n<template>\n  <div class=\"flex gap-2 items-center\">\n    <UColorModeImage\n      v-if=\"header?.logo?.dark || header?.logo?.light\"\n      :light=\"header?.logo?.light!\"\n      :dark=\"header?.logo?.dark!\"\n      :alt=\"header?.logo?.alt\"\n      class=\"h-8 w-auto shrink-0\"\n    />\n\n    <span>{{ header.title }}</span>\n  </div>\n</template>\n\n<style scoped></style>\n"
  },
  {
    "path": "docs/app/components/OgImage/OgImageDocs.takumi.vue",
    "content": "<script lang=\"ts\" setup>\ninterface ComponentProps {\n  title?: string\n  description?: string\n  headline?: string\n}\n\nconst props = withDefaults(\n  defineProps<ComponentProps>(),\n  {\n    title: 'title',\n    description: 'description'\n  }\n)\n\nconst title = computed(() => (props.title || '').slice(0, 60))\nconst description = computed(() => (props.description || '').slice(0, 200))\n</script>\n\n<template>\n  <div class=\"w-full h-full flex flex-col justify-center bg-[#020420]\">\n    <svg\n      class=\"absolute right-0 top-0\"\n      width=\"629\"\n      height=\"593\"\n      viewBox=\"0 0 629 593\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <g filter=\"url(#filter0_f_199_94966)\">\n        <path\n          d=\"M628.5 -578L639.334 -94.4223L806.598 -548.281L659.827 -87.387L965.396 -462.344L676.925 -74.0787L1087.69 -329.501L688.776 -55.9396L1160.22 -164.149L694.095 -34.9354L1175.13 15.7948L692.306 -13.3422L1130.8 190.83L683.602 6.50012L1032.04 341.989L668.927 22.4412L889.557 452.891L649.872 32.7537L718.78 511.519L628.5 36.32L538.22 511.519L607.128 32.7537L367.443 452.891L588.073 22.4412L224.955 341.989L573.398 6.50012L126.198 190.83L564.694 -13.3422L81.8734 15.7948L562.905 -34.9354L96.7839 -164.149L568.224 -55.9396L169.314 -329.501L580.075 -74.0787L291.604 -462.344L597.173 -87.387L450.402 -548.281L617.666 -94.4223L628.5 -578Z\"\n          fill=\"#00DC82\"\n        />\n      </g>\n      <defs>\n        <filter\n          id=\"filter0_f_199_94966\"\n          x=\"0.873535\"\n          y=\"-659\"\n          width=\"1255.25\"\n          height=\"1251.52\"\n          filterUnits=\"userSpaceOnUse\"\n          color-interpolation-filters=\"sRGB\"\n        >\n          <feFlood\n            flood-opacity=\"0\"\n            result=\"BackgroundImageFix\"\n          />\n          <feBlend\n            mode=\"normal\"\n            in=\"SourceGraphic\"\n            in2=\"BackgroundImageFix\"\n            result=\"shape\"\n          />\n          <feGaussianBlur\n            stdDeviation=\"40.5\"\n            result=\"effect1_foregroundBlur_199_94966\"\n          />\n        </filter>\n      </defs>\n    </svg>\n\n    <div class=\"pl-[100px]\">\n      <p\n        v-if=\"headline\"\n        class=\"uppercase text-[24px] text-[#00DC82] mb-4 font-semibold\"\n      >\n        {{ headline }}\n      </p>\n      <h1\n        v-if=\"title\"\n        class=\"m-0 text-[75px] font-semibold mb-4 text-white flex items-center\"\n      >\n        <span>{{ title }}</span>\n      </h1>\n      <p\n        v-if=\"description\"\n        class=\"text-[32px] text-[#E4E4E7] leading-tight w-[700px]\"\n      >\n        {{ description }}\n      </p>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "docs/app/components/content/HeroBackground.vue",
    "content": "<script setup lang=\"ts\">\nconst { isLoading } = useLoadingIndicator()\n\nconst appear = ref(false)\nconst appeared = ref(false)\n\nonMounted(() => {\n  setTimeout(() => {\n    appear.value = true\n    setTimeout(() => {\n      appeared.value = true\n    }, 1000)\n  }, 0)\n})\n</script>\n\n<template>\n  <div\n    class=\"absolute w-full -top-px transition-all text-primary shrink-0\"\n    :class=\"[\n      isLoading ? 'animate-pulse' : (appear ? '' : 'opacity-0'),\n      appeared ? 'duration-[400ms]': 'duration-1000'\n    ]\"\n  >\n    <svg\n      viewBox=\"0 0 1440 181\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      class=\"pointer-events-none\"\n    >\n      <mask\n        id=\"path-1-inside-1_414_5526\"\n        fill=\"white\"\n      >\n        <path d=\"M0 0H1440V181H0V0Z\" />\n      </mask>\n      <path\n        d=\"M0 0H1440V181H0V0Z\"\n        fill=\"url(#paint0_linear_414_5526)\"\n        fill-opacity=\"0.22\"\n      />\n      <path\n        d=\"M0 2H1440V-2H0V2Z\"\n        fill=\"url(#paint1_linear_414_5526)\"\n        mask=\"url(#path-1-inside-1_414_5526)\"\n      />\n      <defs>\n        <linearGradient\n          id=\"paint0_linear_414_5526\"\n          x1=\"720\"\n          y1=\"0\"\n          x2=\"720\"\n          y2=\"181\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stop-color=\"currentColor\" />\n          <stop\n            offset=\"1\"\n            stop-color=\"currentColor\"\n            stop-opacity=\"0\"\n          />\n        </linearGradient>\n        <linearGradient\n          id=\"paint1_linear_414_5526\"\n          x1=\"0\"\n          y1=\"90.5\"\n          x2=\"1440\"\n          y2=\"90.5\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop\n            stop-color=\"currentColor\"\n            stop-opacity=\"0\"\n          />\n          <stop\n            offset=\"0.395\"\n            stop-color=\"currentColor\"\n          />\n          <stop\n            offset=\"1\"\n            stop-color=\"currentColor\"\n            stop-opacity=\"0\"\n          />\n        </linearGradient>\n      </defs>\n    </svg>\n  </div>\n</template>\n"
  },
  {
    "path": "docs/app/components/content/StarsBackground.vue",
    "content": "<script setup lang=\"ts\">\ninterface ComponentProps {\n  starCount?: number\n  color?: string\n  speed?: 'slow' | 'normal' | 'fast'\n  size?: { min: number, max: number }\n}\n\ninterface Star {\n  x: number\n  y: number\n  size: number\n}\n\nconst props = withDefaults(\n  defineProps<ComponentProps>(),\n  {\n    starCount: 300,\n    color: 'var(--ui-primary)',\n    speed: 'normal',\n    size: () => ({\n      min: 1,\n      max: 2\n    })\n  }\n)\n\n// Generate random star positions and sizes\nconst generateStars = (count: number): Star[] => {\n  return Array.from({ length: count }, () => ({\n    x: Math.floor(Math.random() * 2000),\n    y: Math.floor(Math.random() * 2000),\n    size: typeof props.size === 'number'\n      ? props.size\n      : Math.random() * (props.size.max - props.size.min) + props.size.min\n  }))\n}\n\n// Define speed configurations once\nconst speedMap = {\n  slow: { duration: 200, opacity: 0.5, ratio: 0.3 },\n  normal: { duration: 150, opacity: 0.75, ratio: 0.3 },\n  fast: { duration: 100, opacity: 1, ratio: 0.4 }\n}\n\n// Use a more efficient approach to generate and store stars\nconst stars = useState<{ slow: Star[], normal: Star[], fast: Star[] }>('stars', () => {\n  return {\n    slow: generateStars(Math.floor(props.starCount * speedMap.slow.ratio)),\n    normal: generateStars(Math.floor(props.starCount * speedMap.normal.ratio)),\n    fast: generateStars(Math.floor(props.starCount * speedMap.fast.ratio))\n  }\n})\n\n// Compute star layers with different speeds and opacities\nconst starLayers = computed(() => [\n  { stars: stars.value.fast, ...speedMap.fast },\n  { stars: stars.value.normal, ...speedMap.normal },\n  { stars: stars.value.slow, ...speedMap.slow }\n])\n</script>\n\n<template>\n  <div class=\"absolute pointer-events-none z-[-1] inset-y-0 inset-x-5 sm:inset-x-7 lg:inset-x-9 overflow-hidden\">\n    <svg\n      class=\"absolute inset-0 pointer-events-none\"\n      viewBox=\"0 0 1017 181\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <g opacity=\"0.5\">\n        <mask\n          id=\"path-1-inside-1_846_160841\"\n          fill=\"white\"\n        >\n          <path d=\"M0 0H1017V181H0V0Z\" />\n        </mask>\n        <path\n          d=\"M0 0H1017V181H0V0Z\"\n          fill=\"url(#paint0_radial_846_160841)\"\n          fill-opacity=\"0.22\"\n        />\n      </g>\n      <defs>\n        <radialGradient\n          id=\"paint0_radial_846_160841\"\n          cx=\"0\"\n          cy=\"0\"\n          r=\"1\"\n          gradientUnits=\"userSpaceOnUse\"\n          gradientTransform=\"translate(508.999 19.5) rotate(90.177) scale(161.501 509.002)\"\n        >\n          <stop stop-color=\"var(--ui-primary)\" />\n          <stop\n            offset=\"1\"\n            stop-color=\"var(--ui-primary)\"\n            stop-opacity=\"0\"\n          />\n        </radialGradient>\n        <linearGradient\n          id=\"paint1_linear_846_160841\"\n          x1=\"10.9784\"\n          y1=\"91\"\n          x2=\"1017\"\n          y2=\"90.502\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop\n            stop-color=\"var(--ui-primary)\"\n            stop-opacity=\"0\"\n          />\n          <stop\n            offset=\"0.395\"\n            stop-color=\"var(--ui-primary)\"\n          />\n          <stop\n            offset=\"1\"\n            stop-color=\"var(--ui-primary)\"\n            stop-opacity=\"0\"\n          />\n        </linearGradient>\n      </defs>\n    </svg>\n\n    <div class=\"stars size-full absolute inset-x-0 top-0\">\n      <div\n        v-for=\"(layer, index) in starLayers\"\n        :key=\"index\"\n        class=\"star-layer\"\n        :style=\"{\n          '--star-duration': `${layer.duration}s`,\n          '--star-opacity': layer.opacity,\n          '--star-color': color\n        }\"\n      >\n        <div\n          v-for=\"(star, starIndex) in layer.stars\"\n          :key=\"starIndex\"\n          class=\"star absolute rounded-full\"\n          :style=\"{\n            left: `${star.x}px`,\n            top: `${star.y}px`,\n            width: `${star.size}px`,\n            height: `${star.size}px`,\n            backgroundColor: 'var(--star-color)',\n            opacity: 'var(--star-opacity)'\n          }\"\n        />\n      </div>\n    </div>\n  </div>\n</template>\n\n<style scoped>\n.stars {\n  left: 50%;\n  transform: translate(-50%);\n  -webkit-mask-image: linear-gradient(180deg,\n      rgba(217, 217, 217, 0) 0%,\n      rgba(217, 217, 217, 0.8) 25%,\n      #d9d9d9 50%,\n      rgba(217, 217, 217, 0.8) 75%,\n      rgba(217, 217, 217, 0) 100%);\n  mask-image: linear-gradient(180deg,\n      rgba(217, 217, 217, 0) 0%,\n      rgba(217, 217, 217, 0.8) 25%,\n      #d9d9d9 50%,\n      rgba(217, 217, 217, 0.8) 75%,\n      rgba(217, 217, 217, 0) 100%);\n  -webkit-mask-size: cover;\n  mask-size: cover;\n}\n\n.star-layer {\n  animation: risingStarsAnimation linear infinite;\n  animation-duration: var(--star-duration);\n  will-change: transform;\n}\n\n@keyframes risingStarsAnimation {\n  0% {\n    transform: translateY(0);\n  }\n  100% {\n    transform: translateY(-2000px);\n  }\n}\n</style>\n"
  },
  {
    "path": "docs/app/error.vue",
    "content": "<script setup lang=\"ts\">\nimport type { NuxtError } from '#app'\n\ninterface ComponentProps {\n  error: NuxtError\n}\n\ndefineProps<ComponentProps>()\n\nuseHead({\n  htmlAttrs: {\n    lang: 'en'\n  }\n})\n\nuseSeoMeta({\n  title: 'Page not found',\n  description: 'We are sorry but this page could not be found.'\n})\n\nconst { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))\nconst { data: files } = useLazyAsyncData(\n  'search', () => queryCollectionSearchSections('docs'),\n  { server: false }\n)\n\nprovide('navigation', navigation)\n</script>\n\n<template>\n  <UApp>\n    <AppHeader />\n\n    <UError :error=\"error\" />\n\n    <AppFooter />\n\n    <ClientOnly>\n      <LazyUContentSearch\n        :files=\"files\"\n        :navigation=\"navigation\"\n      />\n    </ClientOnly>\n  </UApp>\n</template>\n"
  },
  {
    "path": "docs/app/layouts/docs.vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContentNavigationItem } from '@nuxt/content'\n\nconst navigation = inject<Ref<ContentNavigationItem[]>>('navigation')\n</script>\n\n<template>\n  <UContainer>\n    <UPage>\n      <template #left>\n        <UPageAside>\n          <UContentNavigation\n            highlight\n            :navigation=\"navigation\"\n          />\n        </UPageAside>\n      </template>\n\n      <slot />\n    </UPage>\n  </UContainer>\n</template>\n"
  },
  {
    "path": "docs/app/pages/[...slug].vue",
    "content": "<script setup lang=\"ts\">\nimport type { ContentNavigationItem } from '@nuxt/content'\nimport { findPageHeadline } from '@nuxt/content/utils'\n\ndefinePageMeta({\n  layout: 'docs'\n})\n\nconst route = useRoute()\nconst { toc } = useAppConfig()\nconst navigation = inject<Ref<ContentNavigationItem[]>>('navigation')\n\nconst { data: page } = await useAsyncData(\n  route.path,\n  () => queryCollection('docs').path(route.path).first()\n)\n\nif (!page.value) {\n  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })\n}\n\nconst { data: surround } = await useAsyncData(\n  `${route.path}-surround`,\n  () => {\n    return queryCollectionItemSurroundings(\n      'docs',\n      route.path,\n      { fields: ['description'] }\n    )\n  }\n)\n\nconst title = page.value.seo?.title || page.value.title\nconst description = page.value.seo?.description || page.value.description\n\nuseSeoMeta({\n  title,\n  ogTitle: title,\n  description,\n  ogDescription: description\n})\n\nconst headline = computed(() => findPageHeadline(navigation?.value, page.value?.path))\n\ndefineOgImageComponent(\n  'Docs',\n  {\n    headline: headline.value\n  }\n)\n\nconst links = computed(() => {\n  const links = []\n\n  if (toc?.bottom?.edit) {\n    links.push({\n      icon: 'i-lucide-external-link',\n      label: 'Edit this page',\n      to: `${toc.bottom.edit}/${page?.value?.stem}.${page?.value?.extension}`,\n      target: '_blank'\n    })\n  }\n\n  return [\n    ...links,\n    ...(toc?.bottom?.links || [])\n  ].filter(Boolean)\n})\n</script>\n\n<template>\n  <UPage v-if=\"page\">\n    <UPageHeader\n      :title=\"page.title\"\n      :description=\"page.description\"\n      :headline=\"headline\"\n    >\n      <template #links>\n        <UButton\n          v-for=\"(link, index) in page.links\"\n          :key=\"index\"\n          v-bind=\"link\"\n        />\n      </template>\n    </UPageHeader>\n\n    <UPageBody>\n      <ContentRenderer\n        v-if=\"page\"\n        :value=\"page\"\n      />\n\n      <USeparator v-if=\"surround?.length\" />\n\n      <UContentSurround :surround=\"surround\" />\n    </UPageBody>\n\n    <template\n      v-if=\"page?.body?.toc?.links?.length\"\n      #right\n    >\n      <UContentToc\n        :title=\"toc?.title\"\n        :links=\"page.body?.toc?.links\"\n      >\n        <template\n          v-if=\"toc?.bottom\"\n          #bottom\n        >\n          <div\n            class=\"hidden lg:block space-y-6\"\n            :class=\"{ '!mt-6': page.body?.toc?.links?.length }\"\n          >\n            <USeparator\n              v-if=\"page.body?.toc?.links?.length\"\n              type=\"dashed\"\n            />\n\n            <UPageLinks\n              :title=\"toc.bottom.title\"\n              :links=\"links\"\n            />\n          </div>\n        </template>\n      </UContentToc>\n    </template>\n  </UPage>\n</template>\n"
  },
  {
    "path": "docs/app/pages/index.vue",
    "content": "<script setup lang=\"ts\">\nconst { data: page } = await useAsyncData('index', () => queryCollection('landing').path('/').first())\n\nif (!page.value) {\n  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })\n}\n\nconst title = page.value.seo?.title || page.value.title\nconst description = page.value.seo?.description || page.value.description\n\nuseSeoMeta({\n  titleTemplate: '',\n  title,\n  ogTitle: title,\n  description,\n  ogDescription: description\n})\n\ndefineOgImageComponent(\n  'Docs',\n  {\n    headline: 'Nuxt Laravel Sanctum'\n  }\n)\n</script>\n\n<template>\n  <ContentRenderer\n    v-if=\"page\"\n    :value=\"page\"\n    :prose=\"false\"\n  />\n</template>\n"
  },
  {
    "path": "docs/content/1.getting-started/.navigation.yml",
    "content": "title: Getting Started\nicon: false\n"
  },
  {
    "path": "docs/content/1.getting-started/1.index.md",
    "content": "---\ntitle: Introduction\ndescription: Welcome to Nuxt Laravel Sanctum module documentation\nnavigation:\n  icon: i-simple-icons-laravel\n---\n\nThis module provides a simple way to use Laravel Sanctum with Nuxt. SSR-ready!\n\n## Key Features\n\nThis module includes a range of features designed to streamline authentication:\n\n- `useSanctumAuth` composable for easy access to the current user and authentication methods\n- `useSanctumFetch` and `useLazySanctumFetch` to load data from your API\n- Automated `CSRF` token header and cookie management\n- Automated `Bearer` token header management\n- Both `CSR` and `SSR` modes support\n- Pre-configured middleware for pages that require authentication\n- Cast current user information to any class you want\n- Custom `request` and `response` interceptors\n- Subscribe to `sanctum:*` hooks to react as you want\n- Compatible with default Nuxt `ofetch` client\n- TypeScript support\n- ... and more, check the docs!\n\n::warning\n---\ntarget: _blank\nto: https://laravel.com/docs/10.x/sanctum#spa-authentication\n---\n**Note**: Before using this module, please ensure you have configured Laravel Sanctum on your backend. \nYou can find more information about Laravel Sanctum here.\n::\n\nWe recommend looking at our [breeze-nuxt](https://github.com/manchenkoff/breeze-nuxt) template that works flawlessly with \n[breeze-api](https://github.com/manchenkoff/breeze-api) Laravel application with preconfigured Sanctum and Echo modules.\n\n## Ecosystem\n\nThis project is a part of Nuxt Laravel modules ecosystem which you may find useful:\n\n::card-group\n  :::card\n  ---\n  icon: i-lucide-lock\n  target: _blank\n  title: Sanctum\n  to: https://github.com/manchenkoff/nuxt-auth-sanctum\n  ---\n  Module for Sanctum authentication\n  :::\n\n  :::card\n  ---\n  icon: i-lucide-radio\n  target: _blank\n  title: Echo\n  to: https://github.com/manchenkoff/nuxt-laravel-echo\n  ---\n  Module for Echo broadcasting\n  :::\n\n  :::card\n  ---\n  icon: i-lucide-badge-check\n  target: _blank\n  title: Precognition\n  to: https://github.com/manchenkoff/nuxt-sanctum-precognition\n  ---\n  Module for Precognition form validation and Nuxt UI support, based on Sanctum\n  :::\n\n  :::card\n  ---\n  icon: i-simple-icons-nuxt\n  target: _blank\n  title: Breeze Nuxt\n  to: https://github.com/manchenkoff/breeze-nuxt\n  ---\n  Nuxt application starter with configured modules for Laravel\n  :::\n\n  :::card\n  ---\n  icon: i-simple-icons-laravel\n  target: _blank\n  title: Breeze API\n  to: https://github.com/manchenkoff/breeze-api\n  ---\n  Laravel API application starter with preconfigured Sanctum, Echo and Precognition\n  :::\n::\n\n## Support\n\nIf you like this module, please support the project to help me maintain and improve it!\n\n<a href=\"https://www.buymeacoffee.com/manchenkoff\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" style=\"height: 60px !important;width: 217px !important;\" ></a>\n\n\n"
  },
  {
    "path": "docs/content/1.getting-started/2.installation.md",
    "content": "---\ntitle: Installation\ndescription: How to add nuxt-auth-sanctum to your Nuxt application!\nnavigation:\n  icon: i-lucide-download\n---\n\n## Quick Start\n\nYou can use the following command to install the module \nand automatically register it in your `nuxt.config.ts` modules section\n\n```bash [Terminal]\nnpx nuxi@latest module add nuxt-auth-sanctum\n```\n\nor manually install a dependency via:\n\n```bash [Terminal]\npnpm add nuxt-auth-sanctum\n```\n\nand register the module in your `nuxt.config.ts`:\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n  modules: [\n    // other modules\n    'nuxt-auth-sanctum'\n  ],\n\n  sanctum: {},\n})\n```\n\n## Configuration\n\nOnce you have the module installed and registered, provide the\nconfiguration in `nuxt.config.ts` according to your setup.\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n  //... other parts of the config\n\n  // nuxt-auth-sanctum options (also configurable via environment variables)\n  sanctum: {\n    baseUrl: 'http://localhost:80', // Laravel API\n  }\n})\n```\n\nThat's it! You can now use Nuxt Auth Sanctum in your Nuxt app ✨\n"
  },
  {
    "path": "docs/content/2.usage/1.configuration.md",
    "content": "---\ntitle: Configuration\ndescription: How to configure nuxt-auth-sanctum for Nuxt\nnavigation:\n  icon: i-lucide-cog\n---\n\n## Initial setup\n\nThe only required configuration option is `baseUrl` which will be used for API calls to your Laravel API, \nso you can start using the module with the following definition:\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: ['nuxt-auth-sanctum'],\n\n    sanctum: {\n        baseUrl: 'http://localhost:80', // Laravel API\n    },\n})\n```\n\n## Available options\n\nFor any additional configurations, you can adjust the next list of available parameters:\n\n| Parameter | Description | Default |\n| --------- | ----------- | ------- |\n| `baseUrl` | The base URL of the Laravel API | `undefined` |\n| `mode` | Authentication mode to work with Laravel API. Supported values - `cookie`, `token`. | `cookie` |\n| `origin` | The URL of the current application to use in Referrer header | `useRequestUrl().origin` |\n| `userStateKey` | The key to use to store the user identity in the `useState` variable. | `sanctum.user.identity` |\n| `redirectIfAuthenticated` | Determine whether to redirect the user if it is already authenticated on a login attempt. | `false` |\n| `redirectIfUnauthenticated` | Determine whether to redirect when the user got unauthenticated on any API request. | `false` |\n| `endpoints.csrf` | The endpoint to request a new CSRF token | `/sanctum/csrf-cookie` |\n| `endpoints.login` | The endpoint to send user credentials to authenticate | `/login` |\n| `endpoints.logout` | The endpoint to destroy current user session | `/logout` |\n| `endpoints.user` | The endpoint to fetch current user data | `/api/user` |\n| `csrf.cookie` | Name of the CSRF cookie to extract from server response | `XSRF-TOKEN` |\n| `csrf.header` | Name of the CSRF header to pass from client to server | `X-XSRF-TOKEN` |\n| `client.retry` | The number of times to retry a request when it fails | `false` |\n| `client.initialRequest` | Determines whether to request the user identity on plugin initialization | `true` |\n| `redirect.keepRequestedRoute` | Determines whether to keep the requested route when redirecting after login | `false` |\n| `redirect.onLogin` | Route to redirect to when user is authenticated. If set to false, do nothing | `/` |\n| `redirect.onLogout` | Route to redirect to when user is not authenticated. If set to false, do nothing | `/` |\n| `redirect.onAuthOnly` | Route to redirect to when user has to be authenticated. If set to false, do nothing | `/login` |\n| `redirect.onGuestOnly` | Route to redirect to when user has to be a guest. If set to false, do nothing | `/` |\n| `globalMiddleware.enabled` | Determines whether the global middleware is enabled | `false` |\n| `globalMiddleware.prepend` | Determines whether the global middleware is prepended to the list of middlewares | `false` |\n| `globalMiddleware.allow404WithoutAuth` | Determines whether to allow 404 page without authentication | `true` |\n| `logLevel` | The level to use for the logger. More details [here](/advanced/logging). | `3` |\n| `appendPlugin` | Determines whether to append the plugin to the Nuxt application. More details [here](https://nuxt.com/docs/api/kit/plugins#options). | `false` |\n| `serverProxy.enabled` | Determines whether the server side proxy is enabled. Available on server-side only. | `false` |\n| `serverProxy.route` | Nuxt server route to catch all requests. This route will receive any nested path as well. Available on server-side only. | `/api/sanctum` |\n| `serverProxy.baseUrl` | The base URL of the Laravel API. Available on server-side only. | `http://localhost:80` |\n\nFor more details, please check the source code - [options.ts](https://github.com/manchenkoff/nuxt-auth-sanctum/blob/main/src/runtime/types/options.ts).\n\n## Overrides\n\nYou can override any of these options in the `nuxt.config.ts` file:\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: ['nuxt-auth-sanctum'],\n\n    sanctum: {\n        baseUrl: 'http://localhost:80', // Your Laravel API\n        redirect: {\n            onLogin: '/dashboard', // Custom route after successful login\n        },\n    },\n})\n```\n\n## RuntimeConfig\n\nModule configuration is exposed to `runtimeConfig` property of your Nuxt app,\nso you can override either in sanctum module config or `runtimeConfig.public.sanctum` property.\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: ['nuxt-auth-sanctum'],\n\n    runtimeConfig: {\n        public: {\n            sanctum: {\n                baseUrl: 'http://localhost:80',\n            },\n        },\n    },\n})\n```\n\n## Server vs Client Configuration\n\nThe module supports different configurations for server-side (SSR) and client-side (CSR) contexts.\n\n### Configuration Priority\n\n| Context | Priority (highest to lowest) |\n|---------|------------------------------|\n| Server | `runtimeConfig.sanctum` → `runtimeConfig.public.sanctum` → module defaults |\n| Client | `runtimeConfig.public.sanctum` → module defaults |\n\n### Examples\n\n**Shared config** (same for both server and client):\n```typescript [nuxt.config.ts]\nruntimeConfig: {\n    public: {\n        sanctum: {\n            baseUrl: 'http://localhost:80',\n        },\n    },\n}\n```\n\n**Different config** (server vs client):\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: ['nuxt-auth-sanctum'],\n\n    // Default values for both server and client\n    sanctum: {\n        baseUrl: 'http://localhost:80',\n        logLevel: 3,\n    },\n\n    // Server-specific overrides\n    runtimeConfig: {\n        sanctum: {\n            baseUrl: 'http://laravel:80',  // Docker internal URL\n            logLevel: 4,                   // Verbose server logs\n        },\n\n        // Client-specific overrides\n        public: {\n            sanctum: {\n                baseUrl: 'https://myapp.com',  // Public TLD\n                logLevel: 2,                   // Minimal client logs\n            }\n        }\n    },\n})\n```\n\n### Server-Only Options\n\nThe following options are only available on the server-side:\n- `serverProxy.enabled`\n- `serverProxy.route`\n- `serverProxy.baseUrl`\n\n## Environment variables\n\nIt is possible to override options via environment variables too. \nIt might be useful when you want to use `.env` file to provide baseUrl for Laravel API.\n\n::warning\nIf you are using SSR (Server-Side Rendering) and relying *entirely* on `.env` files rather than hardcoding the `baseUrl` in your `nuxt.config.ts`, you **must** provide both the public and private environment variables. Otherwise, the server-side fetch will not see the public variable and the page will hang indefinitely with an infinite loop of SSR requests.\n::\n\nHere is what it should look like in your `.env` file:\n\n```env [.env]\n# Used by the browser (CSR)\nNUXT_PUBLIC_SANCTUM_BASE_URL='http://localhost:8000'\n\n# Used by the Nuxt server (SSR)\nNUXT_SANCTUM_BASE_URL='http://localhost:8000'\n```\n\n::warning\nThe `origin` option requires a static default in `nuxt.config.ts` for environment\nvariables to work. Unlike other options, Nuxt ignores env var overrides for keys\nthat are `undefined` by default.\n\n```typescript\nsanctum: {\n    origin: 'http://localhost:3000', // Set static default first\n}\n```\n\nThen in your `.env`:\n```env\n# For client-side (CSR)\nNUXT_PUBLIC_SANCTUM_ORIGIN=https://your-domain.com\n\n# For server-side (SSR)\nNUXT_SANCTUM_ORIGIN=https://your-domain.com\n```\n\nNote: `NUXT_PUBLIC_SANCTUM_ORIGIN` only affects client-side, while `NUXT_SANCTUM_ORIGIN`\nonly affects server-side.\n::\n\n\n## Configuration example\n\nHere is an example of a full module configuration\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: ['nuxt-auth-sanctum'],\n\n    sanctum: {\n        mode: 'cookie',\n        userStateKey: 'sanctum.user.identity',\n        redirectIfAuthenticated: false,\n        redirectIfUnauthenticated: false,\n        endpoints: {\n            csrf: '/sanctum/csrf-cookie',\n            login: '/login',\n            logout: '/logout',\n            user: '/api/user',\n        },\n        csrf: {\n            cookie: 'XSRF-TOKEN',\n            header: 'X-XSRF-TOKEN',\n        },\n        client: {\n            retry: false,\n            initialRequest: true,\n        },\n        redirect: {\n            keepRequestedRoute: false,\n            onLogin: '/',\n            onLogout: '/',\n            onAuthOnly: '/login',\n            onGuestOnly: '/',\n        },\n        globalMiddleware: {\n            enabled: false,\n            allow404WithoutAuth: true,\n        },\n        logLevel: 3,\n        appendPlugin: false,\n    }\n})\n```\n\n"
  },
  {
    "path": "docs/content/2.usage/2.cookie.md",
    "content": "---\ntitle: Cookie Authentication\ndescription: Set up cookie CSRF authentication for Sanctum\nnavigation:\n  icon: i-lucide-cookie\n---\n\n## Usage\n\nBy default, the module provides configuration to integrate seamlessly with Laravel Sanctum authentication based on the XSRF token.\n\nTo explicitly set this authentication mode, update `sanctum.mode` configuration property to `cookie`.\n\nYou can check the official Laravel documentation here - [SPA Authentication](https://laravel.com/docs/12.x/sanctum#spa-authentication).\n\n::warning\nNuxt and Laravel applications must share the same top-level domain. For instance:\n- Nuxt application - `domain.com`\n- Laravel application - `api.domain.com`\n::\n\n## How it works\n\nFirst, you need to authenticate a user by submitting credentials to `endpoints.login` endpoint:\n\n```typescript\nconst { login } = useSanctumAuth()\n\nconst credentials = {\n    email: \"john@doe.com\",\n    password: \"password\",\n    remember: true,\n}\n\nawait login(credentials)\n```\n\nThe client will be automatically redirected to `redirect.onLogin` route of your application.\n\nOnce the module has an authentication state, it will take care of requesting a CSRF cookie from the API \nand passing it as an XSRF header to each subsequent request as well as passing all other \nheaders and cookies from CSR to SSR requests.\n\nYou can also [extend default interceptors](/advanced/interceptors) \nand add your information into headers or cookie collections.\n\nTo check other available methods, please refer to the composables section.\n\n## Laravel configuration\n\nYour Laravel API should be configured properly to support Nuxt domain and share cookies:\n\n- The Nuxt application domain should be registered in `stateful` domain list (`SANCTUM_STATEFUL_DOMAINS`)\n- The Nuxt application domain should be registered in `config/cors.php` in `allowed_origins` domain list\n- Also `config/cors.php` configuration should have `support_credentials=true`\n- Sanctum `statefulApi` middleware should be enabled\n- The top-level domain should be used for the session (`SESSION_DOMAIN=.domain.com`), or `localhost` during development (without port)\n\nIf you notice incorrect behavior of the module or authentication flow, \nfeel free to [raise an issue](https://github.com/manchenkoff/nuxt-auth-sanctum/issues/new/choose)!\n"
  },
  {
    "path": "docs/content/2.usage/3.token.md",
    "content": "---\ntitle: Token Authentication\ndescription: Set up cookie Bearer token authentication for Sanctum\nnavigation:\n  icon: i-lucide-shield\n---\n\n## Usage\n\n::caution\nBeware, that token-based authentication is not recommended for SPA applications.\n::\n\nSometimes, token authentication might be useful when you cannot host your application on the same TLD \nor have a mobile or desktop application built with Nuxt (e.g. based on Capacitor).\n\nTo explicitly set this authentication mode, update `sanctum.mode` configuration property to `token`.\n\nYou can check the official Laravel documentation here - [API Token Authentication](https://laravel.com/docs/12.x/sanctum#api-token-authentication).\n\n## How it works\n\nFirst, you need to authenticate a user by submitting credentials to `endpoints.login` endpoint:\n\n```typescript\nconst { login } = useSanctumAuth()\n\nconst credentials = {\n    email: \"john@doe.com\",\n    password: \"password\",\n    remember: true,\n}\n\nawait login(credentials)\n```\n\nThe client will be automatically redirected to `redirect.onLogin` route of your application.\n\nTo check other available methods, please refer to the **composables** section.\n\nThe module expects a plain token value in the response from the API that can be stored in \ncookies to be included in all subsequent requests as `Authorization` header.\n\nYou can also implement your [own token storage](https://sanctum.manchenkoff.me/advanced/token-storage) \nif cookies are not supported, for example - *Capacitor, Ionic, LocalStorage, etc*.\n\n## Laravel configuration\n\nYour API should have at least two endpoints for login and logout which are included in `api.php` routes, \nso make sure that you do not use the same endpoints as for cookie-based authentication (`web.php` routes) \nto avoid **CSRF token mismatch** errors.\n\n```php [routes/api.php]\n<?php \n\nRoute::middleware(['guest'])->post('/login', [TokenAuthenticationController::class, 'store']);\nRoute::middleware(['auth:sanctum'])->post('/logout', [TokenAuthenticationController::class, 'destroy']);\n```\n\n::warning\nKeep in mind, that the domain where API requests are coming from should not be \nincluded in `SANCTUM_STATEFUL_DOMAINS` variable, otherwise you will get a **CSRF mismatch error**.\n::\n\nThe login endpoint must return a JSON response that contains `token` key like this\n\n```json\n{\n    \"token\": \"<plain_token_value>\"\n}\n```\n\nHere you can find an example from official documentation - [Issue API Token](https://laravel.com/docs/12.x/sanctum#issuing-api-tokens).\n\nThe logout endpoint should revoke the current client token to avoid inconsistencies with your Nuxt application state, \nplease check official documentation - [Revoke API Tokens](https://laravel.com/docs/12.x/sanctum#revoking-tokens).\n\n::tip\nYou can also try our API template with the already implemented authentication \nlogic for both cookie and token approach - [breeze-nuxt](/advanced/breeze-nuxt-template).\n::\n\n## Custom token storage\n\nDefault token storage uses cookies to keep the API Authentication token and automatically load it for both CSR and SSR requests. \nHowever, you are free to define custom storage in your `app.config.ts` by implementing an interface.\n\nCheck this section for more details - [Token storage](/advanced/token-storage).\n"
  },
  {
    "path": "docs/content/2.usage/4.proxy.md",
    "content": "---\ntitle: Server Proxy\ndescription: How to use your Nuxt app as a proxy for Sanctum API requests\nnavigation:\n  icon: i-lucide-waypoints\n---\n\n## Usage\n\nWhen using **SSR**, it's often convenient to serve all operations under a single domain. \nYou can achieve this by enabling the **server proxy catch-all** feature, \nwhich forwards client requests to your Laravel API while preserving cookies and headers.\n\nTo enable it, update your `nuxt.config.ts`:\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n  modules: ['nuxt-auth-sanctum'],\n  \n  ssr: true,\n  \n  sanctum: {\n    baseUrl: '/api/sanctum',\n    serverProxy: {\n      enabled: true,\n      route: '/api/sanctum',\n      baseUrl: 'http://api.frontend.dev',\n    },\n  },\n})\n```\n\nOnce `serverProxy.enabled` is set to `true`, Nuxt adds a server route at: `http://frontend.dev/api/sanctum`\nwhich is defined as `serverProxy.route` parameter.\n\nNote that `sanctum.baseUrl` is now `/api/sanctum` (a local path), while `serverProxy.baseUrl` points to your Laravel backend. \nThis setup tells Nuxt where to forward requests internally.\n\nYou can test the proxy using any helper like `useSanctumFetch`:\n\n```typescript\n// Request URL = http://frontend.dev/api/sanctum/user/profile\n// Actual URL = http://api.frontend.dev/user/profile\n\nconst { data } = await useSanctumFetch('/user/profile')\n```\n\nThe catch-all route (`/api/sanctum`) is stripped from the final proxied URL. \nIf needed, you can customise this behaviour by modifying `serverProxy.baseUrl`.\n"
  },
  {
    "path": "docs/content/3.composables/1.useSanctumAuth.md",
    "content": "---\ntitle: useSanctumAuth\ndescription: How to use useSanctumAuth composable to work with state\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nComposable provides 2 computed properties and 4 methods:\n\n- `user` - currently authenticated user (basically the same as `useSanctumUser`)\n- `isAuthenticated` - a boolean flag indicating whether the user is authenticated or not\n- `login` - method for logging in the user\n- `logout` - method for logging out the user\n- `refreshIdentity` - method for manually re-fetching current authenticated user data\n\nTo authenticate a user you should pass the credentials payload as an argument to the `login` method.\nThe payload should contain all fields required by your Laravel Sanctum backend.\n\n```typescript\nconst { login } = useSanctumAuth();\n\nconst userCredentials = {\n  email: \"user@mail.com\",\n  password: \"123123\",\n}\n\nawait login(userCredentials)\n```\n\nIf the login operation was successful, the `user` property will be updated with\nthe current user information returned by the Laravel API.\n\nIf you do not want to update the `user` property automatically (e.g. *for 2FA authentication*),\nyou can disable identity fetching by passing optional argument to `login` method:\n\n```typescript\n// user identity will not be loaded after successful response\nawait login(userCredentials, false) \n```\n\nBy default, methods will use the following Laravel endpoints:\n\n- `/login` to authenticate the user\n- `/logout` to log out the user\n- `/api/user` to get the current user information\n- `/sanctum/csrf-cookie` to get the `CSRF` token cookie\n\nTo change the default endpoints, please check the [Configuration](/usage/configuration) section.\n\n### Additional `fetch` options\n\nIf you want to pass additional header or change HTTP method for either `login` or `logout` calls, \nyou can pass optional `options: SanctumFetchOptions` argument.\n\nFor example, to log out the user with `DELETE` method instead of default `POST`:\n\n```typescript\nconst { logout } = useSanctumAuth()\n\nawait logout({ method: \"DELETE\" })\n```\n\nUse the same approach when you need to pass additional params to `login` call:\n\n```typescript\nconst { login } = useSanctumAuth()\n\nconst userCredentials = {\n  email: \"user@mail.com\",\n  password: \"123123\",\n}\n\nawait login(\n    userCredentials, \n    false, \n    { headers: { \"X-Custom-Header\": \"header_value\" } }\n)\n```\n"
  },
  {
    "path": "docs/content/3.composables/2.useSanctumUser.md",
    "content": "---\ntitle: useSanctumUser\ndescription: How to use useSanctumUser composable to work with authenticated user\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nThis composable provides access to the current authenticated user. \nIt supports generic types, so you can get the user as any class you want.\n\n```typescript\ninterface MyCustomUser {\n    id: number;\n    login: string;\n    custom_metadata: {\n        group: string;\n        role: string;\n    };\n}\n\nconst user = useSanctumUser<MyCustomUser>();\n```\n\nIf there is no authenticated user, the composable will return `null`.\n\n"
  },
  {
    "path": "docs/content/3.composables/3.useSanctumClient.md",
    "content": "---\ntitle: useSanctumClient\ndescription: How to use useSanctumClient to work with preconfigured fetch client\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nAll previous composables work on top of the `ofetch` client which can be used in your application as well. \nThe client is pre-configured with `CSRF` token header and cookie management.\n\nAll requests will be sent to the `baseUrl` specified in the [Configuration](/usage/configuration) section.\n\n```typescript\nconst client = useSanctumClient();\n\nconst { data, status, error, refresh } = await useAsyncData('users', () =>\n    client('/api/users')\n);\n```\n\nSince client implements `$Fetch` interface, you can use it as a regular `ofetch` client. \nCheck examples in the ofetch [documentation](https://github.com/unjs/ofetch?tab=readme-ov-file#%EF%B8%8F-create-fetch-with-default-options).\n\n\n"
  },
  {
    "path": "docs/content/3.composables/4.useSanctumFetch.md",
    "content": "---\ntitle: useSanctumFetch\ndescription: How to use useSanctumFetch to fetch data from Laravel API\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nBesides `useSanctumClient` you can directly send a request by using a module-specific\nversion of fetch composable - `useSanctumFetch`.\n\nThis composable implements an API-specific `useFetch`, so you can check more details [here](https://nuxt.com/docs/api/composables/use-fetch).\n\nComposable accepts 2 arguments:\n\n- `url`: target endpoint to call (type: `MaybeRefOrGetter<string>`)\n- `options`: fetch options for client (type: `UseFetchOptions<T>`)\n\n```typescript\nconst { data, status, error, refresh } = await useSanctumFetch(\"/api/users\")\n\n// or\n\nconst { data, status, error, refresh } = await useSanctumFetch(\"/api/users\", {\n  pick: [\"id\"],\n  method: \"GET\",\n  query: {\n    is_active: true,\n  },\n})\n```\n\nYou can also use type casting to work with the response as an interface:\n\n```typescript\ninterface MyResponse {\n  name: string\n}\n\nconst { data } = await useSanctumFetch<MyResponse>(\"/api/endpoint\")\n\nconst name = data.value.name // augmented by MyResponse interface\n```\n"
  },
  {
    "path": "docs/content/3.composables/5.useLazySanctumFetch.md",
    "content": "---\ntitle: useLazySanctumFetch\ndescription: How to use useLazySanctumFetch to fetch data from Laravel API\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nBesides `useSanctumClient` you can directly send a request by using a module-specific\nversion of fetch composable - `useLazySanctumFetch`.\n\nThis composable implements an API-specific `useLazyFetch`, so you can check more details [here](https://nuxt.com/docs/api/composables/use-fetch).\n\nComposable accepts 2 arguments:\n\n- `url`: target endpoint to call (type: `MaybeRefOrGetter<string>`)\n- `options`: fetch options for client (type: `UseFetchOptions<T>`)\n\n```typescript\nconst { data, status, error, refresh } = await useLazySanctumFetch(\"/api/users\")\n\n// or\n\nconst { data, status, error, refresh } = await useLazySanctumFetch(\n  \"/api/users\",\n  {\n    method: \"GET\",\n    query: { page: 1 },\n    default() {\n      return {\n        data: [],\n        meta: {\n          total: 0,\n          per_page: 0,\n        },\n      }\n    },\n  },\n)\n```\n\nYou can also use type casting to work with the response as an interface:\n\n```typescript\ninterface MyResponse {\n  name: string\n}\n\nconst { data } = await useLazySanctumFetch<MyResponse>(\"/api/endpoint\")\n\nconst name = data.value.name // augmented by MyResponse interface\n```\n"
  },
  {
    "path": "docs/content/3.composables/6.useSanctumConfig.md",
    "content": "---\ntitle: useSanctumConfig\ndescription: How to use useSanctumConfig to retrieve module configuration\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nThis composable provides quick access to the module configuration instead of\nusing `useRuntimeConfig` and several keys like `public.sanctum`.\n\nThe composable is **context-aware** - it automatically returns the appropriate configuration based on where it's executed:\n\n- **Server-side (SSR)**: Returns `runtimeConfig.sanctum` configuration\n- **Client-side (CSR)**: Returns `runtimeConfig.public.sanctum` configuration\n\nThis allows you to have different settings for server and client contexts (e.g., different `baseUrl` for Docker internal network vs public TLD).\n\n```typescript\nconst config = useSanctumConfig();\n\n// On server: runtimeConfig.sanctum.baseUrl\n// On client: runtimeConfig.public.sanctum.baseUrl\nconsole.log(config.baseUrl);\n```\n\nMore details about the configuration structure can be found [here](/usage/configuration).\n"
  },
  {
    "path": "docs/content/3.composables/7.useSanctumAppConfig.md",
    "content": "---\ntitle: useSanctumAppConfig\ndescription: How to use useSanctumAppConfig to retrieve module application configuration\nnavigation:\n  icon: i-lucide-parentheses\n---\n\n## Usage\n\nThis composable provides quick access to the module configuration instead of using `useAppConfig().sanctum`. \n\nTake a look at the following example\n\n```typescript\nconst config = useSanctumAppConfig();\n\nconsole.log(config.interceptors.onRequest); // appConfig.sanctum.interceptors.onRequest\n```\n\nMore details about the configuration structure can be found [here](/usage/configuration).\n\n"
  },
  {
    "path": "docs/content/4.middleware/1.sanctum-auth.md",
    "content": "---\ntitle: sanctum:auth\ndescription: How to protect pages from unauthenticated users\nnavigation:\n  icon: i-lucide-user-lock\n---\n\n## Usage\n\nThis middleware checks if the user is authenticated. \nIf not, it will redirect a user to the page specified in the `redirect.onAuthOnly` option (default is `/login`).\n\nAlso, you might want to remember what page the **user was trying to access** \nand redirect him back to that page after successful authentication. \nTo do that, just enable the `redirect.keepRequestedRoute` option and \nit will be automatically stored in the URL for later redirect.\n\nIf there is no redirect rule the middleware will throw `403` error.\n\n## Example\n\nThis is an example of middleware usage\n\n```vue [app/pages/dashboard.vue]\n<script lang=\"ts\" setup>\ndefinePageMeta({\n    middleware: ['sanctum:auth'],\n});\n</script>\n\n<template>\n    <!-- Page for authenticated users only -->\n</template>\n\n<style scoped></style>\n```\n"
  },
  {
    "path": "docs/content/4.middleware/2.sanctum-guest.md",
    "content": "---\ntitle: sanctum:guest\ndescription: How to protect pages from authenticated users\nnavigation:\n  icon: i-lucide-lock-open\n---\n\n## Usage\n\nThis middleware checks if the user is not authenticated. \nIf not, it will redirect a user to the page specified in the `redirect.onGuestOnly` option (default is `/`).\n\nIf there is no redirect rule the middleware will throw `403` error.\n\n## Example\n\nThis is an example of middleware usage\n\n```vue [app/pages/login.vue]\n<script lang=\"ts\" setup>\ndefinePageMeta({\n    middleware: ['sanctum:guest'],\n});\n</script>\n\n<template>\n    <!-- Page for guests only -->\n</template>\n\n<style scoped></style>\n```\n"
  },
  {
    "path": "docs/content/4.middleware/3.global.md",
    "content": "---\ntitle: Global middleware\ndescription: How to configure global middleware for the whole Nuxt application\nnavigation:\n  icon: i-lucide-shield-check\n---\n\n## Usage\n\nInstead of usage `sanctum:auth` and `sanctum:guest` on each page, \nyou can enable global middleware that checks every route and restricts unauthenticated access.\n\nThe behavior of this middleware is the same as [global middleware](https://nuxt.com/docs/guide/directory-structure/middleware) \nin Nuxt applications.\n\n::warning\nOnce global middleware is enabled, you can no longer use `sanctum:auth` and `sanctum:guest` on your pages.\n::\n\n## Configuration\n\nTo enable middleware, use the following configuration in your `nuxt.config.ts`\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: [\n        'nuxt-auth-sanctum'\n    ],\n    sanctum: {\n        baseUrl: 'http://localhost:80',\n        redirect: {\n            onAuthOnly: '/login',\n            onGuestOnly: '/profile',\n        },\n        globalMiddleware: {\n            enabled: true,\n        },\n    }\n})\n```\n\nKeep in mind, you must define `onAuthOnly` and `onGuestOnly` routes to help the plugin understand \nwhich page should be excluded from the middleware.\n\n- `onAuthOnly` - this route is used to redirect unauthenticated users to let them log in, similar to `sanctum:auth` middleware\n- `onGuestOnly` - this route is used to redirect already authenticated users, similar to `sanctum:guest` middleware\n\n::tip\nYou can also set `globalMiddleware.prepend` to true to load it before any other middleware.\n::\n\n## Exceptions\n\nIf you want to exclude an additional page besides `onAuthOnly` route, \nthen you can define page metadata like in the example below:\n\n```typescript\ndefinePageMeta({\n    sanctum: {\n        excluded: true,\n    }\n})\n```\n\nThis page will not be checked by global middleware regardless of user authentication status.\n\n## Guest mode\n\nSometimes, you may have more than one page which are available only for unauthenticated users, for instance:\n\n- \"Sign up\" page\n- \"Forgot my password\" page\n\nIn these situations, you can use `sanctum.guestOnly` property of the page meta:\n\n```typescript\ndefinePageMeta({\n    sanctum: {\n        guestOnly: true,\n    }\n})\n```\n\n::warning\nKeep in mind, that those pages still will be handled by global middleware to check the user authentication state, \nso for public pages, it is still better to use `sanctum.excluded` to speed up the loading process.\n::\n\n## Non-existing routes\n\nBy default, when a user requests a non-existing route an error page will be thrown with 404 status, \nbut you can also enable redirect to `onAuthOnly` instead by setting `allow404WithoutAuth` to `false`.\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    modules: [\n        'nuxt-auth-sanctum',\n    ],\n    sanctum: {\n        baseUrl: 'http://localhost:80',\n        redirect: {\n            onAuthOnly: '/login',\n            onGuestOnly: '/profile',\n        },\n        globalMiddleware: {\n            enabled: true,\n            allow404WithoutAuth: false,\n        },\n    }\n})\n```\n"
  },
  {
    "path": "docs/content/5.hooks/1.sanctum-request.md",
    "content": "---\ntitle: sanctum:request\ndescription: Subscribe to request hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nWhen your Nuxt application sends any request against the Laravel API, \nyou can subscribe to the `sanctum:request` hook the same way as the ofetch interceptor `onRequest`. \n\n::tip\nMore details about interceptors can be found here - [interceptors](/advanced/interceptors).\n::\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:request', (nuxtApp, context, logger) => {\n    logger.info('Sanctum request hook triggered', context.request)\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n  /**\n   * Triggers on every client request.\n   */\n  'sanctum:request': (app: NuxtApp, ctx: FetchContext, logger: ConsolaInstance) => HookResult\n}\n```\n"
  },
  {
    "path": "docs/content/5.hooks/10.sanctum-proxy-request.md",
    "content": "---\ntitle: sanctum:proxy:request\ndescription: Subscribe to request hook to inject custom event handling for proxy endpoint\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\n::warning\nThis hook works only when you use [Server Proxy](/usage/proxy) endpoint.\n::\n\nWhen your Nuxt application sends any request against the Laravel API via proxy endpoint,\nyou can subscribe to the `sanctum:proxy:request` hook the same way as the ofetch interceptor `onRequest`.\n\n::tip\nMore details about interceptors can be found here - [interceptors](/advanced/interceptors).\n::\n\n```typescript [server/plugins/sanctum-listener.ts]\nexport default defineNitroPlugin((nuxtApp) => {\n  nitroApp.hooks.hook(\"sanctum:proxy:request\", (context, logger) => {\n    logger.info(\"Sanctum proxy request hook triggered\", context.request);\n  });\n});\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface NitroRuntimeHooks {\n  /**\n   * Triggers on every client proxy request.\n   */\n  \"sanctum:proxy:request\": (ctx: FetchContext, logger: ConsolaInstance) => void;\n}\n```\n"
  },
  {
    "path": "docs/content/5.hooks/2.sanctum-response.md",
    "content": "---\ntitle: sanctum:response\ndescription: Subscribe to response hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nWhen your Laravel API returns any response, you can subscribe to the `sanctum:response` hook \nthe same way as the ofetch interceptor `onResponse`. \n\n::tip\nMore details about interceptors can be found here - [interceptors](/advanced/interceptors).\n::\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:response', (nuxtApp, context, logger) => {\n    logger.info('Sanctum response hook triggered', context.request)\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n  /**\n   * Triggers on every server response.\n   */\n  'sanctum:response': (app: NuxtApp, ctx: FetchContext, logger: ConsolaInstance) => HookResult\n}\n```\n"
  },
  {
    "path": "docs/content/5.hooks/3.sanctum-error-request.md",
    "content": "---\ntitle: sanctum:error:request\ndescription: Subscribe to request error hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nWhen you send a request to the API, it could raise an exception even before reaching the remote server. \nFor these cases, `ofetch` uses `onRequestError`. All these errors are available via `sanctum:error:request` hook.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:error:request', (context) => {\n    console.log('Sanctum request error hook triggered', context)\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n  /**\n   * Triggers when receiving an error on fetch request.\n   */\n  'sanctum:error:request': (context: FetchContext) => HookResult\n}\n```\n"
  },
  {
    "path": "docs/content/5.hooks/4.sanctum-error-response.md",
    "content": "---\ntitle: sanctum:error:response\ndescription: Subscribe to response error hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nWhen you send a request to Laravel API using any available module's composable, \nit may return an error, such as 401, 419, 403, 404, etc.\n\n::tip\nBy default, `nuxt-auth-sanctum` will try to redirect a user if 401 is returned. \nUnless you disable this by setting `sanctum.redirectIfUnauthenticated` to `false` in your `nuxt.config.ts` file.\n::\n\nHowever, if you need more granular control over API errors, you can subscribe to the `sanctum:error` hook and \nprocess the HTTP response according to your requirements.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:error:response', (response) => {\n    console.log('Sanctum error hook triggered', response)\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n  /**\n   * Triggers when receiving an error response.\n   */\n  'sanctum:error:response': (response: FetchResponse<any>) => HookResult\n}\n```\n"
  },
  {
    "path": "docs/content/5.hooks/5.sanctum-redirect.md",
    "content": "---\ntitle: sanctum:redirect\ndescription: Subscribe to redirect hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nThe module can apply redirects in different situations, like `onLogin` or `onLogout` and you can subscribe \nto this event to keep track of any redirect happening before it is done.\n\nSubscribe to the `sanctum:redirect` hook which receives the URL of the target path of a redirect.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:redirect', (url) => {\n    console.log('Sanctum redirect hook triggered', url)\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n  /**\n   * Triggers when user has been redirected.\n   */\n  'sanctum:redirect': (response: FetchResponse<any>) => HookResult\n}\n```\n\n\n"
  },
  {
    "path": "docs/content/5.hooks/6.sanctum-init.md",
    "content": "---\ntitle: sanctum:init\ndescription: Subscribe to init hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nOur module registers a plugin which requests a user identity once an application is started. \nThis is needed for middleware and redirects to properly function.\n\nSubscribe to the `sanctum:init` hook which triggers once the identity request is completed.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:init', () => {\n    console.log('Sanctum init hook triggered')\n  })\n})\n```\n\n::warning\nKeep in mind, since `nuxt-auth-sanctum` is loaded before any other module/plugin, \nyou might need to configure your own plugin and set dependencies as described in \n[Plugin dependencies](/advanced/dependencies).\n::\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n /**\n  * Triggers when an initial user identity request has been made.\n  */\n 'sanctum:init': () => HookResult\n}\n```\n\n\n"
  },
  {
    "path": "docs/content/5.hooks/7.sanctum-refresh.md",
    "content": "---\ntitle: sanctum:refresh\ndescription: Subscribe to refresh hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nWhen the authentication state changes (e.g. `onLogin`, `onLogout`), the module has to refresh the user identity.\n\nSubscribe to the `sanctum:refresh` hook which triggers once the identity refresh request is completed.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:refresh', () => {\n    console.log('Sanctum refresh hook triggered')\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n /**\n  * Triggers when user identity has been refreshed.\n  */\n 'sanctum:refresh': () => HookResult\n}\n```\n\n\n"
  },
  {
    "path": "docs/content/5.hooks/8.sanctum-login.md",
    "content": "---\ntitle: sanctum:login\ndescription: Subscribe to login hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nSubscribe to the `sanctum:login` hook which triggers once the user is logged in and the identity refresh request is completed.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:login', () => {\n    console.log('Sanctum login hook triggered')\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n /**\n  * Triggers when user successfully logs in.\n  */\n 'sanctum:login': () => HookResult\n}\n```\n\n\n"
  },
  {
    "path": "docs/content/5.hooks/9.sanctum-logout.md",
    "content": "---\ntitle: sanctum:logout\ndescription: Subscribe to logout hook to inject custom event handling\nnavigation:\n  icon: i-lucide-webhook\n---\n\n## Usage\n\nSubscribe to the `sanctum:logout` hook which triggers once the user is logged out and the identity reset is done.\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:logout', () => {\n    console.log('Sanctum logout hook triggered')\n  })\n})\n```\n\nHere is what the hook looks like\n\n```typescript\ninterface RuntimeNuxtHooks {\n /**\n  * Triggers when user successfully logs out.\n  */\n 'sanctum:logout': () => HookResult\n}\n```\n\n\n"
  },
  {
    "path": "docs/content/6.advanced/1.interceptors.md",
    "content": "---\ntitle: Interceptors\ndescription: Use custom interceptors for the fetch client used for API calls\nnavigation:\n  icon: i-lucide-split\n---\n\n## Usage\n\nInterceptors allow you to define custom functions that will be used by [sanctumClient](/composables/usesanctumclient) during API calls. \nHere are some examples of what you can do with it:\n- Add custom headers to all requests (e.g. `X-Localization`, `Accept-Language`, etc)\n- Use telemetry or logging for requests/responses\n- Modify the request payload before sending\n\n::warning\nIf you are not familiar with [ofetch](https://github.com/unjs/ofetch) interceptors, \ncheck this [documentation](https://github.com/unjs/ofetch?tab=readme-ov-file#%EF%B8%8F-interceptors) first.\n::\n\n## Configuration\n\nThis module provides special hooks to define your interceptors:\n- `sanctum:request`\n- `sanctum:response`\n\nYou can set up a new plugin and describe the behaviour of handling each outgoing request and incoming response.\n\nHere is an example of the plugin that writes a log entry for each request and response:\n\n```typescript [app/plugins/sanctum-listener.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:request', (app, ctx, logger) => {\n    logger.info('Sanctum request hook triggered', ctx.request)\n  })\n\n  nuxtApp.hook('sanctum:response', (app, ctx, logger) => {\n    logger.info('Sanctum response hook triggered', ctx.request)\n  })\n})\n```\n\nEach interceptor receives 3 arguments:\n1. `app` - an instance of the current `NuxtApp`\n2. `ctx` - `FetchContext` instance for the current operation with access to request, response, and options (*query, headers, etc*)\n3. `logger` - an instance of a Consola logger used by the module (will be prefixed with `nuxt-auth-sanctum`)\n"
  },
  {
    "path": "docs/content/6.advanced/2.error-handling.md",
    "content": "---\ntitle: Error handling\ndescription: How to catch Laravel API errors using this module\nnavigation:\n  icon: i-lucide-bug\n---\n\n## Usage\n\nError handling of API responses is not a part of this module since the main goal is to provide an authentication layer \nand configured API client, but on this page, you can find useful hints.\n\nWhen Laravel returns an error of any kind (*403, 404, 500, etc*), the module will throw this as an exception \nthat has a generic `Error` type. \n\n::tip\nBy default, when Laravel API returns a `401` status code, \n**the module will reset the user identity** and redirect to \n`sanctum.redirect.onAuthOnly` route (if `sanctum.redirectIfUnauthenticated` is enabled).\n::\n\n## Error type check\n\nThis is how you can check what type of error you received\n\n```typescript\nimport { FetchError } from 'ofetch'\n\nconst { login } = useSanctumAuth()\n\nconst userCredentials = {\n    email: 'user@mail.com',\n    password: '123123',\n}\n\nasync function onCredentialsFormSubmit() {\n    try {\n        await login(userCredentials)\n    } catch (e) {\n        if (error instanceof FetchError && error.response?.status === 422) {\n           // here you can extract errors from a response \n           // and put it in your form for example\n           console.log(e.response?._data.errors)\n        }\n    }\n}\n```\n\nSometimes, it is not convenient, especially when it comes to validation errors in plenty of forms and components.\n\n## Error helper\n\nHere you can get inspiration from error handling specifically for this case and implement it your way.\n\nCreate a new composable `useApiError` with the following content:\n\n```typescript [app/composables/useApiError.ts]\nimport { FetchError } from 'ofetch'\n\nconst VALIDATION_ERROR_CODE = 422\nconst SERVER_ERROR_CODE = 500\n\nexport const useApiError = (error: any) => {\n    const isFetchError = error instanceof FetchError\n    const isValidationError =\n        isFetchError && error.response?.status === VALIDATION_ERROR_CODE\n\n    const code = isFetchError ? error.response?.status : SERVER_ERROR_CODE\n    const bag: Record<string, string[]> = isValidationError\n        ? error.response?._data.errors\n        : {}\n\n    return {\n        isValidationError,\n        code,\n        bag,\n    }\n}\n```\n\nUse it as in the next example to extract all the errors from the response and handle it according to your logic:\n\n```typescript\ntry {\n    await login(credentials)\n} catch (e) {\n    const error = useApiError(e)\n\n    if (error.isValidationError) {\n        form.setErrors(error.bag)\n\n        return\n    }\n\n    console.error('Request failed not because of a validation', error.code)\n}\n```\n\nHave a good debugging! 😎\n"
  },
  {
    "path": "docs/content/6.advanced/3.logging.md",
    "content": "---\ntitle: Logging\ndescription: How to extract useful debug information from the plugin\nnavigation:\n  icon: i-lucide-logs\n---\n\n## Usage\n\nSometimes it might be useful to check the system logs and messages from the plugin, \nespecially if you want to check what headers and cookies are being sent. \n\nAll messages will be written in the same manner as regular `console.log` messages \nand can be checked in the browser (for CSR) or in the Node console (for SSR).\n\nBy default, the plugin uses `3` as logging level and shows error and informational logs without debugging details. \nYou can override `sanctum.logLevel` parameter in the `nuxt.config.ts` and set one of these levels:\n- 0 - Fatal and Error \n- 1 - Warnings\n- 2 - Normal logs\n- 3 - Informational logs\n- 4 - Debug logs\n- 5 - Trace logs\n\nIt follows the convention of the Consola project, \nmore details can be found here - [Log Level](https://github.com/unjs/consola?tab=readme-ov-file#log-level).\n"
  },
  {
    "path": "docs/content/6.advanced/4.token-storage.md",
    "content": "---\ntitle: Token Storage\ndescription: How to work with storage with enabled token-based authentication\nnavigation:\n  icon: i-lucide-vault\n---\n\n## Usage\n\nToken storage is used for keeping authentication token value from the Laravel API available\nfor module consumption during requests assembling.\n\nStorage is used only when `sanctum.mode` equals to `token` in your nuxt configuration:\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n  modules: [\"nuxt-auth-sanctum\"],\n\n  sanctum: {\n    // ...\n    mode: \"token\",\n    // ...\n  },\n});\n```\n\n::warning\nBy default, if there is no custom token storage defined, cookies will be used.\n::\n\n## How it works\n\nEach token storage implements the following interface:\n\n```typescript\n/**\n * Handlers to work with authentication token.\n */\nexport interface TokenStorage {\n  /**\n   * Function to load a token from the storage.\n   */\n  get: (app: NuxtApp) => Promise<string | undefined>;\n  /**\n   * Function to save a token to the storage.\n   */\n  set: (app: NuxtApp, token?: string) => Promise<void>;\n}\n```\n\nAfter the user sends credentials to the API module passes a token from the response to `set` method as well as\nthe current Nuxt application instance to allow calls like `app.runWithConext()`.\n\nOnce the user logs out, the module sends `undefined` as a token value to reset the stored value.\n\nBefore each request against the API, the module loads the token by calling get method with Nuxt instance passed.\n\n## Define token storage\n\nThere are two approaches to define custom token storage:\n\n### Using the `sanctum:storage:token` Hook (Recommended)\n\nThe recommended way to define custom token storage is using the `sanctum:storage:token` hook.\nThis approach works with all builds including `nuxt generate` (e.g. static builds for Capacitor/Ionic apps).\n\n```typescript [plugins/sanctum-storage.client.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook(\"sanctum:storage:token\", () => {\n    const storage = {\n      async get(app: NuxtApp) {\n        const { Preferences } = await import(\"@capacitor/preferences\");\n\n        const result = await Preferences.get({ key: \"sanctum.token\" });\n        return result.value ?? undefined;\n      },\n      async set(app: NuxtApp, token?: string) {\n        const { Preferences } = await import(\"@capacitor/preferences\");\n\n        if (token) {\n          await Preferences.set({ key: \"sanctum.token\", value: token });\n        } else {\n          await Preferences.remove({ key: \"sanctum.token\" });\n        }\n      },\n    };\n\n    useSanctumTokenStorage(storage);\n  });\n});\n```\n\n::tip\nFor Capacitor/Ionic apps, this is the **only** approach that works with `nuxt generate` because plugins\nare compiled by Vite into the JavaScript bundle, preserving the functions.\n::\n\n### Using app.config.ts\n\nYou can define your own handler in the `app.config.ts` configuration file:\n\n::warning\nThe `app.config.ts` approach only works in **dev mode** and **Node.js production builds**.\nWhen using `nuxt generate` (static builds for platforms like Capacitor/Ionic), functions are\nstripped during JSON serialization and the custom tokenStorage won't work.\n::\n\n```typescript [app/app.config.ts]\n// LocalStorage example for Laravel Authentication token\nconst tokenStorageKey = \"sanctum.storage.token\";\nconst localTokenStorage: TokenStorage = {\n  get: async () => {\n    if (import.meta.server) {\n      return undefined;\n    }\n\n    return window.localStorage.getItem(tokenStorageKey) ?? undefined;\n  },\n\n  set: async (app: NuxtApp, token?: string) => {\n    if (import.meta.server) {\n      return;\n    }\n\n    if (!token) {\n      window.localStorage.removeItem(tokenStorageKey);\n      return;\n    }\n\n    window.localStorage.setItem(tokenStorageKey, token);\n  },\n};\n\nexport default defineAppConfig({\n  sanctum: {\n    tokenStorage: localTokenStorage,\n  },\n});\n```\n\nNow your application will store tokens in a local storage of your browser.\n\n::warning\nKeep in mind, `localStorage` is not available for SSR mode, so you should turn it off in your `nuxt.config.ts`.\n::\n\n## When to use which approach?\n\n| Approach                          | Works with `nuxt dev` | Works with SSR | Works with `nuxt generate` |\n| --------------------------------- | --------------------- | -------------- | -------------------------- |\n| Hook + `useSanctumTokenStorage()` | Yes                   | Yes            | Yes                        |\n| `app.config.ts`                   | Yes                   | Yes            | No                         |\n"
  },
  {
    "path": "docs/content/6.advanced/5.dependencies.md",
    "content": "---\ntitle: Plugin dependencies\ndescription: Configuration of plugin dependencies to control load order\nnavigation:\n  icon: i-lucide-link\n---\n\n## Usage\n\nSometimes you might need to use other plugins while making requests against your Laravel API, \nfor instance - `i18n` headers enrichment on each sanctum fetch request like this:\n\n```typescript [app/plugins/sanctum-plugin.ts]\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:request', (app, ctx, logger) => {\n    ctx\n      .options\n      .headers\n      .set(\"X-Language\", app.$i18n.localeProperties.value.code)\n  })\n})\n```\n\nSince this module cannot know about its dependencies in your application, \nyou should use one of the following approaches to configure this behaviour:\n- use `sanctum.appendPlugin` to register the Sanctum client plugin only after the previous modules are registered already\n- disable an automatic initial user request and call it from your custom plugin with a properly set list of dependencies\n\n## Append plugin\n\nBy default, all Nuxt plugins registered by the module use `prepend` operation on a list of plugins, \nwhich makes it load before other plugins.\n\nTo change this behaviour, you can set the `sanctum.appendPlugin` config key to `true` and see \nthat the sanctum plugin will be registered after most of the plugins from other modules. \nThis is done by using `append` operation instead of `prepend` on the list of plugins.\n\nFor more details, please check the Nuxt documentation [here](https://nuxt.com/docs/api/kit/plugins#options).\n\n## Manual initial identity request\n\nEven after changing the loading order of the plugin, there might be some cases when you need more \ngranular control of the execution flow of the initial identity requests.\n\nFor these kinds of situations, you should disable a plugin initialization request \nby setting `sanctum.client.initialRequest` to `false` and use it in your custom plugin like this:\n\n```typescript [app/plugins/custom-auth.ts]\nexport default defineNuxtPlugin({\n  name: 'custom-auth',\n  dependsOn: ['@nuxtjs/i18n', 'nuxt-auth-sanctum'],\n  async setup() {\n    nuxtApp.hook('sanctum:request', (app, ctx, logger) => {\n      ctx\n        .options\n        .headers\n        .set(\"X-Language\", app.$i18n.localeProperties.value.code)\n    })\n    \n    const { init } = useSanctumAuth()\n    await init()\n  }\n})\n```\n\nThis approach can guarantee that the user's identity will be requested with all dependent plugins loaded properly.\n\n::warning\nBeware, in the case of using a custom plugin for identity initial requests, \nyou might need to handle API errors on your own (e.g. 401, 419) due to missing CSRF cookie values. \nYou can check the default implementation for reference - [identity request error handling](https://github.com/manchenkoff/nuxt-auth-sanctum/blob/main/src/runtime/plugin.ts#L62).\n::\n"
  },
  {
    "path": "docs/content/6.advanced/6.breeze-nuxt-template.md",
    "content": "---\ntitle: Breeze Nuxt Template\ndescription: A quick introduction to the application template based on Nuxt for the Laravel Sanctum API backend. \nnavigation:\n  icon: i-simple-icons-nuxt\n---\n\n## Application template\n\nSuppose you want to start a fresh project based on Nuxt and Laravel Sanctum with Laravel Echo integration. \n\nIn that case, you may consider trying out the template repository that \nhas implemented Echo integration and all authentication logic and also contains several pages such as:\n\n- Landing\n- Login\n- Sign up\n- Password reset\n- Dashboard\n\nThe repository is available here - [breeze-nuxt](https://github.com/manchenkoff/breeze-nuxt), \nfollow the guide in the `readme.md` file to set up Laravel API and connect it to the front-end application.\n\nAlso, it uses the Nuxt UI module that allows you to start building complex interfaces with ease thanks to predefined components and Tailwind CSS. For more details, check the repository.\n\nAs for the backend API part, we also have you covered. \nCheck out our [breeze-api](https://github.com/manchenkoff/breeze-api) template.\n\n"
  },
  {
    "path": "docs/content/6.advanced/7.troubleshooting.md",
    "content": "---\ntitle: Troubleshooting\ndescription: Follow this guide in case you are experiencing unexpected behaviour or facing some errors in the module work.\nnavigation:\n  icon: i-lucide-microscope\n---\n\n## Usage\n\nSince Laravel Sanctum requires a specific configuration for your application, \nyour production might work differently in comparison to a local development environment.\n\nOn this page, you can find a description of the most common issues raised on GitHub and \nhow to solve them by adjusting either your Laravel or Nuxt application configurations.\n\n## Common problems\n\nFirst of all, if you experience any unexpected behaviour, we recommend enabling `logLevel: 5` in your `nuxt.config.ts` \nto get more details in SSR (server console) or CSR (browser console) output. \nFor more details about logging, please refer to this page - [Logging](/advanced/logging).\n\nIn case of misconfiguration on either Nuxt or Laravel side, you may experience:\n- Authentication state reset on page reload\n- Difference between CSR and SSR state\n- Errors while trying to log in\n- Failing subsequent requests after successfully logging in\n\n::tip\nBefore searching for a solution to your problem, we highly recommend double-checking your configuration \nand ensuring that the cache is cleared and your runtime reflects the latest changes.\n::\n\n## Known problems\n\nBelow you can find the description of the most popular issues, which were already resolved.\n\n### Works on client-side (CSR), but not on server-side (SSR)\n\nIf you have SSR enabled, our module sends some of the requests on plugin initialisation before returning content to the client, \nwhich means we have to proxy some initial request data which might work differently on the server side. \nFor example, to fetch user identity we are passing cookies from the initial client request.\n\n::warning\nSince CSR and SSR requests are sent from different environments, you have to ensure that your API \nis accessible from both of them and Laravel allow accepting requests from CSR/SSR hosts.\n::\n\nIf you use a Docker container, make sure that the `sanctum.baseUrl` in your `nuxt.config.ts` is accessible \nfrom both your web browser and the Nuxt container. We recommend using a domain name as the name \nof the container in the virtual network to avoid side effects. \n\n::tip\nYou can check the example here - [breeze-api](https://github.com/manchenkoff/breeze-api/blob/main/docker-compose.yml#L2).\n::\n\nIn case you use additional software to set up virtual domains for development purposes (e.g. Laravel Valet, Homestead, dnsmasq, etc), \nyou may end up with incorrect DNS resolving by Node. We recommend to use `localhost` domain with different ports instead.\n\n### Request blocked by CORS policy\n\nIncorrect CORS configuration on the Laravel side can cause the following problem\n\n::caution\nAccess to fetch at 'X' from origin 'Y' has been blocked by CORS policy:\nThe 'Access-Control-Allow-Origin' header has a value 'Z' that is not equal to the supplied origin. \nHave the server send the header with a valid value, or, if an opaque response serves your needs, \nset the request's mode to 'no-cors' to fetch the resource with CORS disabled.\n::\n\nIn this case, your Nuxt application is calling API endpoint **X** from host **Y**, \nwhich is not the same as **Z** configured as `allowed_origins` in Laravel's `config/cors.php`.\n\nIf you are using Laravel Breeze, then adjusting `FRONTEND_URL` environment variable would be enough.\n\n### Unable to load user identity from API (Code 500 / 403)\n\nIf Nuxt cannot retrieve user identity on plugin initialization, that means that either your API is not reachable \nor there is an endpoint misconfiguration.\n\nFor example, the following error means that `fetch` could not find a host with `laravel.test` URL due to network problems.\n\n```\nUnable to load user identity from API [GET] \"https://laravel.test/api/user\": <no response> fetch failed\n```\n\nYou should double-check: \n- URL exists and is reachable, \n- schema is chosen correctly (*http/https*), \n- API port is set correctly (e.g. *80, 8080, 8000, 3000*)\n- a Docker container is up and running (if applicable),\n- `artisan serve` is using `localhost` instead of `127.0.0.1` (if applicable)\n\nAlso, while working locally with enabled SSL, you may face the following error:\n\n```\n[nuxt-auth-sanctum:ssr]  ERROR  Unable to load user identity from API [GET] \"https://laravel.test/api/user\": <no response> fetch failed\n[cause]: fetch failed\n[cause]: unable to verify the first certificate\n```\n\nTo enable HTTPS protocol, you might need to set an environment variable `NODE_TLS_REJECT_UNAUTHORIZED=0`.\n\n### Page hangs on load with infinite SSR requests when using only NUXT_PUBLIC_SANCTUM_BASE_URL\n\nIf your app never finishes loading and your terminal shows an infinite loop of SSR plugin setup and requests to your base URL with no response, this is likely the cause.\n\nThis happens because you defined your API URL in your `.env` file using **only** `NUXT_PUBLIC_SANCTUM_BASE_URL`. Nuxt strictly isolates public and private runtime configurations. When the server attempts to fetch your user identity during SSR, it cannot see the public variable and the request never resolves.\n\n**Fix:** Add `NUXT_SANCTUM_BASE_URL=http://your-api-url` to your `.env` file alongside the public one to ensure the server context uses the correct URL.\n\n### User is not authenticated on plugin initialization (Code 401)\n\nWith enabled logging, you can check your Nuxt logs to find errors and warnings about the reason for the 401 response. \nFor example, if you see the following message there:\n\n```\n[nuxt-auth-sanctum:ssr]  WARN  [response] set-cookie header is missing\n[nuxt-auth-sanctum:ssr] ⚙ User is not authenticated on plugin initialization, status: 401\n```\n\nthen you should check your SANCTUM_STATEFUL_DOMAINS environment variable on the Laravel side. \nIf you have a domain different than your Nuxt application is hosted on, it can cause an issue.\n\n### CSRF mismatch (Code 419)\n\nIn the logs you can see this entry - **`CSRF token mismatch, check your API configuration`**. \nThis error usually occurs if your API returns a 419 status code, \nmeaning Laravel expects a different cookie value which in most cases can be solved \nby adjusting the `SANCTUM_STATEFUL_DOMAINS` or `SESSION_DOMAIN` environment variables in your Laravel application.\n\nKeep in mind, that Laravel supports cookies only from the same TLD, meaning you cannot call your API from a different domain. \n\nFor instance:\n- frontend app - `https://myapp.com`\n- backoffice app - `https://admin.myapp.com`\n- Laravel API - `https://api.myapp.com`\n\nIn this setup, `SESSION_DOMAIN` should be `.myapp.com` and `SANCTUM_STATEFUL_DOMAINS` should be `myapp.com,admin.myapp.com`.\n\n::warning\nIf you want to use token authentication, make sure to remove your frontend application \nfrom stateful domains to avoid CSRF-check middleware.\n::\n\n### Missing headers in the API request\n\nIf you use `routeRules` and do not see Nuxt passing some of the expected headers to your Laravel API, \nit might be because of proxying behaviour, which is a bit different from the direct fetch request.\n\nMake sure that you also define supported headers in your `nuxt.config.ts` like this:\n\n```typescript [nuxt.config.ts]\nexport default defineNuxtConfig({\n    // ... other config\n\n    routeRules: {\n        '/backend/api/**': {\n            proxy: {\n                to: `http://laravel.test/api/**`,\n                headers: { YOUR_HEADER: 'header_value' },\n            }\n        }\n    }\n})\n```\n\nIf you could not find anything useful, \nplease check the [Issues](https://github.com/manchenkoff/nuxt-auth-sanctum/issues?q=is%3Aissue%20state%3Aclosed) section on GitHub \nor feel free to [create a new one](https://github.com/manchenkoff/nuxt-auth-sanctum/issues/new?template=bug_report.md)!\n"
  },
  {
    "path": "docs/content/index.md",
    "content": "---\nseo:\n  title: Nuxt - Laravel Sanctum\n  description: This module provides a simple way to use Laravel Sanctum with Nuxt. SSR-ready!\n---\n\n::u-page-hero{class=\"dark:bg-gradient-to-b from-neutral-900 to-neutral-950\"}\n---\norientation: horizontal\n---\n#top\n:hero-background\n\n#title\nAuthenticate users [easily]{.text-primary}.\n\n#description\nThe only module you need to set up Laravel Sanctum authentication for Nuxt application!\n\n#links\n  :::u-button\n  ---\n  to: /getting-started\n  size: xl\n  trailing-icon: i-lucide-arrow-right\n  ---\n  Get started\n  :::\n\n  :::u-button\n  ---\n  icon: i-simple-icons-github\n  color: neutral\n  variant: outline\n  size: xl\n  to: https://github.com/sponsors/manchenkoff?o=esb\n  target: _blank\n  ---\n  Support project\n  :::\n\n  :::u-button\n  ---\n  icon: i-simple-icons-buymeacoffee\n  color: neutral\n  variant: outline\n  size: xl\n  to: https://buymeacoffee.com/manchenkoff\n  target: _blank\n  ---\n  Buy me a coffee\n  :::\n\n\n#default\n  :::prose-pre\n  ---\n  code: npx nuxi@latest module add nuxt-auth-sanctum\n  filename: Install module\n  ---\n\n  ```bash\n  npx nuxi@latest module add nuxt-auth-sanctum\n  ```\n  :::\n::\n\n::u-page-section{class=\"dark:bg-neutral-950\"}\n#title\nFeatures\n\n#links\n  :::u-button\n  ---\n  color: neutral\n  size: lg\n  to: /getting-started\n  trailingIcon: i-lucide-arrow-right\n  variant: subtle\n  ---\n  Explore Nuxt Laravel Sanctum\n  :::\n\n#features\n  :::u-page-feature\n  ---\n  icon: i-lucide-cookie\n  ---\n  #title\n  Cookie or Token\n\n  #description\n  Automated handling `CSRF` cookies and `Bearer` tokens for authentication\n  :::\n\n  :::u-page-feature\n  ---\n  icon: i-lucide-user-lock\n  ---\n  #title\n  Focus on logic, authentication is on us\n\n  #description\n  We provide a lot of useful composables, middleware, fetch utilities, hooks and more...\n  :::\n\n  :::u-page-feature\n  ---\n  icon: i-lucide-layers\n  ---\n  #title\n  CSR + SSR\n\n  #description\n  Module supports both client and server rendering!\n  :::\n\n  :::u-page-feature\n  ---\n  icon: i-simple-icons-typescript\n  ---\n  #title\n  TypeScript\n\n  #description\n  Code of this module is written entirely in TypeScript and supports autocompletion\n  :::\n\n  :::u-page-feature\n  ---\n  icon: i-lucide-cog\n  ---\n  #title\n  No complex configuration\n\n  #description\n  You literally can start using this module by just providing Laravel API URL\n  :::\n\n  :::u-page-feature\n  ---\n  icon: i-lucide-git-pull-request\n  ---\n  #title\n  Open-source\n\n  #description\n  Source code is forever-free and open for contributions!\n  :::\n::\n\n::u-page-section{class=\"dark:bg-gradient-to-b from-neutral-950 to-neutral-900\"}\n  :::u-page-c-t-a\n  ---\n  links:\n    - label: Start authenticating\n      to: '/getting-started'\n      trailingIcon: i-lucide-arrow-right\n    - label: View on GitHub\n      to: 'https://github.com/manchenkoff/nuxt-auth-sanctum'\n      target: _blank\n      variant: subtle\n      icon: i-simple-icons-github\n  title: Ready to build?\n  description: Authenticate users with Laravel, Sanctum and Nuxt today!\n  class: dark:bg-neutral-950\n  ---\n\n  :stars-background\n  :::\n::\n\n"
  },
  {
    "path": "docs/content.config.ts",
    "content": "import { defineContentConfig, defineCollection, z } from '@nuxt/content'\n\nexport default defineContentConfig({\n  collections: {\n    landing: defineCollection({\n      type: 'page',\n      source: 'index.md'\n    }),\n    docs: defineCollection({\n      type: 'page',\n      source: {\n        include: '**',\n        exclude: ['index.md']\n      },\n      schema: z.object({\n        links: z.array(z.object({\n          label: z.string(),\n          icon: z.string(),\n          to: z.string(),\n          target: z.string().optional()\n        })).optional()\n      })\n    })\n  }\n})\n"
  },
  {
    "path": "docs/eslint.config.mjs",
    "content": "// @ts-check\nimport withNuxt from './.nuxt/eslint.config.mjs'\n\nexport default withNuxt(\n  // Your custom configs here\n)\n"
  },
  {
    "path": "docs/nuxt.config.ts",
    "content": "export default defineNuxtConfig({\n  modules: [\n    '@nuxt/eslint',\n    '@nuxt/image',\n    '@nuxt/ui',\n    '@nuxt/content',\n    'nuxt-og-image',\n    'nuxt-llms'\n  ],\n\n  devtools: {\n    enabled: true\n  },\n\n  app: {\n    head: {\n      script: [\n        {\n          src: 'https://media.bitterbrains.com/main.js?from=ARTEM&type=top',\n          defer: true,\n          async: true\n        }\n      ]\n    }\n  },\n\n  css: ['~/assets/css/main.css'],\n\n  content: {\n    build: {\n      markdown: {\n        toc: {\n          searchDepth: 1\n        }\n      }\n    }\n  },\n\n  nitro: {\n    prerender: {\n      routes: [\n        '/'\n      ],\n      crawlLinks: true,\n      autoSubfolderIndex: false\n    }\n  },\n\n  eslint: {\n    config: {\n      stylistic: {\n        commaDangle: 'never',\n        braceStyle: '1tbs'\n      }\n    }\n  },\n\n  icon: {\n    provider: 'iconify'\n  },\n\n  llms: {\n    domain: 'https://sanctum.manchenkoff.me',\n    title: 'Nuxt - Laravel Sanctum',\n    description: 'The only module you need to set up Laravel Sanctum authentication for Nuxt application!',\n    full: {\n      title: 'Nuxt - Laravel Sanctum Module Documentation',\n      description: 'This is the full documentation for Nuxt Laravel Sanctum module.'\n    },\n    sections: [\n      {\n        title: 'Getting Started',\n        contentCollection: 'docs',\n        contentFilters: [\n          { field: 'path', operator: 'LIKE', value: '/getting-started%' }\n        ]\n      },\n      {\n        title: 'Usage',\n        contentCollection: 'docs',\n        contentFilters: [\n          { field: 'path', operator: 'LIKE', value: '/usage%' }\n        ]\n      },\n      {\n        title: 'Composables',\n        contentCollection: 'docs',\n        contentFilters: [\n          { field: 'path', operator: 'LIKE', value: '/composables%' }\n        ]\n      },\n      {\n        title: 'Middleware',\n        contentCollection: 'docs',\n        contentFilters: [\n          { field: 'path', operator: 'LIKE', value: '/middleware%' }\n        ]\n      },\n      {\n        title: 'Hooks',\n        contentCollection: 'docs',\n        contentFilters: [\n          { field: 'path', operator: 'LIKE', value: '/hooks%' }\n        ]\n      },\n      {\n        title: 'Advanced',\n        contentCollection: 'docs',\n        contentFilters: [\n          { field: 'path', operator: 'LIKE', value: '/advanced%' }\n        ]\n      }\n    ]\n  }\n})\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"name\": \"nuxt-auth-sanctum-docs\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"nuxt build\",\n    \"generate\": \"nuxt generate\",\n    \"dev\": \"nuxt dev\",\n    \"preview\": \"nuxt preview\",\n    \"postinstall\": \"nuxt prepare\",\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"upgrade\": \"pnpm self-update && npx nuxt upgrade --dedupe && pnpm up && pnpm lint\"\n  },\n  \"dependencies\": {\n    \"@iconify-json/lucide\": \"^1.2.103\",\n    \"@iconify-json/simple-icons\": \"^1.2.79\",\n    \"@iconify-json/vscode-icons\": \"^1.2.46\",\n    \"@nuxt/content\": \"^3.13.0\",\n    \"@nuxt/image\": \"^1.11.0\",\n    \"@nuxt/ui\": \"^4.7.0\",\n    \"better-sqlite3\": \"^12.9.0\",\n    \"nuxt\": \"^4.4.2\",\n    \"nuxt-llms\": \"0.1.3\",\n    \"nuxt-og-image\": \"^6.4.8\"\n  },\n  \"devDependencies\": {\n    \"@nuxt/eslint\": \"^1.15.2\",\n    \"@takumi-rs/core\": \"^0.73.1\",\n    \"eslint\": \"^10.2.1\",\n    \"typescript\": \"^5.9.3\",\n    \"vue-tsc\": \"^3.2.7\"\n  },\n  \"resolutions\": {\n    \"unimport\": \"4.1.1\"\n  },\n  \"packageManager\": \"pnpm@10.33.0\"\n}\n"
  },
  {
    "path": "docs/pnpm-workspace.yaml",
    "content": "ignoredBuiltDependencies:\n  - \"@parcel/watcher\"\n  - \"@tailwindcss/oxide\"\n  - esbuild\n  - unrs-resolver\n  - vue-demi\nonlyBuiltDependencies:\n  - better-sqlite3\n  - sharp\n"
  },
  {
    "path": "docs/renovate.json",
    "content": "{\n  \"extends\": [\"github>nuxt/renovate-config-nuxt\"],\n  \"lockFileMaintenance\": {\n    \"enabled\": true\n  },\n  \"packageRules\": [\n    {\n      \"matchDepTypes\": [\"resolutions\"],\n      \"enabled\": false\n    }\n  ],\n  \"postUpdateOptions\": [\"pnpmDedupe\"]\n}\n"
  },
  {
    "path": "docs/tsconfig.json",
    "content": "{\n  \"files\": [],\n  \"references\": [\n    { \"path\": \"./.nuxt/tsconfig.app.json\" },\n    { \"path\": \"./.nuxt/tsconfig.server.json\" },\n    { \"path\": \"./.nuxt/tsconfig.shared.json\" },\n    { \"path\": \"./.nuxt/tsconfig.node.json\" }\n  ]\n}\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "// @ts-check\nimport { createConfigForNuxt } from '@nuxt/eslint-config/flat'\n\nexport default createConfigForNuxt(\n  {\n    features: {\n      tooling: true,\n      stylistic: true,\n    },\n    dirs: {\n      src: ['./playground'],\n    },\n  },\n  //\n  {\n    rules: {\n      'vue/no-multiple-template-root': 'off',\n      'vue/multi-word-component-names': 'off',\n      '@stylistic/eol-last': 'off',\n    },\n  },\n  //\n  {\n    ignores: ['docs/'],\n  },\n)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"nuxt-auth-sanctum\",\n  \"version\": \"2.3.4\",\n  \"author\": {\n    \"name\": \"Artem Manchenkov\",\n    \"email\": \"artem@manchenkoff.me\",\n    \"url\": \"https://github.com/manchenkoff\"\n  },\n  \"description\": \"Nuxt module for Laravel Sanctum authentication\",\n  \"homepage\": \"https://sanctum.manchenkoff.me\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/manchenkoff/nuxt-auth-sanctum.git\"\n  },\n  \"license\": \"MIT\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/types.d.mts\",\n      \"import\": \"./dist/module.mjs\"\n    }\n  },\n  \"main\": \"./dist/module.mjs\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"prepack\": \"nuxt-module-build build\",\n    \"rc\": \"pnpm pack --pack-destination dist\",\n    \"dev\": \"nuxi dev playground\",\n    \"dev:build\": \"nuxi build playground\",\n    \"dev:prepare\": \"nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground\",\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"test\": \"vitest run\",\n    \"test:watch\": \"vitest watch\",\n    \"test:types\": \"vue-tsc --noEmit && cd playground && vue-tsc --noEmit\",\n    \"validate\": \"npm run lint && npm run test:types && npm run test\",\n    \"release\": \"npm run validate && npm run prepack && changelogen --release && npm publish && git push --follow-tags\",\n    \"upgrade\": \"pnpm self-update && npx nuxt upgrade --dedupe && pnpm up && pnpm dev:prepare && pnpm validate\"\n  },\n  \"dependencies\": {\n    \"defu\": \"^6.1.7\",\n    \"ofetch\": \"^1.5.1\",\n    \"pathe\": \"^2.0.3\"\n  },\n  \"devDependencies\": {\n    \"@nuxt/devtools\": \"^3.2.4\",\n    \"@nuxt/eslint-config\": \"^1.15.2\",\n    \"@nuxt/kit\": \"^4.4.2\",\n    \"@nuxt/module-builder\": \"^1.0.2\",\n    \"@nuxt/schema\": \"^4.4.2\",\n    \"@nuxt/test-utils\": \"^4.0.2\",\n    \"@types/node\": \"^25.6.0\",\n    \"changelogen\": \"^0.6.2\",\n    \"consola\": \"^3.4.2\",\n    \"eslint\": \"^10.2.1\",\n    \"h3\": \"1.15.11\",\n    \"nuxt\": \"^4.4.2\",\n    \"typescript\": \"^5.9.3\",\n    \"vitest\": \"^4.1.5\",\n    \"vue\": \"^3.5.33\",\n    \"vue-tsc\": \"^3.2.7\"\n  },\n  \"packageManager\": \"pnpm@10.32.1\",\n  \"pnpm\": {\n    \"onlyBuiltDependencies\": [\n      \"@parcel/watcher\",\n      \"esbuild\",\n      \"unrs-resolver\"\n    ]\n  }\n}\n"
  },
  {
    "path": "playground/app/app.vue",
    "content": "<template>\n  <NuxtLayout>\n    <NuxtPage />\n  </NuxtLayout>\n</template>\n"
  },
  {
    "path": "playground/app/error.vue",
    "content": "<script setup lang=\"ts\">\nimport { useSanctumAuth } from '#imports'\n\ninterface ErrorProps {\n  error?: {\n    url: string\n    statusCode: number\n    statusMessage: string\n    message: string\n    description: string\n    data?: unknown\n  }\n}\n\nconst props = defineProps<ErrorProps>()\n\nconst { isAuthenticated, logout } = useSanctumAuth()\n</script>\n\n<template>\n  <NuxtLoadingIndicator />\n\n  <h1>Nuxt - Laravel Sanctum application sample</h1>\n\n  <nav class=\"menu\">\n    <NuxtLink to=\"/\">\n      Home\n    </NuxtLink>\n    <NuxtLink to=\"/login\">\n      Login\n    </NuxtLink>\n    <NuxtLink to=\"/logout\">\n      Logout\n    </NuxtLink>\n    <NuxtLink to=\"/profile\">\n      Profile\n    </NuxtLink>\n    <NuxtLink to=\"/welcome\">\n      Welcome\n    </NuxtLink>\n\n    <button\n      v-if=\"isAuthenticated\"\n      @click=\"() => logout()\"\n    >\n      Logout\n    </button>\n  </nav>\n\n  <hr>\n\n  <strong>Error</strong>\n  <p>{{ props.error }}</p>\n</template>\n\n<style scoped>\n.menu {\n    display: flex;\n    gap: 1rem;\n}\n</style>\n"
  },
  {
    "path": "playground/app/layouts/default.vue",
    "content": "<script lang=\"ts\" setup>\nimport { useSanctumAuth } from '#imports'\n\nconst { isAuthenticated, logout } = useSanctumAuth()\n</script>\n\n<template>\n  <NuxtLoadingIndicator />\n\n  <h1>Nuxt - Laravel Sanctum application sample</h1>\n\n  <nav class=\"menu\">\n    <NuxtLink to=\"/\">\n      Home\n    </NuxtLink>\n    <NuxtLink to=\"/login\">\n      Login\n    </NuxtLink>\n    <NuxtLink to=\"/logout\">\n      Logout\n    </NuxtLink>\n    <NuxtLink to=\"/profile\">\n      Profile\n    </NuxtLink>\n    <NuxtLink to=\"/welcome\">\n      Welcome\n    </NuxtLink>\n\n    <button\n      v-if=\"isAuthenticated\"\n      @click=\"() => logout()\"\n    >\n      Logout\n    </button>\n  </nav>\n\n  <hr>\n  <slot />\n</template>\n\n<style scoped>\n.menu {\n    display: flex;\n    gap: 1rem;\n}\n</style>\n"
  },
  {
    "path": "playground/app/pages/index.vue",
    "content": "<script lang=\"ts\" setup>\n//\n</script>\n\n<template>\n  <p>\n    Welcome to demo application for Nuxt & Laravel Sanctum integration! Feel\n    free to use navigation menu to check pages' behavior\n  </p>\n</template>\n"
  },
  {
    "path": "playground/app/pages/login.vue",
    "content": "<script lang=\"ts\" setup>\nimport {\n  definePageMeta,\n  reactive,\n  ref,\n  useRoute,\n  useSanctumAuth,\n} from '#imports'\n\ndefinePageMeta({\n  middleware: ['sanctum:guest'],\n})\n\nconst { login } = useSanctumAuth()\nconst route = useRoute()\n\nconst credentials = reactive({\n  email: '',\n  password: '',\n  remember: false,\n})\n\nconst loginError = ref('')\n\nasync function onFormSubmit() {\n  try {\n    await login(credentials)\n  }\n  catch (error) {\n    loginError.value = error as string\n  }\n}\n</script>\n\n<template>\n  <div v-if=\"route.query.redirect\">\n    Hmmm, looks like you tried to open\n    <em>\"{{ route.query.redirect }}\"</em> page, login first to access it and\n    we can redirect you there\n  </div>\n\n  <h2>Login form</h2>\n\n  <p\n    v-if=\"loginError\"\n    class=\"error-message\"\n  >\n    Error - {{ loginError }}\n  </p>\n\n  <form\n    class=\"login-form\"\n    @submit.prevent=\"onFormSubmit\"\n  >\n    <div class=\"input-group\">\n      <label for=\"email\">User email</label>\n      <input\n        id=\"email\"\n        v-model=\"credentials.email\"\n        autocomplete=\"username\"\n        type=\"text\"\n        name=\"email\"\n      >\n    </div>\n\n    <div class=\"input-group\">\n      <label for=\"password\">Password</label>\n      <input\n        id=\"password\"\n        v-model=\"credentials.password\"\n        type=\"password\"\n        autocomplete=\"current-password\"\n        name=\"password\"\n      >\n    </div>\n\n    <div class=\"input-group\">\n      <label for=\"remember\">Remember me</label>\n      <input\n        id=\"remember\"\n        v-model=\"credentials.remember\"\n        type=\"checkbox\"\n        name=\"remember\"\n      >\n    </div>\n\n    <button type=\"submit\">\n      Log in\n    </button>\n  </form>\n</template>\n\n<style scoped>\n.login-form {\n    display: flex;\n    flex-direction: column;\n    gap: 1rem;\n    width: fit-content;\n}\n\n.input-group {\n    display: flex;\n    gap: 0.5rem;\n}\n\n.error-message {\n    color: red;\n}\n</style>\n"
  },
  {
    "path": "playground/app/pages/logout.vue",
    "content": "<script lang=\"ts\" setup>\nimport { definePageMeta } from '#imports'\n\ndefinePageMeta({\n  middleware: ['sanctum:guest'],\n})\n</script>\n\n<template>\n  <p>Thanks for checking out this demo app! See you later :)</p>\n</template>\n"
  },
  {
    "path": "playground/app/pages/profile.vue",
    "content": "<script lang=\"ts\" setup>\nimport { definePageMeta, useSanctumAuth } from '#imports'\n\ndefinePageMeta({\n  middleware: ['sanctum:auth'],\n})\n\nconst { isAuthenticated, user, refreshIdentity } = useSanctumAuth()\n</script>\n\n<template>\n  <p>Your authentication status - {{ isAuthenticated }}</p>\n  <p>Identity object - {{ user }}</p>\n  <div>\n    <button @click=\"refreshIdentity\">\n      Refetch user\n    </button>\n  </div>\n</template>\n"
  },
  {
    "path": "playground/app/pages/welcome.vue",
    "content": "<script lang=\"ts\" setup>\nimport { definePageMeta } from '#imports'\n\ndefinePageMeta({\n  middleware: ['sanctum:auth'],\n})\n</script>\n\n<template>\n  <p>Welcome, now you can access pages you had no access before... Try it!</p>\n</template>\n"
  },
  {
    "path": "playground/app/plugins/sanctum.hooks.ts",
    "content": "export default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:error:response', (response) => {\n    console.log('Sanctum error hook triggered', response)\n  })\n\n  nuxtApp.hook('sanctum:error:request', (context) => {\n    console.log('Sanctum request error hook triggered', context)\n  })\n\n  nuxtApp.hook('sanctum:request', (_nuxtApp, context, logger) => {\n    logger.info('Sanctum request hook triggered', context.request)\n  })\n\n  nuxtApp.hook('sanctum:response', (_nuxtApp, context, logger) => {\n    logger.info('Sanctum response hook triggered', context.request)\n  })\n\n  nuxtApp.hook('sanctum:redirect', (url) => {\n    console.log('Sanctum redirect hook triggered', url)\n  })\n\n  nuxtApp.hook('sanctum:init', () => {\n    console.log('Sanctum init hook triggered')\n  })\n\n  nuxtApp.hook('sanctum:refresh', () => {\n    console.log('Sanctum refresh hook triggered')\n  })\n\n  nuxtApp.hook('sanctum:login', () => {\n    console.log('Sanctum login hook triggered')\n  })\n\n  nuxtApp.hook('sanctum:logout', () => {\n    console.log('Sanctum logout hook triggered')\n  })\n})\n"
  },
  {
    "path": "playground/app/plugins/sanctum.storage.client.ts",
    "content": "import type { NuxtApp } from '#app'\nimport type { TokenStorage } from '../../../src/runtime/types/config'\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  nuxtApp.hook('sanctum:storage:token', () => {\n    const tokenStorageKey = 'sanctum.storage.token'\n\n    const localTokenStorage: TokenStorage = {\n      get: async () => {\n        if (import.meta.server) {\n          return undefined\n        }\n\n        return window.localStorage.getItem(tokenStorageKey) ?? undefined\n      },\n\n      set: async (_app: NuxtApp, token?: string) => {\n        if (import.meta.server) {\n          return\n        }\n\n        if (!token) {\n          window.localStorage.removeItem(tokenStorageKey)\n          return\n        }\n\n        window.localStorage.setItem(tokenStorageKey, token)\n      },\n    }\n\n    useSanctumTokenStorage(localTokenStorage)\n  })\n})\n"
  },
  {
    "path": "playground/nuxt.config.ts",
    "content": "export default defineNuxtConfig({\n  modules: ['../src/module'],\n  ssr: true,\n  devtools: { enabled: true },\n  future: {\n    compatibilityVersion: 4,\n  },\n  sanctum: {\n    baseUrl: '/api/sanctum',\n    mode: 'cookie',\n    logLevel: 4,\n    redirect: {\n      keepRequestedRoute: true,\n      onAuthOnly: '/login',\n      onGuestOnly: '/profile',\n      onLogin: '/welcome',\n      onLogout: '/logout',\n    },\n    endpoints: {\n      csrf: '/sanctum/csrf-cookie',\n      login: '/login',\n      logout: '/logout',\n      user: '/api/user',\n    },\n    globalMiddleware: {\n      allow404WithoutAuth: true,\n      enabled: false,\n      prepend: false,\n    },\n    serverProxy: {\n      enabled: true,\n      route: '/api/sanctum',\n      baseUrl: 'http://localhost:80',\n    },\n  },\n})\n"
  },
  {
    "path": "playground/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"nuxt-auth-sanctum-playground\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"nuxi dev\",\n    \"build\": \"nuxi build\",\n    \"generate\": \"nuxi generate\",\n    \"upgrade\": \"pnpm self-update && npx nuxt upgrade --dedupe && pnpm up\"\n  },\n  \"devDependencies\": {\n    \"nuxt\": \"latest\",\n    \"vue\": \"^3.5.33\"\n  }\n}\n"
  },
  {
    "path": "playground/pnpm-workspace.yaml",
    "content": "onlyBuiltDependencies:\n  - esbuild\n"
  },
  {
    "path": "playground/server/plugins/sanctum.hooks.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { NitroApp } from 'nitropack'\n\nexport default defineNitroPlugin((nitroApp: NitroApp): void => {\n  nitroApp.hooks.hook('sanctum:proxy:request', (context: FetchContext, logger: ConsolaInstance) => {\n    logger.info('Sanctum proxy request hook triggered', context.request)\n  })\n})\n"
  },
  {
    "path": "playground/server/tsconfig.json",
    "content": "{\n    \"extends\": \"../.nuxt/tsconfig.server.json\"\n}\n"
  },
  {
    "path": "playground/tsconfig.json",
    "content": "{\n    \"extends\": \"./.nuxt/tsconfig.json\"\n}\n"
  },
  {
    "path": "src/config.ts",
    "content": "import type { ModuleOptions } from './runtime/types/options'\n\nexport const defaultModuleOptions: ModuleOptions = {\n  baseUrl: 'http://localhost:80',\n  mode: 'cookie',\n  userStateKey: 'sanctum.user.identity',\n  redirectIfAuthenticated: false,\n  redirectIfUnauthenticated: false,\n  endpoints: {\n    csrf: '/sanctum/csrf-cookie',\n    login: '/login',\n    logout: '/logout',\n    user: '/api/user',\n  },\n  csrf: {\n    cookie: 'XSRF-TOKEN',\n    header: 'X-XSRF-TOKEN',\n  },\n  client: {\n    retry: false,\n    initialRequest: true,\n  },\n  redirect: {\n    keepRequestedRoute: false,\n    onLogin: '/',\n    onLogout: '/',\n    onAuthOnly: '/login',\n    onGuestOnly: '/',\n  },\n  globalMiddleware: {\n    enabled: false,\n    prepend: false,\n    allow404WithoutAuth: true,\n  },\n  logLevel: 3,\n  appendPlugin: false,\n  serverProxy: {\n    enabled: false,\n    route: '/api/sanctum',\n    baseUrl: 'http://localhost:80',\n  },\n}\n"
  },
  {
    "path": "src/module.ts",
    "content": "import {\n  defineNuxtModule,\n  addPlugin,\n  createResolver,\n  addImportsDir,\n  addRouteMiddleware,\n  useLogger,\n  addServerHandler,\n} from '@nuxt/kit'\nimport { defu } from 'defu'\nimport { defaultModuleOptions } from './config'\nimport type { PublicModuleOptions, ModuleOptions } from './runtime/types/options'\nimport { registerTypeTemplates } from './templates'\n\nconst MODULE_NAME = 'nuxt-auth-sanctum'\n\nexport type ModuleRuntimeConfig = { sanctum: Partial<ModuleOptions> }\nexport type ModulePublicRuntimeConfig = { sanctum: Partial<PublicModuleOptions> }\n\nexport default defineNuxtModule<ModuleOptions>({\n  meta: {\n    name: MODULE_NAME,\n    configKey: 'sanctum',\n  },\n\n  defaults: defaultModuleOptions,\n\n  setup(_options, _nuxt) {\n    const resolver = createResolver(import.meta.url)\n\n    const sanctumPublicConfig = defu(\n      _nuxt.options.runtimeConfig.public.sanctum,\n      _options,\n    )\n\n    const { serverProxy: _, ...sanctumClientConfig } = sanctumPublicConfig\n\n    const sanctumConfig = defu(\n      _nuxt.options.runtimeConfig.sanctum,\n      _nuxt.options.runtimeConfig.public.sanctum,\n      _options,\n    )\n\n    _nuxt.options.build.transpile.push(resolver.resolve('./runtime'))\n\n    _nuxt.options.runtimeConfig.sanctum = sanctumConfig\n    _nuxt.options.runtimeConfig.public.sanctum = sanctumClientConfig\n\n    const logger = useLogger(MODULE_NAME, { level: sanctumConfig.logLevel })\n\n    addPlugin(resolver.resolve('./runtime/plugin'), { append: sanctumConfig.appendPlugin })\n    addImportsDir(resolver.resolve('./runtime/composables'))\n\n    if (sanctumConfig.globalMiddleware.enabled) {\n      addRouteMiddleware(\n        {\n          name: 'sanctum:auth:global',\n          path: resolver.resolve('./runtime/middleware/sanctum.global'),\n          global: true,\n        },\n        {\n          prepend: sanctumConfig.globalMiddleware.prepend,\n        },\n      )\n\n      logger.info('Sanctum module initialized with global middleware')\n    }\n    else {\n      addRouteMiddleware({\n        name: 'sanctum:auth',\n        path: resolver.resolve('./runtime/middleware/sanctum.auth'),\n      })\n      addRouteMiddleware({\n        name: 'sanctum:guest',\n        path: resolver.resolve('./runtime/middleware/sanctum.guest'),\n      })\n\n      logger.info('Sanctum module initialized w/o global middleware')\n    }\n\n    if (sanctumConfig.serverProxy.enabled) {\n      addServerHandler({\n        route: `${sanctumConfig.serverProxy.route}/**`,\n        handler: resolver.resolve('./runtime/server/api/proxy'),\n      })\n\n      logger.info('Sanctum module initialized with server proxy')\n    }\n\n    registerTypeTemplates(resolver)\n  },\n})\n"
  },
  {
    "path": "src/runtime/composables/useLazySanctumFetch.ts",
    "content": "import { type UseFetchOptions, useLazyFetch } from '#app'\nimport { toRaw, toValue, type MaybeRefOrGetter } from 'vue'\nimport { useSanctumClient } from '../composables/useSanctumClient'\nimport type { SanctumFetchResponse } from '../types/fetch'\n\nexport function useLazySanctumFetch<T>(\n  url: MaybeRefOrGetter<string>,\n  options?: Omit<UseFetchOptions<T>, 'lazy'>,\n): SanctumFetchResponse<T> {\n  const client = useSanctumClient() as typeof $fetch\n  const key = options?.key ?? JSON.stringify([toRaw(toValue(url)), toRaw(toValue(options))])\n\n  const params = { ...options, key, $fetch: client } as UseFetchOptions<T>\n\n  // @ts-expect-error unable to satisfy params<T>\n  return useLazyFetch<T>(url, params)\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumAppConfig.ts",
    "content": "import type { SanctumAppConfig } from '../types/config'\nimport { useAppConfig } from '#imports'\n\nexport const useSanctumAppConfig = (): SanctumAppConfig => {\n  return (useAppConfig().sanctum ?? {}) as SanctumAppConfig\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumAuth.ts",
    "content": "import { type ComputedRef, type Ref, computed } from 'vue'\nimport { trimTrailingSlash } from '../utils/formatter'\nimport { IDENTITY_LOADED_KEY } from '../utils/constants'\nimport { useSanctumClient } from './useSanctumClient'\nimport { useSanctumUser } from './useSanctumUser'\nimport { useSanctumConfig } from './useSanctumConfig'\nimport { useSanctumAppConfig } from './useSanctumAppConfig'\nimport { navigateTo, useNuxtApp, useRoute, useState } from '#app'\nimport type { SanctumFetchOptions } from '../types/fetch'\n\nexport interface SanctumAuth<T> {\n  user: Ref<T | null>\n  isAuthenticated: ComputedRef<boolean>\n  init: () => Promise<void>\n  login: (credentials: Record<string, unknown>, fetchIdentity?: boolean, options?: SanctumFetchOptions) => Promise<unknown>\n  logout: (options?: SanctumFetchOptions) => Promise<void>\n  refreshIdentity: () => Promise<void>\n}\n\nexport type TokenResponse = {\n  token?: string\n}\n\n/**\n * Provides authentication methods for Laravel Sanctum\n *\n * @template T Type of the user object\n */\nexport const useSanctumAuth = <T>(): SanctumAuth<T> => {\n  const nuxtApp = useNuxtApp()\n\n  const user = useSanctumUser<T>()\n  const client = useSanctumClient()\n  const config = useSanctumConfig()\n  const appConfig = useSanctumAppConfig()\n\n  const isAuthenticated = computed(() => {\n    return user.value !== null\n  })\n\n  const isIdentityLoaded = useState<boolean>(\n    IDENTITY_LOADED_KEY,\n    () => false,\n  )\n\n  /**\n   * Initial request of the user identity for plugin initialization.\n   * Only call this method when `sanctum.client.initialRequest` is false.\n   */\n  async function init() {\n    if (isIdentityLoaded.value) {\n      return\n    }\n\n    isIdentityLoaded.value = true\n\n    await refreshIdentity()\n    await nuxtApp.callHook('sanctum:init')\n  }\n\n  /**\n   * Fetches the user object from the API and sets it to the current state\n   */\n  async function refreshIdentity() {\n    user.value = await client<T>(config.endpoints.user!)\n    await nuxtApp.callHook('sanctum:refresh')\n  }\n\n  /**\n   * Calls the login endpoint and sets the user object to the current state\n   *\n   * @param credentials Credentials to pass to the login endpoint\n   * @param fetchIdentity Determines whether user identity should be fetched on successful response\n   * @param options Additional fetch options\n   */\n  async function login(credentials: Record<string, unknown>, fetchIdentity: boolean = true, options: SanctumFetchOptions = {}): Promise<unknown> {\n    const currentRoute = useRoute()\n    const currentPath = trimTrailingSlash(currentRoute.path)\n\n    if (isAuthenticated.value) {\n      if (!config.redirectIfAuthenticated) {\n        throw new Error('User is already authenticated')\n      }\n\n      if (\n        config.redirect.onLogin === false\n        || config.redirect.onLogin === currentPath\n      ) {\n        return\n      }\n\n      if (config.redirect.onLogin === undefined) {\n        throw new Error('`sanctum.redirect.onLogin` is not defined')\n      }\n\n      const redirectUrl = config.redirect.onLogin as string\n\n      await nuxtApp.callHook('sanctum:redirect', redirectUrl)\n      await nuxtApp.runWithContext(async () => await navigateTo(redirectUrl))\n    }\n\n    if (config.endpoints.login === undefined) {\n      throw new Error('`sanctum.endpoints.login` is not defined')\n    }\n\n    const fetchOptions = {\n      method: 'post',\n      ...options,\n      body: credentials,\n    }\n\n    const response = await client<TokenResponse>(\n      config.endpoints.login,\n      fetchOptions as SanctumFetchOptions<'json', unknown>,\n    )\n\n    if (config.mode === 'token') {\n      if (appConfig.tokenStorage === undefined) {\n        throw new Error('`sanctum.tokenStorage` is not defined in app.config.ts')\n      }\n\n      if (response.token === undefined) {\n        throw new Error('Token was not returned from the API')\n      }\n\n      await appConfig.tokenStorage.set(nuxtApp, response.token)\n    }\n\n    if (fetchIdentity) {\n      await refreshIdentity()\n    }\n\n    await nuxtApp.callHook('sanctum:login')\n\n    if (config.redirect.keepRequestedRoute) {\n      const requestedRoute = currentRoute.query.redirect as string | undefined\n\n      if (requestedRoute && requestedRoute !== currentPath) {\n        await nuxtApp.callHook('sanctum:redirect', requestedRoute)\n        await nuxtApp.runWithContext(async () => await navigateTo(requestedRoute))\n        return response\n      }\n    }\n\n    if (\n      config.redirect.onLogin === false\n      || currentRoute.path === config.redirect.onLogin\n    ) {\n      return response\n    }\n\n    if (config.redirect.onLogin === undefined) {\n      throw new Error('`sanctum.redirect.onLogin` is not defined')\n    }\n\n    const redirectUrl = config.redirect.onLogin as string\n\n    await nuxtApp.callHook('sanctum:redirect', redirectUrl)\n    await nuxtApp.runWithContext(async () => await navigateTo(redirectUrl))\n\n    return response\n  }\n\n  /**\n   * Calls the logout endpoint and clears the user object\n   * @param options Additional fetch options\n   */\n  async function logout(options: SanctumFetchOptions = {}): Promise<void> {\n    if (!isAuthenticated.value) {\n      throw new Error('User is not authenticated')\n    }\n\n    const currentRoute = useRoute()\n    const currentPath = trimTrailingSlash(currentRoute.path)\n\n    if (config.endpoints.logout === undefined) {\n      throw new Error('`sanctum.endpoints.logout` is not defined')\n    }\n\n    const fetchOptions = {\n      method: 'post',\n      ...options,\n    }\n\n    await client(config.endpoints.logout, fetchOptions)\n\n    user.value = null\n    await nuxtApp.callHook('sanctum:logout')\n\n    if (config.mode === 'token') {\n      await appConfig.tokenStorage!.set(nuxtApp, undefined)\n    }\n\n    if (\n      config.redirect.onLogout === false\n      || currentPath === config.redirect.onLogout\n    ) {\n      return\n    }\n\n    if (config.redirect.onLogout === undefined) {\n      throw new Error('`sanctum.redirect.onLogout` is not defined')\n    }\n\n    const redirectUrl = config.redirect.onLogout as string\n\n    await nuxtApp.callHook('sanctum:redirect', redirectUrl)\n    await nuxtApp.runWithContext(async () => await navigateTo(redirectUrl))\n  }\n\n  return {\n    user,\n    isAuthenticated,\n    init,\n    login,\n    logout,\n    refreshIdentity,\n  } as SanctumAuth<T>\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumClient.ts",
    "content": "import type { SanctumFetch } from '../types/fetch'\nimport { useNuxtApp } from '#app'\n\nexport const useSanctumClient = (): SanctumFetch => {\n  const { $sanctumClient } = useNuxtApp()\n  return $sanctumClient as SanctumFetch\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumConfig.ts",
    "content": "import type { PublicModuleOptions, ModuleOptions } from '../types/options'\nimport { useRuntimeConfig } from '#imports'\nimport { isServerRuntime } from '../utils/runtime'\n\nexport const useSanctumConfig = (): PublicModuleOptions | ModuleOptions => {\n  const config = useRuntimeConfig()\n\n  if (isServerRuntime()) {\n    return config.sanctum as ModuleOptions\n  }\n\n  return config.public.sanctum as PublicModuleOptions\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumFetch.ts",
    "content": "import { type UseFetchOptions, useFetch } from '#app'\nimport { toRaw, toValue, type MaybeRefOrGetter } from 'vue'\nimport { useSanctumClient } from '../composables/useSanctumClient'\nimport type { SanctumFetchResponse } from '../types/fetch'\n\nexport function useSanctumFetch<T>(\n  url: MaybeRefOrGetter<string>,\n  options?: UseFetchOptions<T>,\n): SanctumFetchResponse<T> {\n  const client = useSanctumClient() as typeof $fetch\n  const key = options?.key ?? JSON.stringify([toRaw(toValue(url)), toRaw(toValue(options))])\n\n  const params = { ...options, key, $fetch: client } as UseFetchOptions<T>\n\n  // @ts-expect-error unable to satisfy params<T>\n  return useFetch<T>(url, params)\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumTokenStorage.ts",
    "content": "import type { TokenStorage } from '../types/config'\nimport { updateAppConfig } from '#app'\n\nexport const useSanctumTokenStorage = (storage: TokenStorage): void => {\n  updateAppConfig({\n    sanctum: {\n      tokenStorage: storage,\n    },\n  })\n}\n"
  },
  {
    "path": "src/runtime/composables/useSanctumUser.ts",
    "content": "import type { Ref } from 'vue'\nimport { useSanctumConfig } from './useSanctumConfig'\nimport { useState } from '#app'\n\n/**\n * Returns current authenticated user information.\n * @returns Reference to the user state as T.\n */\nexport const useSanctumUser = <T>(): Ref<T | null> => {\n  const options = useSanctumConfig()\n  return useState<T | null>(options.userStateKey, () => null)\n}\n"
  },
  {
    "path": "src/runtime/httpFactory.ts",
    "content": "import type { FetchContext, FetchOptions } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport { useSanctumConfig } from './composables/useSanctumConfig'\nimport type { SanctumInterceptor } from './types/config'\nimport { interceptors } from './interceptors'\nimport { determineCredentialsMode } from './utils/credentials'\nimport type { SanctumFetch } from './types/fetch'\nimport type { NuxtApp } from '#app'\n\n/**\n * Returns a tuple of request and response interceptors.\n */\nfunction useClientInterceptors(): [\n  SanctumInterceptor[],\n  SanctumInterceptor[],\n  SanctumInterceptor[],\n] {\n  const [request, response, responseError] = [\n    [...interceptors.request],\n    [...interceptors.response],\n    [...interceptors.responseError],\n  ]\n\n  return [request, response, responseError]\n}\n\n/**\n * Creates a custom OFetch instance with interceptors and Laravel-specific options.\n *\n * @param nuxtApp Nuxt application instance\n * @param logger Module logger instance\n */\nexport function createHttpClient(nuxtApp: NuxtApp, logger: ConsolaInstance): SanctumFetch {\n  const options = useSanctumConfig()\n\n  const [\n    requestInterceptors,\n    responseInterceptors,\n    responseErrorInterceptors,\n  ] = useClientInterceptors()\n\n  const httpOptions: FetchOptions = {\n    baseURL: options.baseUrl,\n    credentials: determineCredentialsMode(),\n    redirect: 'manual',\n    retry: options.client.retry === true ? 1 : options.client.retry, // false or number\n\n    async onRequest(context: FetchContext): Promise<void> {\n      for (const interceptor of requestInterceptors) {\n        await nuxtApp.runWithContext(async () => {\n          await interceptor(nuxtApp, context, logger)\n        })\n      }\n\n      await nuxtApp.callHook('sanctum:request', nuxtApp, context, logger)\n    },\n\n    async onResponse(context: FetchContext): Promise<void> {\n      for (const interceptor of responseInterceptors) {\n        await nuxtApp.runWithContext(async () => {\n          await interceptor(nuxtApp, context, logger)\n        })\n      }\n\n      await nuxtApp.callHook('sanctum:response', nuxtApp, context, logger)\n    },\n\n    async onRequestError(context: FetchContext): Promise<void> {\n      await nuxtApp.callHook('sanctum:error:request', context)\n    },\n\n    async onResponseError(context): Promise<void> {\n      for (const interceptor of responseErrorInterceptors) {\n        await nuxtApp.runWithContext(async () => {\n          await interceptor(nuxtApp, context, logger)\n        })\n      }\n\n      await nuxtApp.callHook('sanctum:error:response', context)\n    },\n  }\n\n  return $fetch.create(httpOptions) as SanctumFetch\n}\n"
  },
  {
    "path": "src/runtime/interceptors/index.ts",
    "content": "import type { SanctumInterceptor } from '../types/config'\nimport { setRequestParams } from './request/params'\nimport { setStatefulParams } from './request/stateful'\nimport { setTokenHeader } from './request/token'\nimport { logRequestHeaders } from './request/logging'\nimport { proxyResponseHeaders } from './response/proxy'\nimport { validateResponseHeaders } from './response/validation'\nimport { logResponseHeaders } from './response/logging'\nimport { handleResponseError } from './response/errorHandler'\n\nconst [request, response, responseError] = [\n  [\n    setRequestParams,\n    setStatefulParams,\n    setTokenHeader,\n    logRequestHeaders,\n  ] as SanctumInterceptor[],\n  [\n    proxyResponseHeaders,\n    validateResponseHeaders,\n    logResponseHeaders,\n  ] as SanctumInterceptor[],\n  [\n    handleResponseError,\n  ] as SanctumInterceptor[],\n]\n\nexport const interceptors = {\n  request,\n  response,\n  responseError,\n}\n"
  },
  {
    "path": "src/runtime/interceptors/request/logging.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { NuxtApp } from '#app'\n\n/**\n * Logs information about the request before it is sent to the API.\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function logRequestHeaders(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  logger.trace(\n    `Request headers for \"${ctx.request.toString()}\"`,\n    ctx.options.headers instanceof Headers\n      ? Object.fromEntries(ctx.options.headers.entries())\n      : ctx.options.headers,\n  )\n}\n"
  },
  {
    "path": "src/runtime/interceptors/request/params.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { NuxtApp } from '#app'\n\nconst ACCEPT_HEADER = 'Accept'\n\n/**\n * Modify request before sending it to the Laravel API\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function setRequestParams(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  const method = ctx.options.method?.toLowerCase() ?? 'get'\n\n  if (!ctx.options.headers?.has(ACCEPT_HEADER)) {\n    ctx.options.headers.set(ACCEPT_HEADER, 'application/json')\n\n    logger.debug(`[request] added default ${ACCEPT_HEADER} header`)\n  }\n\n  // https://laravel.com/docs/10.x/routing#form-method-spoofing\n  if (method === 'put' && ctx.options.body instanceof FormData) {\n    ctx.options.method = 'POST'\n    ctx.options.body.append('_method', 'PUT')\n\n    logger.debug('[request] changed PUT to POST method for FormData compatibility')\n  }\n}\n"
  },
  {
    "path": "src/runtime/interceptors/request/stateful.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport { useSanctumConfig } from '../../composables/useSanctumConfig'\nimport type { PublicModuleOptions } from '../../types/options'\nimport { useCookie, useRequestHeaders, useRequestURL, refreshCookie, type NuxtApp } from '#app'\nimport { isServerRuntime } from '../../utils/runtime'\n\nconst SECURE_METHODS = new Set(['post', 'delete', 'put', 'patch'])\nconst COOKIE_OPTIONS: { readonly: true, watch: false } = { readonly: true, watch: false }\n\n/**\n * Pass all cookies, headers and referrer from the client to the API\n * @param headers Headers collection to extend\n * @param config Module configuration\n * @param logger Logger instance\n */\nfunction useClientHeaders(\n  headers: Headers,\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): void {\n  const clientHeaders = useRequestHeaders(['cookie', 'user-agent'])\n  const origin = config.origin ?? useRequestURL().origin\n\n  const headersToAdd = {\n    Referer: origin,\n    Origin: origin,\n    ...(clientHeaders.cookie && { Cookie: clientHeaders.cookie }),\n    ...(clientHeaders['user-agent'] && { 'User-Agent': clientHeaders['user-agent'] }),\n  }\n\n  for (const [key, value] of Object.entries(headersToAdd)) {\n    headers.set(key, value)\n  }\n\n  logger.debug(\n    '[request] added client headers to server request',\n    Object.keys(headersToAdd),\n  )\n}\n\n/**\n * Request a new CSRF cookie from the API\n * @param config Module configuration\n * @param logger Logger instance\n */\nasync function initCsrfCookie(\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): Promise<void> {\n  if (config.endpoints.csrf === undefined) {\n    throw new Error('`sanctum.endpoints.csrf` is not defined')\n  }\n\n  await $fetch(config.endpoints.csrf, {\n    baseURL: config.baseUrl,\n    credentials: 'include',\n  })\n\n  logger.debug('[request] CSRF cookie has been initialized')\n}\n\n/**\n * Add CSRF token to the headers collection to pass from the client to the API\n * @param headers Headers collection to extend\n * @param config Module configuration\n * @param logger Logger instance\n */\nasync function useCsrfHeader(\n  headers: Headers,\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): Promise<void> {\n  if (config.csrf.cookie === undefined) {\n    throw new Error('`sanctum.csrf.cookie` is not defined')\n  }\n\n  if (config.csrf.header === undefined) {\n    throw new Error('`sanctum.csrf.header` is not defined')\n  }\n\n  const csrfToken = useCookie(config.csrf.cookie, COOKIE_OPTIONS)\n\n  if (!csrfToken.value) {\n    await initCsrfCookie(config, logger)\n    refreshCookie(config.csrf.cookie)\n  }\n\n  if (!csrfToken.value) {\n    logger.warn(`${config.csrf.cookie} cookie is missing, unable to set ${config.csrf.header} header`)\n    return\n  }\n\n  headers.set(config.csrf.header, csrfToken.value)\n\n  logger.debug(`[request] added ${config.csrf.header} header`)\n}\n\n/**\n * Handle cookies and headers for the request\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function setStatefulParams(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  const config = useSanctumConfig()\n\n  if (config.mode !== 'cookie') {\n    return\n  }\n\n  const method = ctx.options.method?.toLowerCase() ?? 'get'\n\n  if (isServerRuntime()) {\n    useClientHeaders(\n      ctx.options.headers,\n      config,\n      logger,\n    )\n  }\n\n  if (SECURE_METHODS.has(method)) {\n    await useCsrfHeader(\n      ctx.options.headers,\n      config,\n      logger,\n    )\n  }\n}\n"
  },
  {
    "path": "src/runtime/interceptors/request/token.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport { useSanctumAppConfig } from '../../composables/useSanctumAppConfig'\nimport { useSanctumConfig } from '../../composables/useSanctumConfig'\nimport type { NuxtApp } from '#app'\n\nconst AUTHORIZATION_HEADER = 'Authorization'\n\n/**\n * Sets the authentication Bearer token in the request header\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function setTokenHeader(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  const config = useSanctumConfig()\n\n  if (config.mode !== 'token') {\n    return\n  }\n\n  const appConfig = useSanctumAppConfig()\n\n  if (appConfig.tokenStorage === undefined) {\n    throw new Error('`sanctum.tokenStorage` is not defined in app.config.ts')\n  }\n\n  const token = await appConfig.tokenStorage.get(app)\n\n  if (!token) {\n    logger.debug('[request] authentication token is not set in the storage')\n    return\n  }\n\n  const bearerToken = `Bearer ${token}`\n\n  ctx.options.headers.set(AUTHORIZATION_HEADER, bearerToken)\n\n  logger.debug(`[request] added ${AUTHORIZATION_HEADER} token header`)\n}\n"
  },
  {
    "path": "src/runtime/interceptors/response/errorHandler.ts",
    "content": "import type { FetchContext, FetchResponse } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport { useSanctumConfig } from '../../composables/useSanctumConfig'\nimport { useSanctumUser } from '../../composables/useSanctumUser'\nimport { navigateTo } from '#app'\nimport type { NuxtApp } from '#app'\nimport { isServerRuntime } from '../../utils/runtime'\n\n/**\n * Handles error responses from the API.\n *\n * @param nuxtApp Nuxt application current instance.\n * @param context OFetch request/response context.\n * @param logger Module logger instance.\n */\nexport async function handleResponseError(\n  nuxtApp: NuxtApp,\n  context: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  const options = useSanctumConfig()\n  const user = useSanctumUser()\n\n  const response = context.response as FetchResponse<unknown>\n\n  if (response.status === 419) {\n    logger.warn('CSRF token mismatch, check your API configuration')\n    return\n  }\n\n  if (response.status === 401) {\n    if (user.value !== null) {\n      logger.warn('User session is not set in API or expired, resetting identity')\n      user.value = null\n    }\n\n    if (\n      isServerRuntime() === false\n      && options.redirectIfUnauthenticated\n      && options.redirect.onAuthOnly\n    ) {\n      const redirectUrl = options.redirect.onAuthOnly\n\n      await nuxtApp.callHook('sanctum:redirect', redirectUrl)\n      await nuxtApp.runWithContext(async () => await navigateTo(redirectUrl))\n    }\n  }\n}\n"
  },
  {
    "path": "src/runtime/interceptors/response/logging.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { NuxtApp } from '#app'\n\n/**\n * Logs information about the API response before it is sent to the client.\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function logResponseHeaders(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  logger.trace(\n    `Response headers for \"${ctx.request.toString()}\"`,\n    ctx.response ? Object.fromEntries(ctx.response.headers.entries()) : {},\n  )\n}\n"
  },
  {
    "path": "src/runtime/interceptors/response/proxy.ts",
    "content": "import type { OutgoingHttpHeaders } from 'node:http'\nimport { getResponseHeaders, setResponseHeaders, splitCookiesString } from 'h3'\nimport type { H3Event, TypedHeaders } from 'h3'\nimport type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport { useSanctumConfig } from '../../composables/useSanctumConfig'\nimport { navigateTo, useRequestEvent } from '#app'\nimport type { NuxtApp } from '#app'\nimport { isServerRuntime } from '../../utils/runtime'\n\nconst ServerCookieName = 'set-cookie'\n\n/**\n * Append server response headers to the client response\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nfunction appendServerResponseHeaders(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): void {\n  const event = useRequestEvent(app)\n\n  if (event === undefined) {\n    logger.debug(`[response] no event to pass cookies to the client [${ctx.request}]`)\n    return\n  }\n\n  const eventHeaders = getResponseHeaders(event)\n\n  const cookiesFromEvent = extractCookiesFromEventHeaders(eventHeaders)\n  const cookiesFromResponse = extractCookiesFromResponse(ctx, logger)\n\n  const cookiesMap = createCookiesMap(cookiesFromEvent, cookiesFromResponse)\n\n  writeCookiesToEventResponse(event, eventHeaders, cookiesMap)\n\n  logger.debug(\n    `[response] pass cookies from server to client response`,\n    Array.from(cookiesMap.keys()),\n  )\n}\n\n/**\n * Extract cookies from the current H3 event headers\n * @param headers HTTP headers collection\n */\nfunction extractCookiesFromEventHeaders(headers: OutgoingHttpHeaders): string[] {\n  const cookieHeader = headers[ServerCookieName] ?? []\n\n  if (Array.isArray(cookieHeader)) {\n    return cookieHeader\n  }\n\n  return [cookieHeader]\n}\n\n/**\n * Extract cookies from the remote API response headers\n * @param ctx Remote API fetch context\n * @param logger Module logger instance\n */\nfunction extractCookiesFromResponse(ctx: FetchContext, logger: ConsolaInstance): string[] {\n  const cookieHeader = ctx.response!.headers.get(ServerCookieName)\n\n  if (cookieHeader === null) {\n    logger.debug(`[response] no cookies to pass to the client [${ctx.request}]`)\n    return []\n  }\n\n  return splitCookiesString(cookieHeader)\n}\n\n/**\n * Create a map of cookies to deduplicate them\n * @param cookieCollections Arrays of cookies to merge\n */\nfunction createCookiesMap(...cookieCollections: string[][]) {\n  const cookiesMap = new Map<string, string>()\n\n  for (const cookies of cookieCollections) {\n    for (const cookie of cookies) {\n      const cookieName = cookie.split('=')[0]\n\n      if (cookieName === undefined) {\n        continue\n      }\n\n      cookiesMap.set(cookieName, cookie)\n    }\n  }\n\n  return cookiesMap\n}\n\n/**\n * Write cookies to the event response headers, keeping the original headers\n * @param event H3 event instance\n * @param headers HTTP headers collection\n * @param cookiesMap Cookies map\n */\nfunction writeCookiesToEventResponse(event: H3Event, headers: OutgoingHttpHeaders, cookiesMap: Map<string, string>) {\n  const mergedHeaders = {\n    ...headers,\n    [ServerCookieName]: Array.from(cookiesMap.values()),\n  } as TypedHeaders\n\n  setResponseHeaders(event, mergedHeaders)\n}\n\n/**\n * Pass all cookies from the API to the client on SSR response\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function proxyResponseHeaders(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  const config = useSanctumConfig()\n\n  if (config.mode !== 'cookie') {\n    return\n  }\n\n  if (ctx.response === undefined) {\n    logger.debug('[response] no response to process')\n    return\n  }\n\n  if (isServerRuntime()) {\n    appendServerResponseHeaders(app, ctx, logger)\n  }\n\n  // follow redirects on a client\n  if (ctx.response.redirected) {\n    const redirectUrl = ctx.response!.url\n\n    await app.callHook('sanctum:redirect', redirectUrl)\n    await app.runWithContext(async () => await navigateTo(redirectUrl))\n  }\n}\n"
  },
  {
    "path": "src/runtime/interceptors/response/validation.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { PublicModuleOptions } from '../../types/options'\nimport { useRequestURL } from '#app'\nimport type { NuxtApp } from '#app'\nimport { isServerRuntime } from '../../utils/runtime'\nimport { useSanctumConfig } from '../../composables/useSanctumConfig'\n\ntype HeaderValidator = (headers: Headers, config: PublicModuleOptions, logger: ConsolaInstance) => void\n\n/**\n * Checks if the `set-cookie` header is present in the response headers.\n * @param headers The response headers\n * @param config Module options\n * @param logger Logger instance\n */\nconst validateCookieHeader: HeaderValidator = (\n  headers: Headers,\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): void => {\n  if (config.mode == 'token') {\n    return\n  }\n\n  if (!headers.has('set-cookie')) {\n    logger.warn('[response] `set-cookie` header is missing, CSRF token will not be set')\n  }\n}\n\n/**\n * Checks if the `content-type` header is present and valid in the response headers.\n * @param headers The response headers\n * @param config Module options\n * @param logger Logger instance\n */\nconst validateContentTypeHeader: HeaderValidator = (\n  headers: Headers,\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): void => {\n  const contentType = headers.get('content-type')\n\n  if (!contentType) {\n    logger.warn('[response] \"content-type\" header is missing')\n    return\n  }\n\n  if (!contentType.includes('application/json')) {\n    logger.debug(`[response] 'content-type' is present in response but different (expected: application/json, got: ${contentType})`)\n  }\n}\n\n/**\n * Checks if the `access-control-allow-credentials` header is present in the response headers.\n * @param headers The response headers\n * @param config Module options\n * @param logger Logger instance\n */\nconst validateCredentialsHeader: HeaderValidator = (\n  headers: Headers,\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): void => {\n  if (config.mode == 'token') {\n    return\n  }\n\n  const allowCredentials = headers.get('access-control-allow-credentials')\n\n  if (!allowCredentials || allowCredentials !== 'true') {\n    logger.warn(`[response] 'access-control-allow-credentials' header is missing or invalid (expected: true, got: ${allowCredentials})`)\n  }\n}\n\n/**\n * Checks if the `access-control-allow-origin` header is the same as the current origin.\n * @param headers The response headers\n * @param config Module options\n * @param logger Logger instance\n */\nconst validateOriginHeader: HeaderValidator = (\n  headers: Headers,\n  config: PublicModuleOptions,\n  logger: ConsolaInstance,\n): void => {\n  const allowOrigin = headers.get('access-control-allow-origin')\n  const currentOrigin = config?.origin ?? useRequestURL().origin\n\n  if (!allowOrigin || !allowOrigin.includes(currentOrigin)) {\n    logger.warn(`[response] 'access-control-allow-origin' header is missing or invalid (expected: ${currentOrigin}, got: ${allowOrigin})`)\n  }\n}\n\nconst validators: HeaderValidator[] = [\n  validateCookieHeader,\n  validateContentTypeHeader,\n  validateCredentialsHeader,\n  validateOriginHeader,\n]\n\n/**\n * Validate response headers and log warnings if any are missing or invalid.\n * @param app Nuxt application instance\n * @param ctx Fetch context\n * @param logger Module logger instance\n */\nexport async function validateResponseHeaders(\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n): Promise<void> {\n  if (isServerRuntime() === false) {\n    logger.debug('[response] skipping headers validation on CSR')\n    return\n  }\n\n  const config = useSanctumConfig()\n  const headers = ctx.response?.headers\n\n  if (!headers) {\n    logger.warn('[response] no headers returned from API')\n    return\n  }\n\n  for (const validator of validators) {\n    validator(headers, config, logger)\n  }\n}\n"
  },
  {
    "path": "src/runtime/middleware/sanctum.auth.ts",
    "content": "import type { RouteLocationAsPathGeneric } from 'vue-router'\nimport { useSanctumConfig } from '../composables/useSanctumConfig'\nimport { trimTrailingSlash } from '../utils/formatter'\nimport { defineNuxtRouteMiddleware, navigateTo, createError } from '#app'\nimport { isUserSessionActive } from '../utils/session'\n\nexport default defineNuxtRouteMiddleware(async (to) => {\n  const options = useSanctumConfig()\n\n  if (await isUserSessionActive()) {\n    return\n  }\n\n  const endpoint = options.redirect.onAuthOnly\n\n  if (endpoint === undefined) {\n    throw new Error('`sanctum.redirect.onAuthOnly` is not defined')\n  }\n\n  if (endpoint === false) {\n    throw createError({ statusCode: 403 })\n  }\n\n  const redirect: RouteLocationAsPathGeneric = { path: endpoint }\n\n  if (options.redirect.keepRequestedRoute) {\n    redirect.query = { redirect: trimTrailingSlash(to.fullPath) }\n  }\n\n  return navigateTo(redirect, { replace: true })\n})\n"
  },
  {
    "path": "src/runtime/middleware/sanctum.global.ts",
    "content": "import type { RouteLocationAsPathGeneric } from 'vue-router'\nimport { useSanctumConfig } from '../composables/useSanctumConfig'\nimport { trimTrailingSlash } from '../utils/formatter'\nimport { defineNuxtRouteMiddleware, navigateTo } from '#app'\nimport { isUserSessionActive } from '../utils/session'\n\nexport default defineNuxtRouteMiddleware(async (to) => {\n  const options = useSanctumConfig()\n\n  const [homePage, loginPage] = [\n    options.redirect.onGuestOnly,\n    options.redirect.onAuthOnly,\n  ]\n\n  if (homePage === undefined || homePage === false) {\n    throw new Error(\n      'You must define onGuestOnly route when using global middleware.',\n    )\n  }\n\n  if (loginPage === undefined || loginPage === false) {\n    throw new Error(\n      'You must define onAuthOnly route when using global middleware.',\n    )\n  }\n\n  if (\n    options.globalMiddleware.allow404WithoutAuth\n    && to.matched.length === 0\n  ) {\n    return\n  }\n\n  if (to.meta.sanctum?.excluded === true) {\n    return\n  }\n\n  const isPageForGuestsOnly\n    = trimTrailingSlash(to.path) === loginPage\n      || to.meta.sanctum?.guestOnly === true\n\n  if (await isUserSessionActive()) {\n    if (isPageForGuestsOnly) {\n      return navigateTo(homePage, { replace: true })\n    }\n\n    return\n  }\n\n  if (isPageForGuestsOnly) {\n    return\n  }\n\n  const redirect: RouteLocationAsPathGeneric = { path: loginPage }\n\n  if (options.redirect.keepRequestedRoute) {\n    redirect.query = { redirect: trimTrailingSlash(to.fullPath) }\n  }\n\n  return navigateTo(redirect, { replace: true })\n})\n"
  },
  {
    "path": "src/runtime/middleware/sanctum.guest.ts",
    "content": "import { useSanctumAuth } from '../composables/useSanctumAuth'\nimport { useSanctumConfig } from '../composables/useSanctumConfig'\nimport { defineNuxtRouteMiddleware, navigateTo, createError } from '#app'\n\nexport default defineNuxtRouteMiddleware(() => {\n  const options = useSanctumConfig()\n  const { isAuthenticated } = useSanctumAuth()\n\n  if (!isAuthenticated.value) {\n    return\n  }\n\n  const endpoint = options.redirect.onGuestOnly\n\n  if (endpoint === undefined) {\n    throw new Error('`sanctum.redirect.onGuestOnly` is not defined')\n  }\n\n  if (endpoint === false) {\n    throw createError({ statusCode: 403 })\n  }\n\n  return navigateTo(endpoint, { replace: true })\n})\n"
  },
  {
    "path": "src/runtime/plugin.ts",
    "content": "import type { $Fetch } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { NuxtApp } from '#app'\nimport type { PublicModuleOptions } from './types/options'\nimport type { TokenStorage } from './types/config'\nimport { IDENTITY_LOADED_KEY } from './utils/constants'\nimport { createHttpClient } from './httpFactory'\nimport { defineNuxtPlugin, useState } from '#app'\nimport { useSanctumAppConfig } from './composables/useSanctumAppConfig'\nimport { useSanctumConfig } from './composables/useSanctumConfig'\nimport { useSanctumLogger } from './utils/logging'\nimport { useSanctumTokenStorage } from './composables/useSanctumTokenStorage'\nimport { useSanctumUser } from './composables/useSanctumUser'\nimport { isServerRuntime } from './utils/runtime'\n\nasync function resolveTokenStorage(nuxtApp: NuxtApp, logger: ConsolaInstance): Promise<TokenStorage> {\n  let appConfig = useSanctumAppConfig()\n\n  if (appConfig.tokenStorage) {\n    return appConfig.tokenStorage\n  }\n\n  await nuxtApp.callHook('sanctum:storage:token')\n\n  appConfig = await nuxtApp.runWithContext(() => useSanctumAppConfig())\n\n  if (appConfig.tokenStorage) {\n    return appConfig.tokenStorage\n  }\n\n  logger.debug('Token storage is not defined, switch to default cookie storage')\n\n  const defaultStorage = await import('./storages/cookieTokenStorage')\n  return defaultStorage.cookieTokenStorage\n}\n\nasync function initialIdentityLoad(nuxtApp: NuxtApp, client: $Fetch, options: PublicModuleOptions, logger: ConsolaInstance) {\n  const identityFetchedOnInit = useState<boolean>(\n    IDENTITY_LOADED_KEY,\n    () => false,\n  )\n\n  if (identityFetchedOnInit.value) {\n    return\n  }\n\n  const user = useSanctumUser()\n\n  if (user.value !== null) {\n    return\n  }\n\n  identityFetchedOnInit.value = true\n  logger.debug('Fetching user identity on plugin initialization')\n\n  if (!options.endpoints.user) {\n    throw new Error('`sanctum.endpoints.user` is not defined')\n  }\n\n  try {\n    const response = await client.raw(\n      options.endpoints.user,\n      { ignoreResponseError: true },\n    )\n\n    if (response.ok) {\n      user.value = response._data\n      return await nuxtApp.callHook('sanctum:init')\n    }\n\n    if ([401, 419].includes(response.status)) {\n      logger.debug(\n        'User is not authenticated on plugin initialization, status:',\n        response.status,\n      )\n    }\n    else {\n      logger.error(\n        'Unable to load user identity from API',\n        { status: response.status },\n      )\n    }\n  }\n  catch (err) {\n    logger.error(\n      'An unexpected error occurred while fetching user identity',\n      { reason: err },\n    )\n  }\n}\n\nexport default defineNuxtPlugin({\n  name: 'nuxt-auth-sanctum',\n  async setup(_nuxtApp) {\n    const nuxtApp = _nuxtApp as NuxtApp\n    const options = useSanctumConfig()\n    const logger = useSanctumLogger(options.logLevel, isServerRuntime())\n    const client = createHttpClient(nuxtApp, logger)\n\n    if (options.mode === 'token') {\n      nuxtApp.hook(\n        'page:loading:start',\n        async () => {\n          const tokenStorage = await resolveTokenStorage(nuxtApp, logger)\n          await nuxtApp.runWithContext(() => useSanctumTokenStorage(tokenStorage))\n        },\n      )\n    }\n\n    if (options.client.initialRequest) {\n      nuxtApp.hook(\n        'page:loading:start',\n        async () => {\n          await initialIdentityLoad(nuxtApp, client, options, logger)\n        },\n      )\n    }\n\n    return {\n      provide: {\n        sanctumClient: client,\n      },\n    }\n  },\n})\n"
  },
  {
    "path": "src/runtime/server/api/proxy.ts",
    "content": "import type { EventHandlerRequest, H3Event, HTTPMethod, RequestHeaders } from 'h3'\nimport {\n  appendResponseHeader,\n  defineEventHandler,\n  getQuery,\n  getRequestHeader,\n  getRequestHeaders,\n  getRequestWebStream,\n  readBody,\n  setResponseStatus,\n} from 'h3'\nimport { $fetch, type FetchContext, type FetchResponse } from 'ofetch'\nimport { useSanctumLogger } from '../../utils/logging'\nimport { determineCredentialsMode } from '../../utils/credentials'\nimport { trimTrailingSlash } from '../../utils/formatter'\nimport type { ModuleOptions } from '../../types/options'\nimport { useRuntimeConfig } from '#imports'\nimport type { ConsolaInstance } from 'consola'\nimport { useNitroApp } from 'nitropack/runtime'\nimport { isServerRuntime } from '../../utils/runtime'\n\nconst METHODS_WITH_BODY: HTTPMethod[] = ['POST', 'PUT', 'PATCH', 'DELETE']\n\nconst REQUEST_HEADERS_TO_IGNORE = [\n  'connection',\n  'upgrade',\n  'keep-alive',\n  'proxy-authenticate',\n  'proxy-authorization',\n  'te',\n  'trailer',\n  'transfer-encoding',\n  'host',\n  'content-length',\n  'accept-encoding',\n]\n\nconst RESPONSE_HEADERS_TO_IGNORE = [\n  ...REQUEST_HEADERS_TO_IGNORE,\n  'content-encoding',\n]\n\nexport default defineEventHandler(async (event: H3Event<EventHandlerRequest>) => {\n  const config = useRuntimeConfig().sanctum as ModuleOptions\n  const logger = useSanctumLogger(config.logLevel, isServerRuntime())\n\n  const endpoint = assembleEndpoint(event, config.serverProxy.baseUrl)\n\n  logger.debug(`[sanctum] proxying request to ${endpoint}`)\n\n  const response = await proxyRequest(event, endpoint, logger)\n\n  prepareResponse(event, response)\n\n  logger.debug(`[sanctum] proxying response from ${endpoint}`)\n\n  return response._data\n})\n\nfunction assembleEndpoint(event: H3Event<EventHandlerRequest>, baseUrl: string): string {\n  const\n    base = trimTrailingSlash(baseUrl),\n    path = trimTrailingSlash(event.context.params?._ || '')\n\n  if (!base) {\n    throw new Error('[sanctum] serverProxy.baseUrl is not configured')\n  }\n\n  if (path.length === 0) {\n    return base\n  }\n\n  return `${base}/${path}`\n}\n\nfunction dropProxyHeaders(headers: RequestHeaders): RequestHeaders {\n  const filteredHeaders = Object\n    .entries(headers)\n    .filter(([name]) => !REQUEST_HEADERS_TO_IGNORE.includes(name.toLowerCase()))\n\n  return Object.fromEntries(filteredHeaders)\n}\n\nasync function proxyRequest(event: H3Event<EventHandlerRequest>, endpoint: string, logger: ConsolaInstance): Promise<FetchResponse<unknown>> {\n  const nitroApp = useNitroApp()\n\n  const\n    method = event.method,\n    query = getQuery(event),\n    body = await getBody(event),\n    headers = {\n      accept: 'application/json',\n      ...dropProxyHeaders(getRequestHeaders(event)),\n    }\n\n  return await $fetch.raw(endpoint, {\n    method: method,\n    query: query,\n    body: body,\n    credentials: determineCredentialsMode(),\n    headers: headers,\n    ignoreResponseError: true,\n    async onRequest(context: FetchContext): Promise<void> {\n      await nitroApp.hooks.callHook('sanctum:proxy:request', context, logger)\n    },\n  })\n}\n\nasync function getBody(event: H3Event<EventHandlerRequest>) {\n  if (!METHODS_WITH_BODY.includes(event.method)) {\n    return Promise.resolve(undefined)\n  }\n\n  const contentLength = getRequestHeader(event, 'content-length')\n\n  if (!contentLength || contentLength === '0') {\n    return Promise.resolve(undefined)\n  }\n\n  const contentType = getRequestHeader(event, 'content-type')\n\n  if (contentType?.includes('multipart/form-data')) {\n    return getRequestWebStream(event)\n  }\n\n  return readBody(event)\n}\n\nfunction prepareResponse(event: H3Event<EventHandlerRequest>, response: FetchResponse<unknown>): void {\n  response.headers.forEach((value, key) => {\n    if (RESPONSE_HEADERS_TO_IGNORE.includes(key.toLowerCase())) {\n      return\n    }\n\n    appendResponseHeader(event, key, value)\n  })\n\n  setResponseStatus(event, response.status)\n}\n"
  },
  {
    "path": "src/runtime/server/augments.server.d.ts",
    "content": "declare module 'nitropack/types' {\n  interface NitroRuntimeHooks {\n    /**\n     * Triggers on every proxy client request.\n     */\n    'sanctum:proxy:request': (ctx: FetchContext, logger: ConsolaInstance) => void\n  }\n}\n"
  },
  {
    "path": "src/runtime/storages/cookieTokenStorage.ts",
    "content": "import { unref } from 'vue'\nimport type { TokenStorage } from '../types/config'\nimport { useCookie, useRequestURL } from '#app'\nimport type { NuxtApp } from '#app'\n\nconst cookieTokenKey = 'sanctum.token.cookie'\n\n/**\n * Token storage using a secure cookie for HTTPS and plain cookie for HTTP.\n * Works with both CSR/SSR modes.\n */\nexport const cookieTokenStorage: TokenStorage = {\n  async get(app: NuxtApp) {\n    return app.runWithContext(() => {\n      const cookie = useCookie(cookieTokenKey, { readonly: true, watch: false })\n      return unref(cookie.value) ?? undefined\n    })\n  },\n\n  async set(app: NuxtApp, token?: string) {\n    await app.runWithContext(() => {\n      const isSecure = useRequestURL().protocol.startsWith('https')\n      const cookie = useCookie(cookieTokenKey, { secure: isSecure })\n      cookie.value = token\n    })\n  },\n}\n"
  },
  {
    "path": "src/runtime/types/config.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { NuxtApp } from '#app'\n\n/**\n * Handlers to work with authentication token.\n */\nexport interface TokenStorage {\n  /**\n   * Function to load a token from the storage.\n   */\n  get: (app: NuxtApp) => Promise<string | undefined>\n  /**\n   * Function to save a token to the storage.\n   */\n  set: (app: NuxtApp, token?: string) => Promise<void>\n}\n\n/**\n * Interceptor definition type.\n */\nexport type SanctumInterceptor = (\n  app: NuxtApp,\n  ctx: FetchContext,\n  logger: ConsolaInstance,\n) => Promise<void>\n\n/**\n * Sanctum configuration for the application side with user-defined handlers.\n */\nexport interface SanctumAppConfig {\n  /**\n   * Token storage handlers to be used by the client.\n   */\n  tokenStorage?: TokenStorage\n}\n"
  },
  {
    "path": "src/runtime/types/fetch.ts",
    "content": "import type { MappedResponseType, FetchRequest, Fetch, FetchOptions, FetchResponse, ResponseType, $Fetch, FetchError } from 'ofetch'\nimport type { AsyncData, KeysOf, PickFrom } from '#app/composables/asyncData'\n\nexport type SanctumFetchOptions<R extends ResponseType = ResponseType, T = unknown> = Omit<\n  FetchOptions<R, T>,\n  'baseUrl'\n  | 'credentials'\n  | 'redirect'\n  | 'retry'\n  | 'onRequest'\n  | 'onRequestError'\n  | 'onResponse'\n  | 'onResponseError'\n>\n\nexport interface SanctumFetch extends $Fetch {\n  <T = unknown, R extends ResponseType = 'json'>(request: FetchRequest, options?: SanctumFetchOptions<R>): Promise<MappedResponseType<R, T>>\n  raw<T = unknown, R extends ResponseType = 'json'>(request: FetchRequest, options?: SanctumFetchOptions<R>): Promise<FetchResponse<MappedResponseType<R, T>>>\n  native: Fetch\n}\n\nexport type SanctumFetchResponse<T> = AsyncData<PickFrom<T, KeysOf<T>> | undefined, FetchError | undefined>\n"
  },
  {
    "path": "src/runtime/types/meta.ts",
    "content": "/**\n * Page meta information to be used by the global middleware.\n */\nexport interface SanctumGlobalMiddlewarePageMeta {\n  /**\n   * Determines whether the page should be excluded from middleware checks.\n   */\n  excluded?: boolean\n  /**\n   * Determines whether the page should be accessible only by unauthenticated users.\n   */\n  guestOnly?: boolean\n}\n"
  },
  {
    "path": "src/runtime/types/options.ts",
    "content": "/**\n * Definition of Laravel Sanctum endpoints to be used by the client.\n */\nexport interface SanctumEndpoints {\n  /**\n   * The endpoint to request a new CSRF token.\n   * @default '/sanctum/csrf-cookie'\n   */\n  csrf: string\n  /**\n   * The endpoint to send user credentials to authenticate.\n   * @default '/login'\n   */\n  login: string\n  /**\n   * The endpoint to destroy current user session.\n   * @default '/logout'\n   */\n  logout: string\n  /**\n   * The endpoint to fetch current user data.\n   * @default '/api/user'\n   */\n  user: string\n}\n\n/**\n * CSRF token-specific options.\n */\nexport interface CsrfOptions {\n  /**\n   * Name of the CSRF cookie to extract from server response.\n   * @default 'XSRF-TOKEN'\n   */\n  cookie: string\n  /**\n   * Name of the CSRF header to pass from client to server.\n   * @default 'X-XSRF-TOKEN'\n   */\n  header: string\n}\n\n/**\n * OFetch client-specific options.\n */\nexport interface ClientOptions {\n  /**\n   * The number of times to retry a request when it fails.\n   * @default false\n   */\n  retry: number | boolean\n  /**\n   * Determines whether to request the user identity on plugin initialization.\n   * @default true\n   */\n  initialRequest: boolean\n}\n\n/**\n * Behavior of the plugin redirects when the user is authenticated or not.\n */\nexport interface RedirectOptions {\n  /**\n   * Determines whether to keep the requested route when redirecting after login.\n   * @default false\n   */\n  keepRequestedRoute: boolean\n  /**\n   * Route to redirect to when the user is authenticated.\n   * If set to false, do nothing.\n   * @default '/'\n   */\n  onLogin: string | false\n  /**\n   * Route to redirect to when the user is not authenticated.\n   * If set to false, do nothing.\n   * @default '/'\n   */\n  onLogout: string | false\n  /**\n   * Route to redirect to when the user has to be authenticated.\n   * If set to false, the plugin will throw a 403 error.\n   * @default '/login'\n   */\n  onAuthOnly: string | false\n  /**\n   * Route to redirect to when a user has to be a guest.\n   * If set to false, the plugin will throw a 403 error.\n   * @default '/'\n   */\n  onGuestOnly: string | false\n}\n\n/**\n * Configuration of the global application-wide middleware.\n */\nexport interface GlobalMiddlewareOptions {\n  /**\n   * Determines whether the global middleware is enabled.\n   * @default false\n   */\n  enabled: boolean\n  /**\n   * Determines whether the global middleware is prepended.\n   * @default false\n   */\n  prepend: boolean\n  /**\n   * Determines whether to allow 404 pages without authentication.\n   * @default true\n   */\n  allow404WithoutAuth: boolean\n}\n\n/**\n * Server API proxy configuration to handle Laravel requests on SSR-side first.\n */\nexport interface ServerProxy {\n  /**\n   * Determines whether the server side proxy is enabled.\n   * @default false\n   */\n  enabled: boolean\n  /**\n   * Nuxt server route to catch all requests. This route will receive any nested path as well,\n   * for instance: `/api/sanctum` will also catch `/api/sanctum/login` and `/api/sanctum/user/info`.\n   * @default '/api/sanctum'\n   */\n  route: string\n  /**\n   * The base URL of the Laravel API.\n   * @default 'http://localhost:80'\n   */\n  baseUrl: string\n}\n\n/**\n * Options to be passed to the plugin.\n */\nexport interface PublicModuleOptions {\n  /**\n   * The base URL of the Laravel API.\n   * @default 'http://localhost:80'\n   */\n  baseUrl: string\n  /**\n   * The mode to use for authentication.\n   * @default 'cookie'\n   */\n  mode: 'cookie' | 'token'\n  /**\n   * The URL of the current application to use in the Referrer header. (Optional)\n   * @default useRequestURL().origin\n   */\n  origin?: string\n  /**\n   * The key to use to store the user identity in the `useState` variable.\n   * @default 'sanctum.user.identity'\n   */\n  userStateKey: string\n  /**\n   * Determine whether to redirect the user if it is already authenticated on a login attempt.\n   * @default false\n   */\n  redirectIfAuthenticated: boolean\n  /**\n   * Determine whether to redirect when the user got unauthenticated on any API request.\n   * @default false\n   */\n  redirectIfUnauthenticated: boolean\n  /**\n   * Laravel Sanctum endpoints to be used by the client.\n   */\n  endpoints: Partial<SanctumEndpoints>\n  /**\n   * CSRF token-specific options.\n   */\n  csrf: Partial<CsrfOptions>\n  /**\n   * OFetch client-specific options.\n   */\n  client: Partial<ClientOptions>\n  /**\n   * Behavior of the plugin redirects when the user is authenticated or not.\n   */\n  redirect: Partial<RedirectOptions>\n  /**\n   * Behavior of the global middleware.\n   */\n  globalMiddleware: Partial<GlobalMiddlewareOptions>\n  /**\n   * The log level to use for the logger.\n   *\n   * 0: Fatal and Error\n   * 1: Warnings\n   * 2: Normal logs\n   * 3: Informational logs\n   * 4: Debug logs\n   * 5: Trace logs\n   *\n   * More details at https://github.com/unjs/consola?tab=readme-ov-file#log-level\n   * @default 3\n   */\n  logLevel: number\n  /**\n   * Determines whether to append the plugin to the Nuxt application.\n   * By default, Nuxt prepends the plugin to load it before the application modules.\n   * @default false\n   * @see https://nuxt.com/docs/api/kit/plugins#options\n   */\n  appendPlugin: boolean\n}\n\nexport interface ModuleOptions extends PublicModuleOptions {\n  /**\n   * Server API proxy configuration to handle Laravel requests on SSR-side first.\n   */\n  serverProxy: ServerProxy\n}\n"
  },
  {
    "path": "src/runtime/utils/constants.ts",
    "content": "export const IDENTITY_LOADED_KEY = 'sanctum.user.loaded'\n"
  },
  {
    "path": "src/runtime/utils/credentials.ts",
    "content": "/**\n * Determines the credentials mode for the fetch request.\n */\nexport function determineCredentialsMode() {\n  // Fix for Cloudflare workers - https://github.com/cloudflare/workers-sdk/issues/2514\n  const isCredentialsSupported = 'credentials' in Request.prototype\n\n  if (!isCredentialsSupported) {\n    return undefined\n  }\n\n  return 'include'\n}\n"
  },
  {
    "path": "src/runtime/utils/formatter.ts",
    "content": "/**\n * Removes the trailing slash from a path if it exists.\n * @param path The URL path.\n */\nexport function trimTrailingSlash(path: string): string {\n  if (path.length > 1 && path.slice(-1) === '/') {\n    return path.slice(0, -1)\n  }\n  return path\n}\n"
  },
  {
    "path": "src/runtime/utils/logging.ts",
    "content": "import { createConsola } from 'consola'\nimport type { ConsolaInstance } from 'consola'\n\nconst LOGGER_NAME = 'nuxt-auth-sanctum'\n\nexport function useSanctumLogger(logLevel: number, server: boolean): ConsolaInstance {\n  const envSuffix = server ? 'ssr' : 'csr'\n  const loggerName = LOGGER_NAME + ':' + envSuffix\n\n  return createConsola({ level: logLevel }).withTag(loggerName)\n}\n"
  },
  {
    "path": "src/runtime/utils/runtime.ts",
    "content": "export const isServerRuntime = () => import.meta.server\n"
  },
  {
    "path": "src/runtime/utils/session.ts",
    "content": "import { useCookie, useNuxtApp } from '#app'\nimport { unref } from 'vue'\nimport { useSanctumLogger } from '../utils/logging'\nimport { useSanctumConfig } from '../composables/useSanctumConfig'\nimport { useSanctumAppConfig } from '../composables/useSanctumAppConfig'\nimport { useSanctumAuth } from '../composables/useSanctumAuth'\nimport { isServerRuntime } from './runtime'\n\n/**\n * Validates existence of the current user session details\n */\nexport async function isUserSessionActive(): Promise<boolean> {\n  const nuxtApp = useNuxtApp()\n  const options = useSanctumConfig()\n  const appConfig = useSanctumAppConfig()\n  const { isAuthenticated, refreshIdentity } = useSanctumAuth()\n\n  if (isAuthenticated.value === false) {\n    return false\n  }\n\n  const logger = useSanctumLogger(options.logLevel, isServerRuntime())\n\n  if (options.mode == 'cookie') {\n    const csrfToken = unref(\n      useCookie(\n        options.csrf.cookie!,\n        { readonly: true, watch: false },\n      ),\n    )\n\n    if (!csrfToken) {\n      try {\n        logger.debug('[sanctum] csrf cookie is outdated, refreshing identity')\n        await refreshIdentity()\n      }\n      catch {\n        logger.debug('[sanctum] unable to refresh identity on route change')\n      }\n    }\n  }\n\n  if (options.mode == 'token') {\n    const token = await appConfig.tokenStorage!.get(nuxtApp)\n\n    if (!token) {\n      try {\n        logger.debug('[sanctum] csrf token is outdated, refreshing identity')\n        await refreshIdentity()\n      }\n      catch {\n        logger.debug('[sanctum] unable to refresh identity on route change')\n      }\n    }\n  }\n\n  return isAuthenticated.value\n}\n"
  },
  {
    "path": "src/templates.ts",
    "content": "import { addTypeTemplate } from '@nuxt/kit'\nimport type { Resolver } from '@nuxt/kit'\nimport { relative, resolve } from 'pathe'\n\nexport const registerTypeTemplates = (resolver: Resolver) => {\n  addTypeTemplate({\n    filename: 'types/sanctum.d.ts',\n    getContents: ({ nuxt }) => {\n      const { buildDir } = nuxt.options\n      const getRelativePath = (path: string) => relative(resolve(buildDir, './types'), resolver.resolve(path))\n      return `// Generated by nuxt-auth-sanctum module\nimport type { NuxtApp } from '#app'\nimport type { HookResult } from '@nuxt/schema'\nimport type { FetchContext } from 'ofetch'\nimport type { ConsolaInstance } from 'consola'\nimport type { SanctumAppConfig } from '${getRelativePath('./runtime/types/config.ts')}';\nimport type { SanctumGlobalMiddlewarePageMeta } from '${getRelativePath('./runtime/types/meta.ts')}';\n\ndeclare module '@nuxt/schema' {\n  interface AppConfigInput {\n    sanctum?: SanctumAppConfig\n  }\n}\n\ndeclare module '#app' {\n  interface PageMeta {\n    /**\n     * Sanctum global middleware page configuration.\n     */\n    sanctum?: Partial<SanctumGlobalMiddlewarePageMeta>\n  }\n\n  interface RuntimeNuxtHooks {\n    /**\n     * Triggers when receiving an error response.\n     */\n    'sanctum:error:response': (context: FetchContext) => HookResult\n    /**\n     * Triggers when receiving an error on fetch request.\n     */\n    'sanctum:error:request': (context: FetchContext) => HookResult\n    /**\n     * Triggers when the user has been redirected.\n     */\n    'sanctum:redirect': (path: string) => HookResult\n    /**\n     * Triggers when an initial user identity request has been made.\n     */\n    'sanctum:init': () => HookResult\n    /**\n     * Triggers when user identity has been refreshed.\n     */\n    'sanctum:refresh': () => HookResult\n    /**\n     * Triggers when user successfully logs in.\n     */\n    'sanctum:login': () => HookResult\n    /**\n     * Triggers when user successfully logs out.\n     */\n    'sanctum:logout': () => HookResult\n    /**\n     * Triggers on every client request.\n     */\n    'sanctum:request': (app: NuxtApp, ctx: FetchContext, logger: ConsolaInstance) => HookResult\n    /**\n     * Triggers on every server response.\n     */\n    'sanctum:response': (app: NuxtApp, ctx: FetchContext, logger: ConsolaInstance) => HookResult\n    /**\n     * Triggers to register custom token storage.\n     * Call useSanctumTokenStorage() inside this hook.\n     */\n    'sanctum:storage:token': () => HookResult\n  }\n}\n\ndeclare module 'nitropack/types' {\n  interface NitroRuntimeHooks {\n    /**\n     * Triggers on every proxy client request.\n     */\n    'sanctum:proxy:request': (ctx: FetchContext, logger: ConsolaInstance) => void\n  }\n}\n\nexport {}`\n    },\n  })\n}\n"
  },
  {
    "path": "test/helpers/constants.ts",
    "content": "export const TEST_CONFIG = {\n  BASE_URL: 'http://localhost:80',\n  CUSTOM_BASE_URL: 'http://remote-host.dev',\n  CUSTOM_ORIGIN: 'http://custom-origin.dev',\n  CSRF_COOKIE_NAME: 'cookie_name',\n  CSRF_HEADER_NAME: 'header_name',\n  CSRF_ENDPOINT: '/api/token',\n  AUTH_TOKEN: 'abc123token',\n  CSRF_TOKEN: 'csrf-token-cookie-value',\n  REDIRECT_LOGIN: '/login',\n  REDIRECT_HOME: '/',\n}\n\nexport const HTTP_METHODS_REQUIRING_CSRF = ['POST', 'PUT', 'PATCH', 'DELETE']\n\nexport const COMMON_HEADERS = {\n  CONTENT_TYPE: 'content-type',\n  AUTHORIZATION: 'authorization',\n  ACCEPT: 'accept',\n  COOKIE: 'cookie',\n  USER_AGENT: 'user-agent',\n  ORIGIN: 'origin',\n  REFERER: 'referer',\n  SET_COOKIE: 'set-cookie',\n  ACCESS_CONTROL_ALLOW_CREDENTIALS: 'access-control-allow-credentials',\n  ACCESS_CONTROL_ALLOW_ORIGIN: 'access-control-allow-origin',\n}"
  },
  {
    "path": "test/helpers/mocks.ts",
    "content": "import type { NuxtApp } from '#app'\nimport type { ConsolaInstance } from 'consola'\nimport { vi } from 'vitest'\n\nexport function createMock<T>(mock: object = {}): T {\n  return mock as T\n}\n\nexport function createAppMock(mock: object = {}): NuxtApp {\n  const resolved = {\n    callHook: vi.fn(),\n    runWithContext: vi.fn().mockImplementation((fn: () => void) => { fn() }),\n    ...mock,\n  }\n\n  return createMock<NuxtApp>(resolved)\n}\n\nexport function createLoggerMock(mock: object = {}): ConsolaInstance {\n  const resolved = {\n    debug: vi.fn(),\n    info: vi.fn(),\n    warn: vi.fn(),\n    error: vi.fn(),\n    trace: vi.fn(),\n    ...mock,\n  }\n\n  return createMock<ConsolaInstance>(resolved)\n}\n"
  },
  {
    "path": "test/unit/config.test.ts",
    "content": "import { describe, it, expect } from 'vitest'\nimport { defu } from 'defu'\nimport { defaultModuleOptions } from '../../src/config'\nimport type { ModuleOptions } from '../../src/runtime/types/options'\n\ndescribe('default config', () => {\n  it('should have correct default values for all config keys', () => {\n    expect(defaultModuleOptions).toStrictEqual({\n      baseUrl: 'http://localhost:80',\n      mode: 'cookie',\n      userStateKey: 'sanctum.user.identity',\n      redirectIfAuthenticated: false,\n      redirectIfUnauthenticated: false,\n      endpoints: {\n        csrf: '/sanctum/csrf-cookie',\n        login: '/login',\n        logout: '/logout',\n        user: '/api/user',\n      },\n      csrf: {\n        cookie: 'XSRF-TOKEN',\n        header: 'X-XSRF-TOKEN',\n      },\n      client: {\n        retry: false,\n        initialRequest: true,\n      },\n      redirect: {\n        keepRequestedRoute: false,\n        onLogin: '/',\n        onLogout: '/',\n        onAuthOnly: '/login',\n        onGuestOnly: '/',\n      },\n      globalMiddleware: {\n        enabled: false,\n        prepend: false,\n        allow404WithoutAuth: true,\n      },\n      logLevel: 3,\n      appendPlugin: false,\n      serverProxy: {\n        enabled: false,\n        route: '/api/sanctum',\n        baseUrl: 'http://localhost:80',\n      },\n    })\n  })\n})\n\ndescribe('config merging', () => {\n  it('should merge user config with defaults', () => {\n    const userConfig = {\n      baseUrl: 'https://api.example.com',\n      mode: 'token' as const,\n      endpoints: {\n        user: '/api/v2/user',\n      },\n      redirect: {\n        onLogin: '/dashboard',\n      },\n    }\n\n    const merged = defu(userConfig, defaultModuleOptions) as ModuleOptions\n\n    expect(merged.baseUrl).toBe('https://api.example.com')\n    expect(merged.mode).toBe('token')\n    expect(merged.logLevel).toBe(defaultModuleOptions.logLevel)\n\n    expect(merged.endpoints!.csrf).toBe(defaultModuleOptions.endpoints!.csrf)\n    expect(merged.endpoints!.user).toBe('/api/v2/user')\n\n    expect(merged.redirect!.onLogin).toBe('/dashboard')\n    expect(merged.redirect!.onLogout).toBe(defaultModuleOptions.redirect!.onLogout)\n  })\n})\n\ndescribe('type safety', () => {\n  it('should accept valid mode values', () => {\n    const cookieConfig: ModuleOptions = {\n      ...defaultModuleOptions,\n      mode: 'cookie',\n    }\n    expect(cookieConfig.mode).toBe('cookie')\n\n    const tokenConfig: ModuleOptions = {\n      ...defaultModuleOptions,\n      mode: 'token',\n    }\n    expect(tokenConfig.mode).toBe('token')\n  })\n\n  it('should allow boolean redirect options', () => {\n    const config: ModuleOptions = {\n      ...defaultModuleOptions,\n      redirect: {\n        onLogin: false,\n        onLogout: false,\n        onAuthOnly: false,\n        onGuestOnly: false,\n        keepRequestedRoute: true,\n      },\n    }\n\n    expect(config.redirect!.onLogin).toBe(false)\n    expect(config.redirect!.onLogout).toBe(false)\n  })\n})\n"
  },
  {
    "path": "test/unit/constants.test.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { IDENTITY_LOADED_KEY } from '../../src/runtime/utils/constants'\n\ndescribe('constants', () => {\n  it('has expected identity key', () => {\n    expect(IDENTITY_LOADED_KEY).toBe('sanctum.user.loaded')\n  })\n})\n"
  },
  {
    "path": "test/unit/credentials.test.ts",
    "content": "import { describe, expect, it, beforeEach, afterEach } from 'vitest'\nimport { determineCredentialsMode } from '../../src/runtime/utils/credentials'\n\ndescribe('credentials', () => {\n  let deleteCredentials: () => boolean\n\n  beforeEach(() => {\n    deleteCredentials = () => delete (global.Request.prototype as unknown as Record<string, unknown>)['credentials']\n  })\n\n  afterEach(() => {\n    Object.defineProperty(global.Request.prototype, 'credentials', {\n      value: { toString: () => 'include' },\n      writable: true,\n      configurable: true,\n    })\n  })\n\n  it('returns \"include\" when credentials are supported', () => {\n    expect(determineCredentialsMode()).toBe('include')\n  })\n\n  it('returns \"undefined\" when credentials are not supported', () => {\n    deleteCredentials()\n    expect(determineCredentialsMode()).toBeUndefined()\n  })\n})\n"
  },
  {
    "path": "test/unit/formatter.test.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { trimTrailingSlash } from '../../src/runtime/utils/formatter'\n\ndescribe('formatters', () => {\n  describe('trimTrailingSlash', () => {\n    it('does not remove root slash', () => {\n      const value = '/'\n      expect(trimTrailingSlash(value)).toBe(value)\n    })\n\n    it('returns unchanged string if correct', () => {\n      const value = '/home'\n      expect(trimTrailingSlash(value)).toBe(value)\n    })\n\n    it('returns path without trailing slash', () => {\n      const value = '/home/'\n      expect(trimTrailingSlash(value)).toBe('/home')\n    })\n\n    it('removes only one slash', () => {\n      const value = '/home//'\n      expect(trimTrailingSlash(value)).toBe('/home/')\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/interceptors/request/logging.test.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport { describe, it, expect, vi, beforeEach } from 'vitest'\nimport { logRequestHeaders } from '../../../../src/runtime/interceptors/request/logging'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\n\ndescribe('request interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('logRequestHeaders', () => {\n    it('writes trace log on request with plain headers object', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        request: new URL('https://example.com/api/user'),\n        options: {\n          headers: {\n            'Content-Type': 'application/json',\n            'Authorization': 'Bearer token123',\n          },\n        },\n      })\n\n      await logRequestHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.trace).toHaveBeenCalledWith(\n        expect.stringContaining('Request headers for \"https://example.com/api/user\"'),\n        {\n          'Content-Type': 'application/json',\n          'Authorization': 'Bearer token123',\n        },\n      )\n    })\n\n    it('writes trace log on request with Headers instance', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        request: new URL('https://example.com/api/user'),\n        options: {\n          headers: new Headers({\n            'Content-Type': 'application/json',\n            'Authorization': 'Bearer token123',\n          }),\n        },\n      })\n\n      await logRequestHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.trace).toHaveBeenCalledWith(\n        expect.stringContaining('Request headers for \"https://example.com/api/user\"'),\n        {\n          'content-type': 'application/json',\n          'authorization': 'Bearer token123',\n        },\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/interceptors/request/params.test.ts",
    "content": "import { describe, it, expect, vi, beforeEach } from 'vitest'\nimport { setRequestParams } from '../../../../src/runtime/interceptors/request/params'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\nimport type { FetchContext } from 'ofetch'\n\ndescribe('request interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('setRequestParams', () => {\n    it('sets application/json accept header if not set', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'GET',\n          headers: new Headers(),\n        },\n      })\n\n      await setRequestParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.headers?.get('Accept')).toBe('application/json')\n      expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('[request] added default Accept header'))\n    })\n\n    it('does not override existing accept header', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'GET',\n          headers: new Headers({ Accept: 'application/xml' }),\n        },\n      })\n\n      await setRequestParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.headers?.get('Accept')).toBe('application/xml')\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('updates FormData body on PUT requests', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const formData = new FormData()\n      formData.append('name', 'test')\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'PUT',\n          headers: new Headers(),\n          body: formData,\n        },\n      })\n\n      await setRequestParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.method).toBe('POST')\n      expect((ctx.options.body as FormData)!.get('_method')).toBe('PUT')\n      expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('[request] changed PUT to POST method'))\n    })\n\n    it('does not modify non-FormData PUT body', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'PUT',\n          headers: new Headers({ Accept: 'application/json' }),\n          body: { name: 'test' },\n        },\n      })\n\n      await setRequestParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.method).toBe('PUT')\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/interceptors/request/stateful.test.ts",
    "content": "import { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { setStatefulParams } from '../../../../src/runtime/interceptors/request/stateful'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\nimport { TEST_CONFIG, HTTP_METHODS_REQUIRING_CSRF } from '../../../helpers/constants'\nimport type { FetchContext } from 'ofetch'\n\nconst {\n  useSanctumConfigMock,\n  isServerRuntimeMock,\n  useRequestHeadersMock,\n  useRequestURLMock,\n  useCookieMock,\n  refreshCookieMock,\n  fetchMock,\n} = vi.hoisted(() => {\n  return {\n    useSanctumConfigMock: vi.fn(),\n    isServerRuntimeMock: vi.fn(),\n    useRequestHeadersMock: vi.fn(),\n    useRequestURLMock: vi.fn(),\n    useCookieMock: vi.fn(),\n    refreshCookieMock: vi.fn(),\n    fetchMock: vi.fn(),\n  }\n})\n\nvi.mock(\n  '../../../../src/runtime/composables/useSanctumConfig',\n  () => ({ useSanctumConfig: useSanctumConfigMock }),\n)\n\nvi.mock(\n  '../../../../src/runtime/utils/runtime',\n  () => ({ isServerRuntime: isServerRuntimeMock.mockReturnValue(true) }),\n)\n\nvi.mock(\n  '#app',\n  () => ({\n    useRequestHeaders: useRequestHeadersMock,\n    useRequestURL: useRequestURLMock,\n    useCookie: useCookieMock,\n    refreshCookie: refreshCookieMock,\n  }),\n)\n\nvi.stubGlobal('$fetch', fetchMock)\n\ndescribe('request interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n    vi.restoreAllMocks()\n  })\n\n  describe('setStatefulParams', () => {\n    it('no-op when token mode is enabled', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'token' })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({})\n\n      await setStatefulParams(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).not.toHaveBeenCalled()\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('skips client headers in CSR', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: { method: undefined },\n      })\n\n      await setStatefulParams(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('appends client headers in SSR [cookie, user-agent, origin]', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        mode: 'cookie',\n        origin: TEST_CONFIG.CUSTOM_ORIGIN,\n      })\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestHeadersMock.mockReturnValue({\n        'cookie': 'random-cookie=value',\n        'user-agent': 'random-user-agent',\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: undefined,\n          headers: new Headers(),\n        },\n      })\n\n      await setStatefulParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.headers).toStrictEqual(\n        new Headers({\n          'Referer': TEST_CONFIG.CUSTOM_ORIGIN,\n          'Origin': TEST_CONFIG.CUSTOM_ORIGIN,\n          'Cookie': 'random-cookie=value',\n          'User-Agent': 'random-user-agent',\n        }),\n      )\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).toHaveBeenCalledWith(['cookie', 'user-agent'])\n      expect(useRequestURLMock).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(\n        '[request] added client headers to server request',\n        ['Referer', 'Origin', 'Cookie', 'User-Agent'],\n      )\n    })\n\n    it('appends request origin if not provided', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestHeadersMock.mockReturnValue({\n        'cookie': 'random-cookie=value',\n        'user-agent': 'random-user-agent',\n      })\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: undefined,\n          headers: new Headers(),\n        },\n      })\n\n      await setStatefulParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.headers).toStrictEqual(\n        new Headers({\n          'Referer': TEST_CONFIG.CUSTOM_ORIGIN,\n          'Origin': TEST_CONFIG.CUSTOM_ORIGIN,\n          'Cookie': 'random-cookie=value',\n          'User-Agent': 'random-user-agent',\n        }),\n      )\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).toHaveBeenCalledWith(['cookie', 'user-agent'])\n      expect(useRequestURLMock).toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(\n        '[request] added client headers to server request',\n        ['Referer', 'Origin', 'Cookie', 'User-Agent'],\n      )\n    })\n\n    describe.each(HTTP_METHODS_REQUIRING_CSRF)('appends CSRF for %s method', (method) => {\n      it(`appends CSRF for ${method} method`, async () => {\n        useSanctumConfigMock.mockReturnValue({\n          mode: 'cookie',\n          csrf: {\n            cookie: TEST_CONFIG.CSRF_COOKIE_NAME,\n            header: TEST_CONFIG.CSRF_HEADER_NAME,\n          },\n        })\n        isServerRuntimeMock.mockReturnValue(false)\n        useCookieMock.mockReturnValue({ value: TEST_CONFIG.CSRF_TOKEN })\n\n        const mockApp = createAppMock()\n        const mockLogger = createLoggerMock()\n\n        const ctx = createMock<FetchContext>({\n          options: {\n            method: method.toLowerCase() as 'post' | 'put' | 'patch' | 'delete',\n            headers: new Headers(),\n          },\n        })\n\n        await setStatefulParams(mockApp, ctx, mockLogger)\n\n        expect(ctx.options.headers).toStrictEqual(\n          new Headers({\n            [TEST_CONFIG.CSRF_HEADER_NAME]: TEST_CONFIG.CSRF_TOKEN,\n          }),\n        )\n\n        expect(isServerRuntimeMock).toHaveBeenCalled()\n        expect(useRequestHeadersMock).not.toHaveBeenCalled()\n        expect(useRequestURLMock).not.toHaveBeenCalled()\n        expect(useCookieMock).toHaveBeenCalledWith(\n          TEST_CONFIG.CSRF_COOKIE_NAME,\n          { readonly: true, watch: false },\n        )\n        expect(mockLogger.debug).toHaveBeenCalledWith('[request] added header_name header')\n      })\n    })\n\n    it('throws error if cookie name is undefined', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        mode: 'cookie',\n        csrf: {\n          cookie: undefined,\n          header: undefined,\n        },\n      })\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'post',\n          headers: new Headers(),\n        },\n      })\n\n      await expect(setStatefulParams(mockApp, ctx, mockLogger))\n        .rejects\n        .toThrow('`sanctum.csrf.cookie` is not defined')\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).not.toHaveBeenCalled()\n      expect(useRequestURLMock).not.toHaveBeenCalled()\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('throws error if cookie header name is undefined', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        mode: 'cookie',\n        csrf: {\n          cookie: TEST_CONFIG.CSRF_COOKIE_NAME,\n          header: undefined,\n        },\n      })\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'post',\n          headers: new Headers(),\n        },\n      })\n\n      await expect(setStatefulParams(mockApp, ctx, mockLogger))\n        .rejects\n        .toThrow('`sanctum.csrf.header` is not defined')\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).not.toHaveBeenCalled()\n      expect(useRequestURLMock).not.toHaveBeenCalled()\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('requests CSRF token if not fetched yet', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        mode: 'cookie',\n        baseUrl: TEST_CONFIG.CUSTOM_BASE_URL,\n        csrf: {\n          cookie: TEST_CONFIG.CSRF_COOKIE_NAME,\n          header: TEST_CONFIG.CSRF_HEADER_NAME,\n        },\n        endpoints: {\n          csrf: TEST_CONFIG.CSRF_ENDPOINT,\n        },\n      })\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const csrfToken = { value: undefined as string | undefined }\n\n      useCookieMock.mockImplementation(() => csrfToken)\n      fetchMock.mockImplementation(() => csrfToken.value = 'token-value')\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'delete',\n          headers: new Headers(),\n        },\n      })\n\n      await setStatefulParams(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.headers).toStrictEqual(\n        new Headers({\n          [TEST_CONFIG.CSRF_HEADER_NAME]: 'token-value',\n        }),\n      )\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).not.toHaveBeenCalled()\n      expect(useRequestURLMock).not.toHaveBeenCalled()\n      expect(useCookieMock).toHaveBeenCalledWith(\n        TEST_CONFIG.CSRF_COOKIE_NAME,\n        { readonly: true, watch: false },\n      )\n      expect(fetchMock).toHaveBeenCalledWith(TEST_CONFIG.CSRF_ENDPOINT, {\n        baseURL: TEST_CONFIG.CUSTOM_BASE_URL,\n        credentials: 'include',\n      })\n      expect(mockLogger.debug).toHaveBeenCalledWith('[request] CSRF cookie has been initialized')\n      expect(refreshCookieMock).toHaveBeenCalledWith(TEST_CONFIG.CSRF_COOKIE_NAME)\n      expect(mockLogger.warn).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith('[request] added header_name header')\n    })\n\n    it('throws error if csrf endpoint is undefined', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        mode: 'cookie',\n        csrf: {\n          cookie: TEST_CONFIG.CSRF_COOKIE_NAME,\n          header: TEST_CONFIG.CSRF_HEADER_NAME,\n        },\n        endpoints: {\n          csrf: undefined,\n        },\n      })\n      isServerRuntimeMock.mockReturnValue(false)\n      useCookieMock.mockReturnValue({ value: undefined })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'delete',\n          headers: new Headers(),\n        },\n      })\n\n      await expect(setStatefulParams(mockApp, ctx, mockLogger))\n        .rejects\n        .toThrow('`sanctum.endpoints.csrf` is not defined')\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).not.toHaveBeenCalled()\n      expect(useRequestURLMock).not.toHaveBeenCalled()\n      expect(useCookieMock).toHaveBeenCalledWith(\n        TEST_CONFIG.CSRF_COOKIE_NAME,\n        { readonly: true, watch: false },\n      )\n      expect(refreshCookieMock).not.toHaveBeenCalled()\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('writes warning if no CSRF token returned from API', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        mode: 'cookie',\n        baseUrl: TEST_CONFIG.CUSTOM_BASE_URL,\n        csrf: {\n          cookie: TEST_CONFIG.CSRF_COOKIE_NAME,\n          header: TEST_CONFIG.CSRF_HEADER_NAME,\n        },\n        endpoints: {\n          csrf: TEST_CONFIG.CSRF_ENDPOINT,\n        },\n      })\n      isServerRuntimeMock.mockReturnValue(false)\n      useCookieMock.mockReturnValue({ value: undefined })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          method: 'delete',\n          headers: new Headers(),\n        },\n      })\n\n      await setStatefulParams(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestHeadersMock).not.toHaveBeenCalled()\n      expect(useRequestURLMock).not.toHaveBeenCalled()\n      expect(useCookieMock).toHaveBeenCalledWith(\n        TEST_CONFIG.CSRF_COOKIE_NAME,\n        { readonly: true, watch: false },\n      )\n      expect(fetchMock).toHaveBeenCalledWith(TEST_CONFIG.CSRF_ENDPOINT, {\n        baseURL: TEST_CONFIG.CUSTOM_BASE_URL,\n        credentials: 'include',\n      })\n      expect(mockLogger.debug).toHaveBeenCalledWith('[request] CSRF cookie has been initialized')\n      expect(refreshCookieMock).toHaveBeenCalledWith(TEST_CONFIG.CSRF_COOKIE_NAME)\n      expect(mockLogger.warn).toHaveBeenCalledWith('cookie_name cookie is missing, unable to set header_name header')\n      expect(mockLogger.debug).not.toHaveBeenCalledWith('[request] added header_name header')\n    })\n  })\n})"
  },
  {
    "path": "test/unit/interceptors/request/token.test.ts",
    "content": "import { describe, it, expect, vi, beforeEach } from 'vitest'\nimport { setTokenHeader } from '../../../../src/runtime/interceptors/request/token'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\nimport { TEST_CONFIG } from '../../../helpers/constants'\nimport type { FetchContext } from 'ofetch'\n\nconst {\n  useSanctumConfigMock,\n  useSanctumAppConfigMock,\n} = vi.hoisted(() => {\n  return {\n    useSanctumConfigMock: vi.fn().mockReturnValue({ mode: 'token' }),\n    useSanctumAppConfigMock: vi.fn().mockReturnValue({ tokenStorage: undefined }),\n  }\n})\n\nvi.mock(\n  '../../../../src/runtime/composables/useSanctumConfig',\n  () => ({ useSanctumConfig: useSanctumConfigMock }),\n)\n\nvi.mock(\n  '../../../../src/runtime/composables/useSanctumAppConfig',\n  () => ({ useSanctumAppConfig: useSanctumAppConfigMock }),\n)\n\ndescribe('request interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('setTokenHeader', () => {\n    it('skips when mode is not token', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          headers: new Headers(),\n        },\n      })\n\n      await setTokenHeader(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n      expect(ctx.options.headers?.get('Authorization')).toBeNull()\n    })\n\n    it('throws when tokenStorage not defined', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'token' })\n      useSanctumAppConfigMock.mockReturnValue({ tokenStorage: undefined })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          headers: new Headers(),\n        },\n      })\n\n      await expect(setTokenHeader(mockApp, ctx, mockLogger))\n        .rejects\n        .toThrow('`sanctum.tokenStorage` is not defined in app.config.ts')\n    })\n\n    it('skips when token is not in storage', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'token' })\n      useSanctumAppConfigMock.mockReturnValue({\n        tokenStorage: { get: vi.fn().mockResolvedValue(null) } as unknown,\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          headers: new Headers(),\n        },\n      })\n\n      await setTokenHeader(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('[request] authentication token is not set'))\n      expect(ctx.options.headers?.get('Authorization')).toBeNull()\n    })\n\n    it('sets Bearer token in authorization header', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'token' })\n      useSanctumAppConfigMock.mockReturnValue({\n        tokenStorage: { get: vi.fn().mockResolvedValue(TEST_CONFIG.AUTH_TOKEN) } as unknown,\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        options: {\n          headers: new Headers(),\n        },\n      })\n\n      await setTokenHeader(mockApp, ctx, mockLogger)\n\n      expect(ctx.options.headers?.get('Authorization')).toBe(`Bearer ${TEST_CONFIG.AUTH_TOKEN}`)\n      expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('[request] added Authorization token header'))\n    })\n  })\n})"
  },
  {
    "path": "test/unit/interceptors/response/errorHandler.test.ts",
    "content": "import { describe, it, expect, vi, beforeEach } from 'vitest'\nimport { handleResponseError } from '../../../../src/runtime/interceptors/response/errorHandler'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\nimport { TEST_CONFIG } from '../../../helpers/constants'\nimport type { FetchContext } from 'ofetch'\n\nconst {\n  useSanctumConfigMock,\n  useSanctumUserMock,\n  navigateToMock,\n  isServerRuntimeMock,\n} = vi.hoisted(() => {\n  return {\n    useSanctumConfigMock: vi.fn(),\n    useSanctumUserMock: vi.fn(),\n    navigateToMock: vi.fn(),\n    isServerRuntimeMock: vi.fn(),\n  }\n})\n\nvi.mock(\n  '../../../../src/runtime/composables/useSanctumConfig',\n  () => ({ useSanctumConfig: useSanctumConfigMock }),\n)\n\nvi.mock(\n  '../../../../src/runtime/composables/useSanctumUser',\n  () => ({ useSanctumUser: useSanctumUserMock }),\n)\n\nvi.mock(\n  '../../../../src/runtime/utils/runtime',\n  () => ({ isServerRuntime: isServerRuntimeMock.mockReturnValue(true) }),\n)\n\nvi.mock('#app', () => ({ navigateTo: navigateToMock }))\n\ndescribe('response interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('handleResponseError', () => {\n    it('writes warning on 419 response', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n      const ctx = createMock<FetchContext>({ response: { status: 419 } })\n\n      await handleResponseError(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).toHaveBeenCalledWith('CSRF token mismatch, check your API configuration')\n    })\n\n    it('resets identity on 401 response', async () => {\n      const mockUser = { value: { id: 1, name: 'John' } }\n\n      useSanctumUserMock.mockReturnValue(mockUser)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n      const ctx = createMock<FetchContext>({ response: { status: 401 } })\n\n      await handleResponseError(mockApp, ctx, mockLogger)\n\n      expect(mockUser.value).toBeNull()\n      expect(mockLogger.warn).toHaveBeenCalledWith('User session is not set in API or expired, resetting identity')\n    })\n\n    it('no-op on 401 response if not authenticated', async () => {\n      const mockUser = { value: null }\n\n      useSanctumUserMock.mockReturnValue(mockUser)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n      const ctx = createMock<FetchContext>({ response: { status: 401 } })\n\n      await handleResponseError(mockApp, ctx, mockLogger)\n\n      expect(mockUser.value).toBeNull()\n      expect(mockLogger.warn).not.toHaveBeenCalled()\n    })\n\n    it('redirects on client for 401 with redirect enabled', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        redirectIfUnauthenticated: true,\n        redirect: { onAuthOnly: TEST_CONFIG.REDIRECT_LOGIN },\n      })\n\n      useSanctumUserMock.mockReturnValue({ value: { id: 1 } })\n\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n      const ctx = createMock<FetchContext>({ response: { status: 401 } })\n\n      await handleResponseError(mockApp, ctx, mockLogger)\n\n      expect(mockApp.callHook).toHaveBeenCalledWith('sanctum:redirect', TEST_CONFIG.REDIRECT_LOGIN)\n      expect(navigateToMock).toHaveBeenCalledWith(TEST_CONFIG.REDIRECT_LOGIN)\n    })\n\n    it('skips redirect on server for 401', async () => {\n      useSanctumConfigMock.mockReturnValue({\n        redirectIfUnauthenticated: true,\n        redirect: { onAuthOnly: TEST_CONFIG.REDIRECT_LOGIN },\n      })\n\n      useSanctumUserMock.mockReturnValue({ value: { id: 1 } })\n\n      isServerRuntimeMock.mockReturnValue(true)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({ response: { status: 401 } })\n\n      await handleResponseError(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).toHaveBeenCalledWith('User session is not set in API or expired, resetting identity')\n      expect(mockApp.callHook).not.toHaveBeenCalled()\n      expect(navigateToMock).not.toHaveBeenCalled()\n    })\n\n    it('no action for other status codes', async () => {\n      const mockUser = { value: { id: 1 } }\n\n      useSanctumUserMock.mockReturnValue(mockUser)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({ response: { status: 500 } })\n\n      await handleResponseError(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).not.toHaveBeenCalled()\n      expect(mockUser.value).not.toBeNull()\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/interceptors/response/logging.test.ts",
    "content": "import type { FetchContext } from 'ofetch'\nimport { describe, expect, it, vi, beforeEach } from 'vitest'\nimport { logResponseHeaders } from '../../../../src/runtime/interceptors/response/logging'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\n\ndescribe('response interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('logResponseHeaders', () => {\n    it('writes headers when response is not empty', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        request: new URL('https://example.com/api/user'),\n        response: {\n          headers: new Headers({\n            'Content-Type': 'application/json',\n            'X-Custom-Header': 'value123',\n          }),\n        },\n      })\n\n      await logResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.trace).toHaveBeenCalledWith(\n        expect.stringContaining('Response headers for \"https://example.com/api/user\"'),\n        {\n          'content-type': 'application/json',\n          'x-custom-header': 'value123',\n        },\n      )\n    })\n\n    it('writes empty object when response is empty', async () => {\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        request: new URL('https://example.com/api/user'),\n        response: undefined,\n      })\n\n      await logResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.trace).toHaveBeenCalledWith(\n        expect.stringContaining('Response headers for \"https://example.com/api/user\"'),\n        {},\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/interceptors/response/proxy.test.ts",
    "content": "import { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { proxyResponseHeaders } from '../../../../src/runtime/interceptors/response/proxy'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\nimport type { FetchContext } from 'ofetch'\nimport type { H3Event, EventHandlerRequest } from 'h3'\n\nconst {\n  useSanctumConfigMock,\n  useRequestEventMock,\n  navigateToMock,\n  isServerRuntimeMock,\n} = vi.hoisted(() => {\n  return {\n    useSanctumConfigMock: vi.fn(),\n    useRequestEventMock: vi.fn(),\n    navigateToMock: vi.fn(),\n    isServerRuntimeMock: vi.fn(),\n  }\n})\n\nvi.mock(\n  '../../../../src/runtime/composables/useSanctumConfig',\n  () => ({ useSanctumConfig: useSanctumConfigMock }),\n)\n\nvi.mock(\n  '../../../../src/runtime/utils/runtime',\n  () => ({ isServerRuntime: isServerRuntimeMock.mockReturnValue(true) }),\n)\n\nvi.mock(\n  '#app',\n  () => ({\n    useRequestEvent: useRequestEventMock,\n    navigateTo: navigateToMock,\n  }),\n)\n\ndescribe('response interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('proxyResponseHeaders', () => {\n    it('no-op when token mode enabled', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'token' })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({})\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).not.toHaveBeenCalled()\n      expect(useRequestEventMock).not.toHaveBeenCalled()\n      expect(navigateToMock).not.toHaveBeenCalled()\n    })\n\n    it('writes warning on empty response', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({})\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).not.toHaveBeenCalled()\n      expect(useRequestEventMock).not.toHaveBeenCalled()\n      expect(navigateToMock).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith('[response] no response to process')\n    })\n\n    it('follows redirects from response', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const redirectUrl = 'http://redirect.dev'\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          redirected: true,\n          url: redirectUrl,\n        },\n      })\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestEventMock).not.toHaveBeenCalled()\n      expect(navigateToMock).toHaveBeenCalledWith(redirectUrl)\n      expect(mockApp.callHook).toHaveBeenCalledWith('sanctum:redirect', redirectUrl)\n      expect(mockApp.runWithContext).toHaveBeenCalled()\n      expect(mockLogger.debug).not.toHaveBeenCalled()\n    })\n\n    it('appends headers in SSR mode', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(true)\n\n      const mockEvent = createMock<H3Event<EventHandlerRequest>>({\n        node: {\n          res: {\n            getHeaders: vi\n              .fn()\n              .mockReturnValue({\n                'set-cookie': 'event-cookie-name=value-one',\n              }),\n            setHeader: vi.fn(),\n          },\n        },\n      })\n\n      useRequestEventMock.mockReturnValue(mockEvent)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            'set-cookie': 'response-cookie-name=value-two',\n          }),\n        },\n      })\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestEventMock).toHaveBeenCalledWith(mockApp)\n      expect(navigateToMock).not.toHaveBeenCalled()\n      expect(mockApp.callHook).not.toHaveBeenCalledWith()\n      expect(mockApp.runWithContext).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(\n        '[response] pass cookies from server to client response',\n        [\n          'event-cookie-name',\n          'response-cookie-name',\n        ],\n      )\n      expect(mockEvent.node.res.setHeader).toHaveBeenCalledWith(\n        'set-cookie',\n        [\n          'event-cookie-name=value-one',\n          'response-cookie-name=value-two',\n        ],\n      )\n    })\n\n    it('writes warning when request event is not defined', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(true)\n\n      useRequestEventMock.mockReturnValue(undefined)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        request: 'http://requested-url.dev',\n        response: {},\n      })\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestEventMock).toHaveBeenCalledWith(mockApp)\n      expect(navigateToMock).not.toHaveBeenCalled()\n      expect(mockApp.callHook).not.toHaveBeenCalledWith()\n      expect(mockApp.runWithContext).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(`[response] no event to pass cookies to the client [${ctx.request}]`)\n    })\n\n    it('writes debug when no cookies returned from API', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(true)\n\n      const mockEvent = createMock<H3Event<EventHandlerRequest>>({\n        node: {\n          res: {\n            getHeaders: vi\n              .fn()\n              .mockReturnValue({\n                'set-cookie': 'event-cookie-name=value-one',\n              }),\n            setHeader: vi.fn(),\n          },\n        },\n      })\n\n      useRequestEventMock.mockReturnValue(mockEvent)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        request: 'http://requested-url.dev',\n        response: {\n          headers: new Headers({}),\n        },\n      })\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestEventMock).toHaveBeenCalledWith(mockApp)\n      expect(navigateToMock).not.toHaveBeenCalled()\n      expect(mockApp.callHook).not.toHaveBeenCalledWith()\n      expect(mockApp.runWithContext).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(`[response] no cookies to pass to the client [${ctx.request}]`)\n      expect(mockLogger.debug).toHaveBeenCalledWith(\n        '[response] pass cookies from server to client response',\n        [\n          'event-cookie-name',\n        ],\n      )\n      expect(mockEvent.node.res.setHeader).toHaveBeenCalledWith(\n        'set-cookie',\n        [\n          'event-cookie-name=value-one',\n        ],\n      )\n    })\n\n    it('deduplicates cookies in the response', async () => {\n      useSanctumConfigMock.mockReturnValue({ mode: 'cookie' })\n      isServerRuntimeMock.mockReturnValue(true)\n\n      const mockEvent = createMock<H3Event<EventHandlerRequest>>({\n        node: {\n          res: {\n            getHeaders: vi\n              .fn()\n              .mockReturnValue({\n                'set-cookie': 'unique-cookie-name=value-one',\n              }),\n            setHeader: vi.fn(),\n          },\n        },\n      })\n\n      useRequestEventMock.mockReturnValue(mockEvent)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            'set-cookie': 'unique-cookie-name=value-two',\n          }),\n        },\n      })\n\n      await proxyResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(isServerRuntimeMock).toHaveBeenCalled()\n      expect(useRequestEventMock).toHaveBeenCalledWith(mockApp)\n      expect(navigateToMock).not.toHaveBeenCalled()\n      expect(mockApp.callHook).not.toHaveBeenCalledWith()\n      expect(mockApp.runWithContext).not.toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(\n        '[response] pass cookies from server to client response',\n        [\n          'unique-cookie-name',\n        ],\n      )\n      expect(mockEvent.node.res.setHeader).toHaveBeenCalledWith(\n        'set-cookie',\n        [\n          'unique-cookie-name=value-two',\n        ],\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/interceptors/response/validation.test.ts",
    "content": "import { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { validateResponseHeaders } from '../../../../src/runtime/interceptors/response/validation'\nimport { createAppMock, createLoggerMock, createMock } from '../../../helpers/mocks'\nimport { TEST_CONFIG, COMMON_HEADERS } from '../../../helpers/constants'\nimport type { FetchContext } from 'ofetch'\n\nconst {\n  isServerRuntimeMock,\n  useRequestURLMock,\n  useRuntimeConfigMock,\n} = vi.hoisted(() => {\n  return {\n    isServerRuntimeMock: vi.fn(),\n    useRequestURLMock: vi.fn(),\n    useRuntimeConfigMock: vi.fn(),\n  }\n})\n\nvi.mock(\n  '../../../../src/runtime/utils/runtime',\n  () => ({ isServerRuntime: isServerRuntimeMock }),\n)\n\nvi.mock(\n  '#app',\n  () => ({ useRequestURL: useRequestURLMock }),\n)\n\nvi.mock(\n  '#imports',\n  () => ({ useRuntimeConfig: useRuntimeConfigMock }),\n)\n\ndescribe('response interceptors', () => {\n  beforeEach(() => {\n    vi.clearAllMocks()\n  })\n\n  describe('validateResponseHeaders', () => {\n    it('skips validation in CSR mode', async () => {\n      isServerRuntimeMock.mockReturnValue(false)\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({})\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.debug).toHaveBeenCalledWith('[response] skipping headers validation on CSR')\n    })\n\n    it('writes warning log on empty headers', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {},\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({})\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).toHaveBeenCalledWith('[response] no headers returned from API')\n    })\n\n    it('passes validation when all headers are present', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).not.toHaveBeenCalledWith('[response] `set-cookie` header is missing, CSRF token will not be set')\n    })\n\n    it('writes warning if cookie header is missing [cookie mode]', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).toHaveBeenCalledWith('[response] `set-cookie` header is missing, CSRF token will not be set')\n    })\n\n    it('does not validate cookie header [token mode]', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'token',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).not.toHaveBeenCalled()\n    })\n\n    it('writes warning if content-type header is missing', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(mockLogger.warn).toHaveBeenCalledWith('[response] \"content-type\" header is missing')\n    })\n\n    it('writes debug if content-type header is not JSON', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'unknown',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.debug).toHaveBeenCalledWith(`[response] 'content-type' is present in response but different (expected: application/json, got: unknown)`)\n    })\n\n    it('writes warning if credentials header is missing [cookie mode]', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.warn).toHaveBeenCalledWith(`[response] 'access-control-allow-credentials' header is missing or invalid (expected: true, got: null)`)\n    })\n\n    it('writes warning if credentials header is disabled [cookie mode]', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'false',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.warn).toHaveBeenCalledWith(`[response] 'access-control-allow-credentials' header is missing or invalid (expected: true, got: false)`)\n    })\n\n    it('skips validation of credentials header [token mode]', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'token',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: TEST_CONFIG.CUSTOM_ORIGIN,\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.warn).not.toHaveBeenCalled()\n    })\n\n    it('writes warning if origin header is missing', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.warn).toHaveBeenCalledWith(`[response] 'access-control-allow-origin' header is missing or invalid (expected: ${TEST_CONFIG.CUSTOM_ORIGIN}, got: null)`)\n    })\n\n    it('writes warning if origin header does not include request origin', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: 'http://another-host.dev',\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.warn).toHaveBeenCalledWith(`[response] 'access-control-allow-origin' header is missing or invalid (expected: ${TEST_CONFIG.CUSTOM_ORIGIN}, got: http://another-host.dev)`)\n    })\n\n    it('writes warning if origin header does not include origin from config', async () => {\n      isServerRuntimeMock.mockReturnValue(true)\n      useRequestURLMock.mockReturnValue({ origin: TEST_CONFIG.CUSTOM_ORIGIN })\n      useRuntimeConfigMock.mockReturnValue({\n        sanctum: {\n          mode: 'cookie',\n          origin: 'http://sanctum-host.dev',\n        },\n\n        public: {\n          sanctum: {\n            origin: 'http://client-sanctum-host.dev',\n          },\n        },\n      })\n\n      const mockApp = createAppMock()\n      const mockLogger = createLoggerMock()\n\n      const ctx = createMock<FetchContext>({\n        response: {\n          headers: new Headers({\n            [COMMON_HEADERS.SET_COOKIE]: 'header_value',\n            [COMMON_HEADERS.CONTENT_TYPE]: 'application/json',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true',\n            [COMMON_HEADERS.ACCESS_CONTROL_ALLOW_ORIGIN]: 'http://another-host.dev',\n          }),\n        },\n      })\n\n      await validateResponseHeaders(mockApp, ctx, mockLogger)\n\n      expect(useRuntimeConfigMock).toHaveBeenCalled()\n      expect(mockLogger.warn).toHaveBeenCalledWith(`[response] 'access-control-allow-origin' header is missing or invalid (expected: http://sanctum-host.dev, got: http://another-host.dev)`)\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/logging.test.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { useSanctumLogger } from '../../src/runtime/utils/logging'\n\ndescribe('logging', () => {\n  describe('useSanctumLogger', () => {\n    it('returns a ConsolaInstance', () => {\n      const logger = useSanctumLogger(3, false)\n\n      expect(logger).toBeDefined()\n\n      expect(typeof logger.info).toBe('function')\n      expect(typeof logger.debug).toBe('function')\n      expect(typeof logger.warn).toBe('function')\n      expect(typeof logger.error).toBe('function')\n      expect(typeof logger.trace).toBe('function')\n      expect(typeof logger.log).toBe('function')\n    })\n\n    it('applies log level correctly', () => {\n      const logger0 = useSanctumLogger(0, false)\n      const logger3 = useSanctumLogger(3, false)\n      const logger5 = useSanctumLogger(5, false)\n\n      expect(logger0.level).toBe(0)\n      expect(logger3.level).toBe(3)\n      expect(logger5.level).toBe(5)\n    })\n\n    it('creates logger with ssr tag in SSR environment', () => {\n      const logger = useSanctumLogger(3, true)\n      const tag = logger.options?.defaults?.tag ?? ''\n\n      expect(tag).toMatch('nuxt-auth-sanctum:ssr')\n    })\n\n    it('creates logger with csr tag in CSR environment', () => {\n      const logger = useSanctumLogger(3, false)\n      const tag = logger.options?.defaults?.tag ?? ''\n\n      expect(tag).toMatch('nuxt-auth-sanctum:csr')\n    })\n  })\n})\n"
  },
  {
    "path": "test/unit/runtime.test.ts",
    "content": "import { describe, expect, it } from 'vitest'\nimport { isServerRuntime } from '../../src/runtime/utils/runtime'\n\ndescribe('runtime', () => {\n  describe('isServerRuntime', () => {\n    it('returns import.meta.server value', () => {\n      const isServer = import.meta.server\n\n      expect(isServerRuntime()).toBe(isServer)\n    })\n  })\n})\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"extends\": \"./.nuxt/tsconfig.json\",\n  \"exclude\": [\"dist\", \"node_modules\", \"playground\", \"docs\", \"test/fixtures\"]\n}\n\n"
  },
  {
    "path": "vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config'\nimport { defineVitestProject } from '@nuxt/test-utils/config'\n\nexport default defineConfig({\n  test: {\n    hookTimeout: 10000,\n    testTimeout: 5000,\n    projects: [\n      {\n        test: {\n          name: 'unit',\n          include: ['test/unit/**/*.{test,spec}.ts'],\n          environment: 'node',\n          testTimeout: 5000,\n          hookTimeout: 10000,\n        },\n      },\n      {\n        test: {\n          name: 'e2e',\n          include: ['test/e2e/**/*.{test,spec}.ts'],\n          environment: 'node',\n          testTimeout: 30000,\n          hookTimeout: 30000,\n        },\n      },\n      await defineVitestProject({\n        test: {\n          name: 'nuxt',\n          include: ['test/nuxt/**/*.{test,spec}.ts'],\n          environment: 'nuxt',\n          testTimeout: 30000,\n          hookTimeout: 30000,\n        },\n      }),\n    ],\n  },\n})\n"
  }
]