diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..da284b83 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,130 @@ +# Contributing guide + +Thank you to everyone who has contributed to this project by writing issues or opening pull requests. Your efforts help us improve and grow. We appreciate your support and look forward to your contributions! + +Please see the GitHub [Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-community-guidelines) and follow any templates configured in GitHub when reporting bugs, requesting enhancements, or contributing code. + +If you would like to report a security vulnerability, please DO NOT post an issue but instead follow our [Security reporting guide](/SECURITY.md). + +## Bug reports + +A bug is a reproducible issue caused by the package. Reporting bugs is very helpful and allows us to address problems efficiently! + +### Before opening an issue + +Before opening a [new bug report](https://github.com/sidebase/nuxt-auth/issues/new?assignees=&labels=bug%2Cpending&projects=&template=bug-report.yaml), please: + +- **Search for other issues relating to this bug** using the Github Issue search +- **Check if the issue may have already been patched** in a newer version or inside the `main` branch +- **Isolate the issue** and create a reproduction + +Please refer to the fantastic Nuxt [bug report guidelines here](https://nuxt.com/docs/community/reporting-bugs) for more details on efficiently describing bugs. + +### Opening an issue + +To report a bug, please [open an issue](https://github.com/sidebase/nuxt-auth/issues/new?assignees=&labels=bug%2Cpending&projects=&template=bug-report.yaml) based on our Bug Reporting template. The template will prompt you with questions that will increase our ability to scope and address the bug. + +## Feature requests + +Feature requests are divided into two types: + +- **Enhancements**: Improve or extend an existing feature of the package +- **Feature request**: Add a new feature to the package + +Both types can be created using our [Enchantment issue template](https://github.com/sidebase/nuxt-auth/issues/new?assignees=&labels=pending%2Cenhancement&projects=&template=enhancement.yml). + +Before posting an enchantment, please consider the following questions: + +- What problem does this enhancement fix? +- How would you recommend implementing this enhancement? +- How would this enhancement change the entire package? + - Would it require a major, minor, or patch release? + - Which providers would be impacted by this change? + +> [!IMPORTANT] +> The more impact your Enchantment has on the package, the longer it may take to push, as we need to integrate it into our release cycle. + +> [!IMPORTANT] +> While we appreciate every request, we cannot accept them all. Please be understanding if we do not accept your request. + +## Pull requests + +Thank you to everyone who plans to open a pull request on our package! We appreciate your hard work and motivation to help us improve! + +Before opening a pull request, please open a corresponding issue outlining the bug or enhancement you are adding. If you plan to implement a more significant change to the code base, please discuss this with us in an issue before beginning your work. This is to avoid the risk of you spending a lot of time working on a contribution we may disagree with. + +### Your first contribution + +To start developing on this package, please follow the quick start guide below. + +1. [Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the project to your own personal GitHub +1. Setup a local fork of the project: + ```sh + # Clone the fork + git clone https://github.com// + # Navigate to the directory + cd + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/sidebase/nuxt-auth + # If you cloned a while ago, checkout the main branch and re-pull the latest changes + git checkout main + git pull + ``` +2. Set up the correct ppm version, using [Corepack](https://nodejs.org/api/corepack.html) and install the dependencies + ```sh + corepack enable ppm + ppm install + ``` +4. Create a new branch (based on the `main` branch): + ```sh + git checkout -b /- + ``` +5. Update the code to include your fix or Enchantment +6. Add or update any tests that relate to your changes. +7. Ensure that the `tests,` `lint,` and `prepack` all pass + ```sh + ppm prepack + ppm lint + ppm type check + + # Test any provider that you have modified + cd playground- + ppm build + ppm test:e2e + ``` +8. [Open a Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests), and fill out the provided fields. + +> [!TIP] +> Read more about how to use the included playground [here](../README.md#development). + +> [!IMPORTANT] +> By submitting a Pull request, you agree to license your work under the MIT license used by the project. + +### Reviews + +After you submit your Pull request, a member of our core team will review it. Please be patient with this process, as it can take up to 14 weeks, depending on the team's availability. + +After receiving a review, please address any comments left by the reviewer or debate them if you disagree. This process will repeat until the pull request is approved and merged! + +> [!NOTE] +> Besides a manual review of your pull request, we will run an automated CI pipeline on your code. + +## Release cycles + +This package follows [Semantic Versioning 2.0.0](https://semver.org/). + +- **MAJOR** version when you make incompatible API changes +- **MINOR** version when you add functionality in a backward-compatible manner +- **PATCH** version when you make backward compatible bug fixes + +If we release a pre-version of a new release, we will tag it with `next` in the npmjs release and add one of the following additions to the versions: + +- **ALPHA**: Early development build of a new release + - Not properly tested yet +- **RC**: Potential release candidate of a new release + - Internally tested on a series of demo apps + - We will begin to integrate the new version into our production apps as a final test + +## Additional Questions + +If you have any questions or would like to get in contact with us directly, feel free to [join our Discord server](https://discord.gg/NDDgQkcv3s)! diff --git a/.github/nuxt-auth-demo.png b/.github/nuxt-auth-demo.png deleted file mode 100644 index 673ae68f..00000000 Binary files a/.github/nuxt-auth-demo.png and /dev/null differ diff --git a/.github/nuxt-auth.jpg b/.github/nuxt-auth.jpg index 4e6881d9..19773668 100644 Binary files a/.github/nuxt-auth.jpg and b/.github/nuxt-auth.jpg differ diff --git a/.github/sync.yml b/.github/sync.yml deleted file mode 100644 index 1ce7fec1..00000000 --- a/.github/sync.yml +++ /dev/null @@ -1,8 +0,0 @@ -sidebase/nuxt-auth: - - docs/content - - docs/public -sidebase/docs: - - source: docs/content/ - dest: content/2.nuxt-auth/ - - source: docs/public/ - dest: public/ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 76e36418..80e64b31 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,6 +42,30 @@ jobs: # Check building - run: pnpm build + test-docs: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ env.NODE_VER }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VER }} + + - uses: pnpm/action-setup@v3 + name: Install pnpm + id: pnpm-install + with: + version: 8 + + # Install deps and prepare types + - run: pnpm i + - run: pnpm dev:prepare + + # Build docs + - run: cd docs && pnpm docs:build + test-playground-local: runs-on: ubuntu-latest defaults: diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 00000000..453ef065 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,62 @@ +name: Deploy Docs + +on: + push: + branches: [main] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required to calculate lastUpdated + - name: Install pnpm + uses: pnpm/action-setup@v3 + with: + version: 8 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Install dependencies + run: pnpm i + - name: Build with VitePress + run: cd docs/ && pnpm docs:build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + runs-on: ubuntu-latest + name: Deploy + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/sync.yaml b/.github/workflows/sync.yaml deleted file mode 100644 index 42c8e853..00000000 --- a/.github/workflows/sync.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: Sync docs -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@master - - name: Run GitHub File Sync - uses: BetaHuhn/repo-file-sync-action@v1 - with: - GH_PAT: ${{ secrets.GH_TOKEN }} diff --git a/.gitignore b/.gitignore index 49940026..8f2c2e60 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,7 @@ coverage Network Trash Folder Temporary Items .apdisk + +## Vite Press related +docs/.vitepress/cache +docs/.vitepress/dist diff --git a/CNAME b/CNAME index a9698b99..1cfc530e 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -auth.sidebase.io \ No newline at end of file +auth.sidebase.io diff --git a/README.md b/README.md index 704362a6..3355e9ef 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,34 @@ -![nuxt-auth demo page](.github/nuxt-auth.jpg) - -# 🔐 nuxt-auth - -[![npm version][npm-version-src]][npm-version-href] -[![npm downloads][npm-downloads-src]][npm-downloads-href] -[![GitHub stars](https://badgen.net/github/stars/sidebase/nuxt-auth)](https://GitHub.com/sidebase/nuxt-auth/) -[![License][license-src]][license-href] -[![Follow us on Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/sidebase_io) -[![Join our Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/NDDgQkcv3s) - -> `nuxt-auth` is a feature-packed, open-source authentication module for Nuxt 3 applications. -> Starting with v0.6 `nuxt-auth` also supports static Nuxt applications +![@sidebase/nuxt-auth banner](.github/nuxt-auth.jpg) + +# @sidebase/nuxt-auth + + +

+ + Version + + + Downloads + + + Downloads + + + License + + + Docs + + + Follow us on X + + + Join our Discord + +

+ + +> Authentication built for Nuxt 3! Easily add authentication via OAuth providers, credentials or Email Magic URLs! ## Quick Start @@ -18,99 +36,115 @@ npx nuxi@latest module add sidebase-auth ``` -Then visit the [Quick Start documentation](https://sidebase.io/nuxt-auth/getting-started/quick-start) to setup the module for <= v0.5 - the current stable version. +
+ Or install manually + + #### 1. Install the package as a dev dependency + + ```sh + npm i -D @sidebase/nuxt-auth + + pnpm i -D @sidebase/nuxt-auth + + yarn add --dev @sidebase/nuxt-auth + ``` + + #### 2. Add the modules to your `nuxt.config.ts` + + ```ts + export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'] + }) + ``` +
-Visit the [Quick Start documentation](https://sidebase.io/nuxt-auth/v0.6/getting-started/quick-start) to setup the module for >= v0.6 - the "future" experimental version with support for static Nuxt 3 apps and the `local` provider. +Then visit the [Quick Start documentation](https://auth.sidebase.io/guide/getting-started/introduction) to continue the configuration of your app! ## Features -`nuxt-auth` is a library with the goal of supporting authentication for any universal Nuxt 3 application. At the moment two providers are supported: -- [Auth.js / NextAuth.js](https://github.com/nextauthjs/next-auth) to offer the reliability & convenience of a 12k star library to the nuxt 3 ecosystem with a native developer experience (DX) -- `local` for static pages that rely on an external backend with a credential flow for authentication. `local` is supported starting with v0.6 of the module - -Features of the `authjs`-provider of `nuxt-auth` include: -- ✔️ Authentication providers: - - ✔️ OAuth (e.g., Github, Google, Twitter, Azure, ...) - - ✔️ Custom OAuth (write it yourself) - - ✔️ Credentials (password + username) - - ✔️ Email Magic URLs -- ✔️ Isomorphic / Universal Auth Composable `useAuth` supports: - - actions: `getSession`, `getCsrfToken`, `getProviders`, `signIn`, `signOut` - - getters: `status`, `data`, `lastRefreshedAt` - - full typescript support for all methods and property -- ✔️ Application-side middleware protection -- ✔️ Server-side middleware and endpoint protection -- ✔️ Advanced features for session life-cycle management: - - Refresh the session periodically - - Refresh the session on tab-refocus - - One time session fetch on page load, afterwards for specific actions (e.g., on navigation) - - 🚧 Session broadcasting between tabs (see #70) -- ✔️ Persistent sessions across requests -- ✔️ REST API: - - `GET /signin`, - - `POST /signin/:provider`, - - `GET/POST /callback/:provider`, - - `GET /signout`, - - `POST /signout`, - - `GET /session`, - - `GET /csrf`, - - `GET /providers` - -You can find a feature-table of all starting with v0.6 in the [nuxt-auth "next"-version-docs](https://sidebase.io/nuxt-auth/v0.6/getting-started). +`@sidebase/nuxt-auth` is a library with the goal of supporting authentication for any universal Nuxt 3 application. At the moment three providers are supported: +- [`authjs`](https://auth.sidebase.io/guide/authjs/quick-start): for non-static apps that want to use [Auth.js / NextAuth.js](https://github.com/nextauthjs/next-auth) to offer the reliability & convenience of a 23k star library to the Nuxt 3 ecosystem with a native developer experience (DX) +- [`local`](https://auth.sidebase.io/guide/local/quick-start): for static pages that rely on an external backend with a credential flow for authentication. +- [`refresh`](https://auth.sidebase.io/guide/local/quick-start#refresh-token): for static pages that rely on an external backend with a credential flow and refresh tokens for authentication. -## Demo Page +You can find a full list of our features, as well as which provider supports each feature [on our docs](https://auth.sidebase.io/guide/getting-started/choose-provider). + +### Authentication providers: +- OAuth (e.g., Github, Google, Twitter, Azure, ...) +- Custom OAuth (write it yourself) +- Credentials (password + username) +- Email Magic URLs + +### Application Side Session Managment using [`useAuth`](https://auth.sidebase.io/guide/application-side/session-access#useauth-composable) +- Session fetching with `status`, `data` and `lastRefreshedAt` +- Methods to `getSession`, `getCsrfToken`, `getProviders`, `signIn` and `signOut` +- Full TypeScript support for all methods and properties + +### Application protection +- Application-side middleware protection for the [full application](https://auth.sidebase.io/guide/application-side/protecting-pages#global-middleware) or [specific pages](https://auth.sidebase.io/guide/application-side/protecting-pages#local-middleware) +- Server-side [middleware](https://auth.sidebase.io/guide/authjs/server-side/session-access#server-middleware) and [endpoint protection](https://auth.sidebase.io/guide/authjs/server-side/session-access#endpoint-protection) -Visit the [`nuxt-auth` demo page here](https://nuxt-auth-example.sidebase.io/): -![nuxt-auth demo page](.github/nuxt-auth-demo.png) +### Advanced features for session life-cycle management: +- Pre-built and [customizable refresh behaviour](https://auth.sidebase.io/guide/application-side/configuration#sessionrefresh) + - Refresh the session periodically + - Refresh the session on tab-refocus + - One time session fetch on page load, afterwards for specific actions (e.g., on navigation) +- Completly configure the Refresh behaviour of your application using the [`RefreshHandler`](https://auth.sidebase.io/guide/application-side/configuration#refreshhandler) -You can find the [demo source-code here](https://github.com/sidebase/nuxt-auth-example). +### Server Side utilities +- Session access using [`getServerSession`](https://auth.sidebase.io/guide/authjs/server-side/session-access) +- JWT Token access using [`getToken`](https://auth.sidebase.io/guide/authjs/server-side/jwt-access) +- Server-side [middleware](https://auth.sidebase.io/guide/authjs/server-side/session-access#server-middleware) and [endpoint protection](https://auth.sidebase.io/guide/authjs/server-side/session-access#endpoint-protection) + +## Demo Page + +Want to get a preview of what `@sidebase/nuxt-auth` has to offer? Visit the [`nuxt-auth` demo page here](https://nuxt-auth-example.sidebase.io/). Peak into the [demo source-code here](https://github.com/sidebase/nuxt-auth-example). ## Development This project uses `pnpm` for development. - Run `pnpm dev:prepare` to generate type stubs. -- Use `pnpm dev` to start [the module playground](./playground) in development mode. +- Use `pnpm dev` inside a [module playground directory](#module-playground) to start a playground in development mode. - Run `pnpm lint` to run eslint - Run `pnpm typecheck` to run typescheck via tsc -- Run `pnpm publish --access public` to publish (bump version before) - - -[npm-version-src]: https://img.shields.io/npm/v/@sidebase/nuxt-auth/latest.svg -[npm-version-href]: https://npmjs.com/package/@sidebase/nuxt-auth - -[npm-downloads-src]: https://img.shields.io/npm/dt/@sidebase/nuxt-auth.svg -[npm-downloads-href]: https://npmjs.com/package/@sidebase/nuxt-auth - -[license-src]: https://img.shields.io/npm/l/@sidebase/nuxt-auth.svg -[license-href]: https://npmjs.com/package/@sidebase/nuxt-auth +- Run `pnpm publish --access public` to publish +- Run `pnpm publish --access public --tag next` to publish a pre-release ### Module Playground This module also has it's own playground: + ```sh > git clone https://github.com/sidebase/nuxt-auth > cd nuxt-auth -# **OPEN THE `~/playground/server/api/auth/[...].ts` and configure your own auth-provider +> cd playground-[PROVIDER] > pnpm i > pnpm dev:prepare > pnpm dev - -# -> open http://localhost:3000 ``` +#### Additional playground commands + +- Run `pnpm dev:prepare` to generate type stubs. +- Run `pnpm dev` to start the playground. +- Run `pnpm test:e2e` to run the end-to-end tests. +- Run `pnpm lint` to run eslint +- Run `pnpm typecheck` to run typescheck via tsc + #### Testing different Providers We have one playground per provider: -- [`local`](./playground-local) - [`authjs`](./playground-authjs) +- [`local`](./playground-local) +- [`refresh`](./playground-refresh) -**How to test static Nuxt 3 apps?** +##### How to test static Nuxt 3 apps? To test static Nuxt 3 apps we want to run a static frontend and a separate backend that will take over authentication: 1. `playground-local/nuxt.config.ts`: Add `baseURL: 'http://localhost:3001'` to the `auth`-config @@ -128,6 +162,19 @@ To test static Nuxt 3 apps we want to run a static frontend and a separate backe pnpm dev - # A seconds Nuxt app should now be running on http://localhost:3001. We use this purely for authentication + # A second Nuxt app should now be running on http://localhost:3001. + # We use this purely for authentication ``` 4. Visit [http://localhost:3000](http://localhost:3000) -> this should open the static application. Performing any auth-related actions, the app should send requests to the backend running on port `3001` + +## Contributing + +Thank you to everyone who has contributed to this project by writing issues or opening pull requests. Your efforts help us improve and grow. If you are interested in contributing, please take a moment to review our [Contributing Guidelines](.github/CONTRIBUTING.md). We appreciate your support and look forward to your contributions! + +## Acknowledgments + +`@sidebase/nuxt-auth` is supported by all of our amazing contributors and the [Nuxt 3 team](https://nuxters.nuxt.com/)! + + + + diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100755 index 69f6b69d..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -node_modules -*.iml -.idea -*.log* -.nuxt -.vscode -.DS_Store -coverage -dist -sw.* -.env -.output diff --git a/docs/.nuxtrc b/docs/.nuxtrc deleted file mode 100644 index 109361b8..00000000 --- a/docs/.nuxtrc +++ /dev/null @@ -1 +0,0 @@ -imports.autoImport=true diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 00000000..a405b937 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,36 @@ +import { defineConfig } from 'vitepress' +import { routes as navRoutes } from './routes/navbar' +import { routes as sidebarRoutes } from './routes/sidebar' + +export default defineConfig({ + title: 'NuxtAuth', + titleTemplate: ':title - by sidebase', + description: 'Authentication for Nuxt 3', + base: '/', + cleanUrls: true, + lang: 'en-US', + appearance: 'dark', + lastUpdated: true, + head: [['link', { rel: 'icon', href: '/favicon.ico' }]], + themeConfig: { + logo: '/lock.png', + nav: navRoutes, + sidebar: sidebarRoutes, + socialLinks: [ + { icon: 'github', link: 'https://github.com/sidebase/nuxt-auth' }, + { icon: 'x', link: 'https://twitter.com/sidebase_io' }, + { icon: 'discord', link: 'https://discord.gg/VzABbVsqAc' }, + ], + footer: { + message: 'Released under the MIT License.', + copyright: 'Developed by SIDESTREAM', + }, + search: { + provider: 'local', + }, + editLink: { + pattern: 'https://github.com/sidebase/nuxt-auth/tree/main/docs/:path', + text: 'Edit this page on GitHub' + } + }, +}) diff --git a/docs/.vitepress/routes/navbar.ts b/docs/.vitepress/routes/navbar.ts new file mode 100644 index 00000000..3d88aeb0 --- /dev/null +++ b/docs/.vitepress/routes/navbar.ts @@ -0,0 +1,53 @@ +import type { DefaultTheme } from 'vitepress' + +export const routes: DefaultTheme.Config['nav'] = [ + { + text: 'Docs', + items: [ + { + text: 'Getting started', + link: '/guide/getting-started/introduction', + }, + { + text: 'AuthJS guide', + link: '/guide/authjs/quick-start', + }, + { + text: 'Local / Refresh guide', + link: '/guide/local/quick-start', + }, + ], + }, + // TODO: Add full API docs + // { text: 'API', link: '/api/overview' }, + { + text: 'Resources', + items: [ + { + text: 'Overview', + link: '/resources/overview', + }, + { + text: 'Security', + link: '/resources/security', + }, + { + text: 'Error references', + link: '/resources/error-reference', + }, + ], + }, + { + text: '0.8.0', + items: [ + { + text: '0.7.2', + link: 'https://github.com/sidebase/nuxt-auth/tree/0.7.2/docs/content', + }, + { + text: '0.6.7', + link: 'https://github.com/sidebase/nuxt-auth/tree/0.6.7/docs/content', + }, + ], + }, +] diff --git a/docs/.vitepress/routes/sidebar.ts b/docs/.vitepress/routes/sidebar.ts new file mode 100644 index 00000000..02762323 --- /dev/null +++ b/docs/.vitepress/routes/sidebar.ts @@ -0,0 +1,103 @@ +import type { DefaultTheme } from 'vitepress' + +export const routes: DefaultTheme.Config['sidebar'] = { + '/guide': [ + { + text: 'Getting started', + base: '/guide/getting-started', + items: [ + { + text: 'Introduction', + link: '/introduction', + }, + { + text: 'Installation', + link: '/installation', + }, + { + text: 'Choosing the provider', + link: '/choose-provider', + }, + ], + }, + { + text: 'Application side', + base: '/guide/application-side', + items: [ + { + text: 'Configuration', + link: '/configuration', + }, + { + text: 'Session access', + link: '/session-access', + }, + { + text: 'Protecting pages', + link: '/protecting-pages', + }, + ], + }, + { + text: 'AuthJS Provider', + base: '/guide/authjs', + items: [ + { + text: 'Quick Start', + link: '/quick-start', + }, + { + text: 'NuxtAuthHandler', + link: '/nuxt-auth-handler', + }, + { + text: 'Custom pages', + link: '/custom-pages', + }, + { + text: 'Session data', + link: '/session-data', + }, + { + text: 'Server side', + collapsed: true, + items: [ + { text: 'Session access', link: '/server-side/session-access' }, + { text: 'JWT access', link: '/server-side/jwt-access' }, + { text: 'Rest API', link: '/server-side/rest-api' }, + ], + }, + ], + }, + { + text: 'Local / Refresh Provider', + base: '/guide/local', + items: [ + { + text: 'Quick Start', + link: '/quick-start', + }, + { + text: 'Session data', + link: '/session-data', + } + ], + }, + { + text: 'Advanced', + base: '/guide/advanced', + items: [ + { + text: 'Deployment', + collapsed: true, + items: [ + { text: 'Self-hosted', link: '/deployment/self-hosted' }, + { text: 'Vercel', link: '/deployment/vercel' }, + { text: 'Netlify', link: '/deployment/netlify' }, + ], + }, + { text: 'Caching', link: '/caching' }, + ], + }, + ], +} diff --git a/docs/.vitepress/theme/components/Banner.vue b/docs/.vitepress/theme/components/Banner.vue new file mode 100644 index 00000000..f05e310a --- /dev/null +++ b/docs/.vitepress/theme/components/Banner.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/docs/.vitepress/theme/components/GithubStarsButton.vue b/docs/.vitepress/theme/components/GithubStarsButton.vue new file mode 100644 index 00000000..3fd921fb --- /dev/null +++ b/docs/.vitepress/theme/components/GithubStarsButton.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/docs/.vitepress/theme/components/StarIcon.vue b/docs/.vitepress/theme/components/StarIcon.vue new file mode 100644 index 00000000..759a3a78 --- /dev/null +++ b/docs/.vitepress/theme/components/StarIcon.vue @@ -0,0 +1,5 @@ + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 00000000..960e5068 --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,32 @@ +// https://vitepress.dev/guide/custom-theme +import { h } from 'vue' +import type { Theme } from 'vitepress' +import DefaultTheme from 'vitepress/theme' + +// Styles +import './style.css' + +// Custom Components +import GithubStarsButton from './components/GithubStarsButton.vue' +import Banner from './components/Banner.vue' + +// Configuration +const bannerConfig = { + // Leave text empty to disable the banner + text: '✨ NuxtAuth v0.8.0 has been released! ✨', + button: { + href: 'https://github.com/sidebase/nuxt-auth/releases/tag/0.8.0', + text: 'View release notes' + } +} + +export default { + extends: DefaultTheme, + Layout: () => { + return h(DefaultTheme.Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + 'nav-bar-content-after': h(GithubStarsButton, { owner: 'sidebase', repo: 'nuxt-auth' }), + 'home-hero-before': h(Banner, bannerConfig) + }) + }, +} satisfies Theme diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 00000000..7a4d1d89 --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,167 @@ +/** + * Customize default theme styling by overriding CSS variables: + * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css + */ + +/** + * Colors + * + * Each colors have exact same color scale system with 3 levels of solid + * colors with different brightness, and 1 soft color. + * + * - `XXX-1`: The most solid color used mainly for colored text. It must + * satisfy the contrast ratio against when used on top of `XXX-soft`. + * + * - `XXX-2`: The color used mainly for hover state of the button. + * + * - `XXX-3`: The color for solid background, such as bg color of the button. + * It must satisfy the contrast ratio with pure white (#ffffff) text on + * top of it. + * + * - `XXX-soft`: The color used for subtle background such as custom container + * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors + * on top of it. + * + * The soft color must be semi transparent alpha channel. This is crucial + * because it allows adding multiple "soft" colors on top of each other + * to create a accent, such as when having inline code block inside + * custom containers. + * + * - `default`: The color used purely for subtle indication without any + * special meanings attched to it such as bg color for menu hover state. + * + * - `brand`: Used for primary brand colors, such as link text, button with + * brand theme, etc. + * + * - `tip`: Used to indicate useful information. The default theme uses the + * brand color for this by default. + * + * - `warning`: Used to indicate warning to the users. Used in custom + * container, badges, etc. + * + * - `danger`: Used to show error, or dangerous message to the users. Used + * in custom container, badges, etc. + * -------------------------------------------------------------------------- */ + + :root { + --vp-c-default-1: var(--vp-c-gray-1); + --vp-c-default-2: var(--vp-c-gray-2); + --vp-c-default-3: var(--vp-c-gray-3); + --vp-c-default-soft: var(--vp-c-gray-soft); + + --vp-c-brand-1: var(--vp-c-green-1); + --vp-c-brand-2: var(--vp-c-green-2); + --vp-c-brand-3: var(--vp-c-green-3); + --vp-c-brand-soft: var(--vp-c-green-soft); + + --vp-c-tip-1: var(--vp-c-brand-1); + --vp-c-tip-2: var(--vp-c-brand-2); + --vp-c-tip-3: var(--vp-c-brand-3); + --vp-c-tip-soft: var(--vp-c-brand-soft); + + --vp-c-warning-1: var(--vp-c-yellow-1); + --vp-c-warning-2: var(--vp-c-yellow-2); + --vp-c-warning-3: var(--vp-c-yellow-3); + --vp-c-warning-soft: var(--vp-c-yellow-soft); + + --vp-c-danger-1: var(--vp-c-red-1); + --vp-c-danger-2: var(--vp-c-red-2); + --vp-c-danger-3: var(--vp-c-red-3); + --vp-c-danger-soft: var(--vp-c-red-soft); +} + +/** + * Colors: Background + * + * - `bg`: The bg color used for main screen. + * + * - `bg-alt`: The alternative bg color used in places such as "sidebar", + * or "code block". + * + * - `bg-elv`: The elevated bg color. This is used at parts where it "floats", + * such as "dialog". + * + * - `bg-soft`: The bg color to slightly distinguish some components from + * the page. Used for things like "carbon ads" or "table". + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-bg: #ffffff; + --vp-c-bg-alt: #f6f6f7; + --vp-c-bg-elv: #ffffff; + --vp-c-bg-soft: #f6f6f7; +} + +.dark { + --vp-c-bg: #0C0C0D; + --vp-c-bg-alt: #161618; + --vp-c-bg-elv: #202127; + --vp-c-bg-soft: #202127; +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-border: transparent; + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand-3); + --vp-button-brand-hover-border: transparent; + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-2); + --vp-button-brand-active-border: transparent; + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: var(--vp-c-brand-1); +} + +/** + * Component: Home + * -------------------------------------------------------------------------- */ + +:root { + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #42B883 30%, + #82f4b1 + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #42B883 50%, + #0F8674 50% + ); + --vp-home-hero-image-filter: blur(44px); +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(68px); + } +} + +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-tip-border: transparent; + --vp-custom-block-tip-text: var(--vp-c-text-1); + --vp-custom-block-tip-bg: var(--vp-c-brand-soft); + --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); +} + +/** + * Component: Algolia + * -------------------------------------------------------------------------- */ + +.DocSearch { + --docsearch-primary-color: var(--vp-c-brand-1) !important; +} diff --git a/docs/README.md b/docs/README.md deleted file mode 100755 index d71f46c4..00000000 --- a/docs/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Docus Starter - -Starter template for [Docus](https://docus.dev). - -## Clone - -Clone the repository (using `nuxi`): - -```bash -npx nuxi init docs -t nuxt-themes/docus-starter -``` - -## Setup - -Install dependencies: - -```bash -npm install -``` - -## Development - -```bash -npm dev -``` - -## Edge Side Rendering - -Can be deployed to Vercel Functions, Netlify Functions, AWS, and most Node-compatible environments. - -Look at all the available presets [here](https://nuxt.com/docs/getting-started/deployment#presets). - -```bash -npm build -``` - -## Static Generation - -Use the `generate` command to build your application. - -The HTML files will be generated in the .output/public directory and ready to be deployed to any static compatible hosting. - -```bash -npm generate -``` - -## Preview build - -You might want to preview the result of your build locally, to do so, run the following command: - -```bash -npm preview -``` - ---- - -For a detailed explanation of how things work, check out [Docus](https://docus.dev). diff --git a/docs/api/overview.md b/docs/api/overview.md new file mode 100644 index 00000000..a195a28a --- /dev/null +++ b/docs/api/overview.md @@ -0,0 +1,3 @@ +# Coming soon + +Oops, this page is not ready yet. You can find our current docs [here](https://sidebase.io). diff --git a/docs/app.config.ts b/docs/app.config.ts deleted file mode 100644 index 39395b53..00000000 --- a/docs/app.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default defineAppConfig({ - // @ts-ignore - docus: { - title: 'nuxt-auth' - } -}) diff --git a/docs/content/1.getting-started/1.index.md b/docs/content/1.getting-started/1.index.md deleted file mode 100644 index 7e54c277..00000000 --- a/docs/content/1.getting-started/1.index.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -description: "Introduction to `nuxt-auth` and its features as an authentication module for your Vue / Nuxt 3 application: authentication for static- and non-static Nuxt 3 applications." ---- - -# Introduction - -::alert{type="info"} -These are the docs for the new v0.6 version of `nuxt-auth` with static Nuxt 3 and `local` provider support. [Checkout the `0.5`-docs](/nuxt-auth/getting-started) for the nuxt-auth-version <=0.5. -:: - -`nuxt-auth` is an open source Nuxt module that provides authentication for Nuxt 3 applications. - -`nuxt-auth` supports the following: -::list{type="success"} -- `useAuth` composable to perform authentication-related actions from the application -- global- and local-application middleware to protect application-pages -- guest-mode to only allows guest to see certain pages (e.g.: the login page) -- authentication-backend setup for the `authjs`-auth-provider -- static Nuxt 3 apps with the `local`-auth-provider -- documentation, recipes and example code to get you started -:: - -`nuxt-auth` employs 3 providers to facilitate the act of authenticating a user: -::list{type="success"} -- `local`: Username and password authentication. `local` expects the endpoint to return a token that can be used to authenticate subsequent requests -- `refresh`: A extended version of the `local` provider, made for systems that require a token refresh. -- `authjs`: A `authjs` (`next-auth`) based provider that supports most OAuth- and Magic-URL sign-ins (think Slack or Notion). This provider also supports username and password based sign-in, but discourages from using it -:: - -Other libraries / modules would call these authentication-schemes, backends or maybe even strategys. - -::callout -#summary -Show me the code! - -#content -Visit the [quick start](/nuxt-auth/v0.6/getting-started/quick-start) page to see code examples. - -Checkout the example `nuxt-auth` app: https://nuxt-auth-example.sidebase.io/ - -Here's the source-code https://github.com/sidebase/nuxt-auth-example of the example app. -:: - -## Which Provider should I pick? - -To pick a provider you will first have to take into consideration the requirements of your use-case. Below is a small table to help you pick: - -| | authjs | local | refresh -|----------------------------------------------------------- |-------------------------------------: |------: | ------: -| **Authentication Methods** | | | -| OAuth | ✅ (>50 providers) | ❌ | ❌ -| Magic URLs | ✅ | ❌ | ❌ -| Credentials / Username + Password flow | 🚧 (if possible: use `local` instead) | ✅ | ✅ -| Refresh tokens | ✅ | ❌ | ✅ -| | | | -| **Features** | | | -| app `useAuth`-composable to sign-in, sign-out, ... | ✅ | ✅ | ✅ -| session-management: auto-refresh, refresh on refocus, ... | ✅ | ✅ | ✅ -| static apps ("nuxi generate") | ❌ | ✅ | ✅ -| guest mode | ✅ | ✅ | ✅ -| app-side middleware | ✅ | ✅ | ✅ -| server-side middleware | ✅ | ✅ | ✅ -| pre-made login-page | ✅ (impacts bundle-size) | ❌ | ❌ -| database-adapters, server-side callback-hooks | ✅ | ❌ | ❌ - -In general one can say that picking: -- `authjs` is best suited for plug-and-play OAuth for established oauth-providers or magic-url based sign-ins -- `local` is best when you already have a backend that accepts username + password as a login or want to build a static application -- `refresh` if you would need to extend the functionality of the `local` provider, with a refresh token - -### `authjs` Remarks - -The `authjs` provider is able to provide all of its features by wrapping [Auth.js / NextAuth.js](https://github.com/nextauthjs/next-auth) under the hood. This gives the reliability & convenience of a >12.000 github star library to the Nuxt 3 ecosystem with a native nuxt developer experience (DX). Wrapping `Auth.js / NextAuth.js` has the second advantage that many OAuth providers, database adapters, callbacks and more are supported out-of-the-box. This also means that you can use all NextAuth.js and Auth.js guides and documentation to achieve things with the `authjs` provider of `nuxt-auth`. - -`nuxt-auth` also provides Nuxt 3 specific features like a convenient application-side composable to login, logout, access user-authentication data or an authentication middleware and plugin that take care of managing the user authentication lifecycle by fetching authentication data on initial load, refreshing the user authentication on re-focusing the tab and more. - -In the future a separate OAuth provider for static apps may be added, read the next section for more on this. - -#### Auth.js vs next-auth - -We use `authjs` everywhere to mean `authjs` and `next-auth` interchangably as `next-auth` is currently transitioning to become `authjs` (branded name: `Auth.js`), see [the announcement here](https://twitter.com/balazsorban44/status/1603082914362986496). - -We are following this transition and are changing code related to this as it becomes stable enough to use it. You can follow our implementation of this transition [in this issue](https://github.com/sidebase/nuxt-auth/v0.6/issues/117). If you are googling anything related to this provider, we recommend that you still use the term `next-auth` as this is still the mainly used library and the stable one we mostly use under the hood. New features that are `Auth.js` only are _not_ guaranteed to work at the moment, as we still mostly rely on next-auth as a stable foundation. - -### `local` Remarks - -The `local` provider is based on [the local schema of the Nuxt 2 nuxt-auth module](https://auth.nuxtjs.org/schemes/local). It was added in v0.6.0 of `nuxt-auth` and serves as a starting point to add further static providers like [cookie based](https://auth.nuxtjs.org/schemes/cookie) authentication, support for [refresh tokens](https://auth.nuxtjs.org/schemes/refresh) and [static OAuth support](https://auth.nuxtjs.org/schemes/oauth2). - -The `local` provider works by receiving an authentication token from the backend it send a `username` and `password` to. This could for example be a JWT token. The `local` provider then stores this token and makes it accessible to you for use. The `local` provider will also fetch user / session information from the backend once it has successfully logged in. - -Any help with this effort is very welcome! Feel free to open an issue or a PR to add support for further providers 💘. - -## Starting with sidebase - -The easiest way to get started with `nuxt-auth` is using [the sidebase Merino stack](/sidebase): -::code-group -```bash [npm] -npm create sidebase@latest -``` -```bash [pnpm] -pnpm create sidebase@latest -``` -```bash [yarn] -# Note: Due to a known problem with `yarn`, it is not possible to force yarn to always use `@latest`: https://github.com/yarnpkg/yarn/issues/6587 -yarn create sidebase -``` -:: -This will create a Nuxt 3 project with `nuxt-auth` already setup & working. - -## Feature Collection - -### Authentication providers - -::list{type="success"} -- OAuth (e.g., Github, Google, Twitter, Azure, ...) -- Custom OAuth (write it yourself) -- Credentials (password + username) -- Email Magic URLs -:: - -### Application Side Session Management - -::list{type="success"} -- composable `const { signIn, signOut, status, data, lastRefreshedAt, ... } = useAuth()` -- Auto-refresh the session periodically -- Auto-refresh the session on tab-refocus -- Efficient session fetching: One time on page load, afterwards for specific actions (e.g., on navigation) -- Full typescript support for all methods and properties -- Guest-Mode -:: - -### Application Protection - -::list{type="success"} -- Application-side middleware protection either for the full application or specific pages -- Server-side middleware and endpoint protection -:: - -### REST API (authjs only) - -::list{type="success"} -- `GET /signin`, -- `POST /signin/:provider`, -- `GET/POST /callback/:provider`, -- `GET /signout`, -- `POST /signout`, -- `GET /session`, -- `GET /csrf`, -- `GET /providers` -:: diff --git a/docs/content/1.getting-started/2.installation.md b/docs/content/1.getting-started/2.installation.md deleted file mode 100644 index 32fa300b..00000000 --- a/docs/content/1.getting-started/2.installation.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -description: "How to install nuxt-auth." ---- - -# Installation - -You can install `nuxt-auth` using `nuxi`: -```bash -npx nuxi@latest module add sidebase-auth -``` - -## Specifics: `authjs`-Provider - -If you want to use the `authjs` provider, you have to install `next-auth`. With all package managers except `npm` you must manually install the peer dependency alongside `nuxt-auth`: -::code-group -```bash [yarn] -yarn add next-auth@4.21.1 -``` -```bash [pnpm] -pnpm i next-auth@4.21.1 -``` -:: - -::alert{type="warning"} -Due to a breaking change in NextAuth, nuxt-auth is only compoatible with NextAuth versions under v4.23.0. We recommend pinning the version to `4.22.5`. See more [here](https://github.com/sidebase/nuxt-auth/issues/514). -:: - -::alert{type="info"} -Note that we try our best to keep `nuxt-auth` stable, but it is also a fresh module that is in active development. If you want to be extra sure nothing breaks, you should pin the patch version, e.g., by using `--save-exact` when running the install command. -:: - -You can find all available `next-auth` versions [on npm](https://www.npmjs.com/package/next-auth?activeTab=versions). You do not need to install any other peer-dependencies in order to use `nuxt-auth`. - -If you are unsure which provider to choose, have a look at the [overview on the getting-started page](/nuxt-auth/v0.6/getting-started#which-provider-should-i-pick). - -## Specifics: `local`/`refresh`-Provider - -The `local` provider does not have any specific extra dependencies. However, you will need to make sure that you have a backend somewhere that provides username + password based authentication, [read more about this on the quick-start page](/nuxt-auth/v0.6/getting-started/quick-start). - -If you are unsure which provider to choose, have a look at the [overview on the getting-started page](/nuxt-auth/v0.6/getting-started#which-provider-should-i-pick). - -## Requirements - -`nuxt-auth` only needs Nuxt 3 to run. In the future Nuxt 2 or Nuxt Bridge may be supported. diff --git a/docs/content/1.getting-started/3.quick-start.md b/docs/content/1.getting-started/3.quick-start.md deleted file mode 100644 index bba3f580..00000000 --- a/docs/content/1.getting-started/3.quick-start.md +++ /dev/null @@ -1,245 +0,0 @@ -# Quick Start - -After [following the installation-steps](/nuxt-auth/v0.6/getting-started/installation), add `@sidebase/nuxt-auth` to your `nuxt.config.ts` and specify the provider-type you want to use: -::code-group -```ts [authjs] -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - provider: { - type: 'authjs' - } - } -}) -``` -```ts [local] -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - provider: { - type: 'local' - } - } -}) -``` -```ts [refresh] -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - provider: { - type: 'refresh' - } - } -}) -``` -:: - -Then continue with the provider-specific steps below. - -## Provider-specific Steps - -### Provider: `authjs` - -After the `nuxt.config.ts` setup from above you have to create the authentication handler (`NuxtAuthHandler`) that will setup the backend and expose the API endpoints for handling all authentication-related requests and add at least one [authentication provider](https://next-auth.js.org/providers/): - -```ts -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' -import GithubProvider from 'next-auth/providers/github' - -export default NuxtAuthHandler({ - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - GithubProvider.default({ - clientId: 'enter-your-client-id-here', - clientSecret: 'enter-your-client-secret-here' - }) - ] -}) -``` - -### Provider: `local` - -The local provider does not require any additional steps, as it relies on an already existing backend. By default, the `local` provider will try to reach this backend using the following default-configuration. - -```ts -{ - auth: { - baseURL: '/api/auth', - provider: { - type: 'local', - endpoints: { - signIn: { path: '/login', method: 'post' }, - signOut: { path: '/logout', method: 'post' }, - signUp: { path: '/register', method: 'post' }, - getSession: { path: '/session', method: 'get' } - }, - token: { signInResponseTokenPointer: '/token/accessToken' }, - } - } -} -``` - -You can customize each endpoint to fit your needs or disable it by setting it to `false`. For example you may want to disable the `getSession` endpoint. -```ts -{ - auth: { - baseURL: '/api/auth', - provider: { - type: 'local', - endpoints: { - getSession: false - } - } - } -} -``` - -::alert{type="info"} -See [configuration](/nuxt-auth/configuration/nuxt-config) for all options. -:: - -#### Sign In Example - -To perform a sign in, all you need to do is calling the `signIn` function from `useAuth` composable and pass the credentials expected by your backend. - -::alert{type="warning"} -Note that `signIn` method will be successful only if a valid token in the path specified by `auth.provider.token` in the config is returned in the API response. Otherwise it will throw an error. -:: - -Take this example where we're using the default options and require the user to enter his email, phone, and password to login. - -```vue - - - -``` - -If the backend response includes a token in the path `token.accessToken` (specified above in the config), the user will be signed successfully. - -```ts -// Backend response -{ - token: { - accessToken: 'eyBlaBlub' - }, -} -``` - -#### Full Example with `local` Provider - -Have a look at this example in the `nuxt-auth` repository which uses Nuxt in the backend too. -- [full nuxt app](https://github.com/sidebase/nuxt-auth/tree/main/playground-local) - - its [backend](https://github.com/sidebase/nuxt-auth/tree/main/playground-local/server/api/auth) - - its [`nuxt.config.ts`](https://github.com/sidebase/nuxt-auth/blob/main/playground-local/nuxt.config.ts) - -::alert{type="info"} -The linked example-implementation only serves as a starting-point and is not considered to be secure. -:: - -### Provider: `refresh` -::alert{type="info"} -The refresh provider is only available in version `0.6.3` and later -:: - -The refresh provider is exactly the same as the [local](#provider-local) provider except it has an additional `refresh` endpoint and a `refreshToken` object. - -```ts -{ - auth: { - baseURL: '/api/auth', - provider: { - type: 'refresh', - endpoints: { - signIn: { path: '/login', method: 'post' }, - signOut: { path: '/logout', method: 'post' }, - signUp: { path: '/register', method: 'post' }, - getSession: { path: '/session', method: 'get' }, - refresh: { path: '/refresh', method: 'post' } - }, - token: { signInResponseTokenPointer: '/token' }, - refreshToken: { signInResponseRefreshTokenPointer: '/refreshToken' }, - } - } -} -``` - -On a successful sign in with the default options, the value of `refreshToken` in the backend response will be saved and will be passed the body payload to the `/api/auth/refresh` endpoint when you call the `useAuth().refresh` method. - -#### Full Example with `refresh` Provider - -Have a look at this example in the `nuxt-auth` repository which uses Nuxt in the backend too. -- [full nuxt app](https://github.com/sidebase/nuxt-auth/tree/main/playground-refresh) - - its [backend](https://github.com/sidebase/nuxt-auth/tree/main/playground-refresh/server/api/auth) - - its [`nuxt.config.ts`](https://github.com/sidebase/nuxt-auth/blob/main/playground-refresh/nuxt.config.ts) - -::alert{type="info"} -The linked example-implementation only serves as a starting-point and is not considered to be secure. -:: - - -## Finishing up - -That's it! You can now use all user-related functionality, for example: - -::code-group -```ts [Application side] -// file: e.g ~/pages/login.vue -const { status, data, signIn, signOut, refresh } = useAuth() - -status.value // Session status: `unauthenticated`, `loading`, `authenticated` -data.value // Session data, e.g., expiration, user.email, ... - -await signIn() // Sign in the user -await refresh() // Refresh the token -await signOut() // Sign out the user - -``` -```ts [authjs: Server side] -// file: e.g: ~/server/api/session.get.ts -import { getServerSession } from '#auth' - -export default eventHandler(async (event) => { - const session = await getServerSession(event) - if (!session) { - return { status: 'unauthenticated!' } - } - return { status: 'authenticated!', text: 'im protected by an in-endpoint check', session } -}) -``` -:: - -To learn how to protect pages read [about the application-side usage](/nuxt-auth/v0.6/application-side), to learn how to protect server-routes and API endpoints read [about the server-side usage](/nuxt-auth/v0.6/server-side). You can also find more provider-specific information on these pages. diff --git a/docs/content/1.getting-started/5.getting-help.md b/docs/content/1.getting-started/5.getting-help.md deleted file mode 100644 index 62d19984..00000000 --- a/docs/content/1.getting-started/5.getting-help.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: "How to get help when using `nuxt-auth` in your Vue / Nuxt 3 application." ---- - -# Getting Help - -At some point, you may find that there's an issue you need some help with. - -But don't worry! We're a friendly community of developers and we'd love to help. Concretely this means to: -- Checkout the docs (page that you are currently viewing), -- Search open & resolved issues and discussions: https://github.com/sidebase/nuxt-auth/v0.6/issues -- Hop on Discord to ask us directly: https://discord.gg/VzABbVsqAc, -- Open an issue to file a bug, ask for an enhancement or get an answer to a question: https://github.com/sidebase/nuxt-auth/issues/new/choose - -We aim to follow the getting-help standards of the nuxt-project as described here and ask you to do the same when opening an issue or pinging us for help: https://nuxt.com/docs/community/getting-help#getting-help. - -## Minimal Reproductions - -When resolving a bug a minimal reproduction is almost always required, [read on the official nuxt.com page how to create one](https://nuxt.com/docs/community/reporting-bugs#create-a-minimal-reproduction). - -Note that your reproduction should also work outside of providers like stackblitz. The reason for this is that stackblitz (and likely other, similar providers) do not properly support cookies, which are essential for `nuxt-auth` to work. diff --git a/docs/content/1.getting-started/_dir.yml b/docs/content/1.getting-started/_dir.yml deleted file mode 100644 index 76238cf5..00000000 --- a/docs/content/1.getting-started/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Getting Started -icon: heroicons-outline:sparkles diff --git a/docs/content/1.index.md b/docs/content/1.index.md deleted file mode 100644 index 38e68f01..00000000 --- a/docs/content/1.index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -navigation: false -redirect: /nuxt-auth/getting-started ---- diff --git a/docs/content/2.configuration/1.index.md b/docs/content/2.configuration/1.index.md deleted file mode 100644 index ce91ed75..00000000 --- a/docs/content/2.configuration/1.index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -description: "Overview of the configuration options of `nuxt-auth` for Vue / Nuxt 3." ---- - -# Overview - -Use the following places to configure how `nuxt-auth` behaves: -- `local`/`refresh` & `authjs`-provider: The [auth key in `nuxt.config.ts`](/nuxt-auth/v0.6/configuration/nuxt-config). Use it to configure the module itself, e.g., whether global page protection is enabled -- `authjs`-provider: The [NuxtAuthHandler](/nuxt-auth/v0.6/configuration/nuxt-auth-handler). Use it to configure the `authjs` authentication behavior, e.g., what authentication providers to use. - -See the detailed possible configuration options on the next pages. diff --git a/docs/content/2.configuration/2.nuxt-config.md b/docs/content/2.configuration/2.nuxt-config.md deleted file mode 100644 index 6d360379..00000000 --- a/docs/content/2.configuration/2.nuxt-config.md +++ /dev/null @@ -1,615 +0,0 @@ ---- -description: "Learn how to configure nuxt-auth inside of the nuxt.config.ts" -toc: true ---- - -# Module (nuxt.config.ts) - -Use the `auth`-key inside the `nuxt.config.ts` to configure the `nuxt-auth` module itself. The module config has a root-config and then sub-properties for different aspects of the module: -::code-group -```ts [root] -/** - * Configuration for the whole module. - */ -interface ModuleOptions { - /** - * Whether the module is enabled at all - */ - isEnabled?: boolean - /** - * Forces your server to send a "loading" authentication status on all requests, thus prompting the client to do a fetch. If your website has caching, this prevents the server from caching someone's authentication status. - * - * This effects the entire site, for route-specific rules, add `disableServerSideAuth` on `routeRules`. - * - * @default false - */ - disableServerSideAuth?: boolean; - /** - * Full url at which the app will run combined with the path to authentication. You can set this differently depending on your selected authentication-provider: - * - `authjs`: You must set the full URL, with origin and path in production. You can leave this empty in development - * - `local`: You can set a full URL, but can also leave this empty to fallback to the default value of `/api/auth` or set only the path. - * - * ### `authjs` - * - * `baseURL` can be `undefined` during development but _must_ be set to the combination of origin + path that points to your `NuxtAuthHandler` for production. The origin consists out of: - * - `scheme`: http / https - * - `host`: e.g., localhost, example.org, google.com - * - `port`: _empty_ (implies `:80` for http and `:443` for https), :3000, :8888 - * - * The path then is a string like `/path/to/auth/api/endpoint/root`. - * - * ### `local` - * - * Defaults to `/api/auth` for both development and production. Setting this is optional, if you set it you can set it to either: - * - just a path: Will lead to `nuxt-auth` using `baseURL` as a relative path appended to the origin you deploy to. Example: `/backend/auth` - * - an origin and a path: Will leav to `nuxt-auth` using `baseURL` as an absolute request path to perform requests to. Example: `https://example.com/auth` - * - * Note: If you point to a different origin than the one you deploy to you likely have to take care of CORS: Allowing cross origin requests. - * - * @example undefined - * @example http://localhost:3000 - * @example https://example.org/_auth - * @example https://my-cool-site.com/api/authentication - * @default http://localhost:3000/api/auth Default for `authjs` provider in development - * @default undefined Default for `authjs` in production, will result in an error - * @default /api/auth Default for `local` for both production and development - */ - baseURL?: string - /** - * Configuration of the authentication provider. Different providers are supported: - * - auth.js: OAuth focused provider for non-static Nuxt 3 applications - * - local: Provider for credentials & token based backends, e.g., written by yourself or provided by something like Laraval - * - * Find more about supported providers here: https://sidebase.io/nuxt-auth/v0.6/getting-started - * - */ - provider?: AuthProviders - /** - * Configuration of the application-side session. You can configure the following attributes: - * - enablePeriodically: Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists. Setting this to `true` will refresh the session every second. Setting this to `false` will turn off session refresh. Setting this to a number `X` will refresh the session every `X` milliseconds. - * - enableOnWindowFocus: Whether to refresh the session every time the browser window is refocused. - * - * @example { enablePeriodically: 5000 } - * @example { enableOnWindowFocus: false } - * @default { enablePeriodically: false, enableOnWindowFocus: true } - */ - sessionRefresh?: SessionRefreshConfig - /** - * Whether to add a global authentication middleware that protects all pages. Can be either `false` to disable, `true` to enabled - * or an object to enable and apply extended configuration. - * - * If you enable this, everything is going to be protected and you can selectively disable protection for some pages by specifying `definePageMeta({ auth: false })` - * If you disable this, everything is going to be public and you can selectively enable protection for some pages by specifying `definePageMeta({ auth: true })` - * - * Read more on this topic [in the page protection docs](https://sidebase.io/nuxt-auth/v0.6/application-side/protecting-pages#global-middleware). - * - * @example true - * @example { allow404WithoutAuth: true } - * @default false - */ - globalAppMiddleware?: GlobalMiddlewareOptions | boolean -} -``` -```ts [AuthProviders - authjs] -/** - * Configuration for the `authjs`-provider. - */ -type ProviderAuthjs = { - /** - * Uses the `authjs` provider to facilitate autnetication. Currently, two providers exclusive are supported: - * - `authjs`: `next-auth` / `auth.js` based OAuth, Magic URL, Credential provider for non-static applications - * - `local`: Username and password provider with support for static-applications - * - * Read more here: https://sidebase.io/nuxt-auth/v0.6/getting-started - */ - type: Extract - /** - * If set to `true`, `authjs` will use either the `x-forwarded-host` or `host` headers instead of `auth.baseURL`. - * - * Make sure that reading `x-forwarded-host` on your hosting platform can be trusted. - * - ⚠ **This is an advanced option.** Advanced options are passed the same way as basic options, - * but **may have complex implications** or side effects. - * You should **try to avoid using advanced options** unless you are very comfortable using them. - * @default false - */ - trustHost: boolean - /** - * Select the default-provider to use when `signIn` is called. Setting this here will also effect the global middleware behavior: E.g., when you set it to `github` and the user is unauthorized, they will be directly forwarded to the Github OAuth page instead of seeing the app-login page. - * - * @example "github" - * @default undefined - */ - defaultProvider: SupportedProviders | undefined - /** - * Whether to add a callbackUrl to sign in requests. Setting this to a string-value will result in that being used as the callbackUrl path. Setting this to `true` will result in the blocked original target path being chosen (if it can be determined). - */ - addDefaultCallbackUrl: boolean | string -} -``` -```ts [AuthProviders - local] -/** - * Configuration for the `local`-provider. - */ -type ProviderLocal = { - /** - * Uses the `local` provider to facilitate autnetication. Currently, two providers exclusive are supported: - * - `authjs`: `next-auth` / `auth.js` based OAuth, Magic URL, Credential provider for non-static applications - * - `local`: Username and password provider with support for static-applications - * - * Read more here: https://sidebase.io/nuxt-auth/v0.6/getting-started - */ - type: Extract - /** - * Endpoints to use for the different methods. `nuxt-auth` will use this and the root-level `baseURL` to create the final request. E.g.: - * - `baseURL=/api/auth`, `path=/login` will result in a request to `/api/auth/login` - * - `baseURL=http://localhost:5000/_authenticate`, `path=/sign-in` will result in a request to `http://localhost:5000/_authenticate/sign-in` - */ - endpoints?: { - /** - * What method and path to call to perform the sign-in. This endpoint must return a token that can be used to authenticate subsequent requests. - * - * @default { path: '/login', method: 'post' } - */ - signIn?: { path?: string, method?: RouterMethod }, - /** - * What method and path to call to perform the sign-out. Set to false to disable. - * - * @default { path: '/logout', method: 'post' } - */ - signOut?: { path?: string, method?: RouterMethod } | false, - /** - * What method and path to call to perform the sign-up. - * - * @default { path: '/register', method: 'post' } - */ - signUp?: { path?: string, method?: RouterMethod }, - /** - * What method and path to call to fetch user / session data from. `nuxt-auth` will send the token received upon sign-in as a header along this request to authenticate. - * - * Refer to the `token` configuration to configure how `nuxt-auth` uses the token in this request. By default it will be send as a bearer-authentication header like so: `Authentication: Bearer eyNDSNJDASNMDSA....` - * - * @default { path: '/session', method: 'get' } - * @example { path: '/user', method: 'get' } - */ - getSession?: { path?: string, method?: RouterMethod }, - }, - /** - * Pages that `nuxt-auth` needs to know the location off for redirects. - */ - pages?: { - /** - * Path of the login-page that the user should be redirected to, when they try to access a protected page without being logged in. This page will also not be blocked by the global middleware. - * - * @default '/login' - */ - login?: string - }, - /** - * Settings for the authentication-token that `nuxt-auth` receives from the `signIn` endpoint and that can be used to authenticate subsequent requests. - */ - token?: { - /** - * How to extract the authentication-token from the sign-in response. - * - * E.g., setting this to `/token/bearer` and returning an object like `{ token: { bearer: 'THE_AUTH_TOKEN' }, timestamp: '2023' }` from the `signIn` endpoint will - * result in `nuxt-auth` extracting and storing `THE_AUTH_TOKEN`. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default /token Access the `token` property of the sign-in response object - * @example / Access the root of the sign-in response object, useful when your endpoint returns a plain, non-object string as the token - */ - signInResponseTokenPointer?: string - /** - * Header type to be used in requests. This in combination with `headerName` is used to construct the final authentication-header `nuxt-auth` uses, e.g, for requests via `getSession`. - * - * @default Bearer - * @example Beer - */ - type?: string, - /** - * It refers to the name of the property when it is stored in a cookie. - * - * @default auth.token - * @example auth._token - */ - cookieName?: string, - /** - * Header name to be used in requests that need to be authenticated, e.g., to be used in the `getSession` request. - * - * @default Authorization - * @example Auth - */ - headerName?: string, - /** - * Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e., in the users' browser. - * - * Note: Your backend may reject / expire the token earlier / differently. - * - * @default 1800 - * @example 60 * 60 * 24 - */ - maxAgeInSeconds?: number, - /** - * The cookie sameSite policy. Can be used as a form of csrf forgery protection. If set to `strict`, the cookie will only be passed with requests to the same 'site'. Typically, this includes subdomains. So, a sameSite: strict cookie set by app.mysite.com will be passed to api.mysite.com, but not api.othersite.com. - * - * See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7 - * - * @default 'lax' - * @example 'strict' - */ - sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, - /** - * Whether to set the secure flag on the cookie. This is useful when the application is served over HTTPS. - * - * @default false - * @example true - */ - secureCookieAttribute?: boolean, - /** - * The cookie domain. - * See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.3 - * - * @default '' - * @example 'sidebase.io' - */ - cookieDomain?: string, - }, - /* - * Settings for the session-data that `nuxt-auth` receives from the `getSession` endpoint. - */ - session?: { - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - dataType?: SessionDataObject; - /** - * How to extract the session-data from the session response. - * - * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will - * storing the 'User' object typed as the type created via the 'dataType' prop. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default / Access the root of the session response object - * @example /data/user Access the `data/user` property of the session response object - */ - dataResponsePointer?: string; - } -} - -``` -```ts [AuthProviders - refresh] -/** - * Configuration for the `refresh`-provider. - */ -type ProviderRefresh = { - /** - * Uses the `refresh` provider to facilitate autnetication. Currently, two providers exclusive are supported: - * - `authjs`: `next-auth` / `auth.js` based OAuth, Magic URL, Credential provider for non-static applications - * - `local`: Username and password provider with support for static-applications - * - `refresh`: Username and password provider with support for static-applications with refresh token logic - * Read more here: https://sidebase.io/nuxt-auth/v0.6/getting-started - */ - type: Extract - /** - * Endpoints to use for the different methods. `nuxt-auth` will use this and the root-level `baseURL` to create the final request. E.g.: - * - `baseURL=/api/auth`, `path=/login` will result in a request to `/api/auth/login` - * - `baseURL=http://localhost:5000/_authenticate`, `path=/sign-in` will result in a request to `http://localhost:5000/_authenticate/sign-in` - */ - endpoints?: { - /** - * What method and path to call to perform the sign-in. This endpoint must return a token that can be used to authenticate subsequent requests. - * - * @default { path: '/login', method: 'post' } - */ - signIn?: { path?: string, method?: RouterMethod }, - /** - * What method and path to call to perform the sign-out. Set to false to disable. - * - * @default { path: '/logout', method: 'post' } - */ - signOut?: { path?: string, method?: RouterMethod } | false, - /** - * What method and path to call to perform the sign-up. - * - * @default { path: '/register', method: 'post' } - */ - signUp?: { path?: string, method?: RouterMethod }, - /** - * What method and path to call to fetch user / session data from. `nuxt-auth` will send the token received upon sign-in as a header along this request to authenticate. - * - * Refer to the `token` configuration to configure how `nuxt-auth` uses the token in this request. By default it will be send as a bearer-authentication header like so: `Authentication: Bearer eyNDSNJDASNMDSA....` - * - * @default { path: '/session', method: 'get' } - * @example { path: '/user', method: 'get' } - */ - getSession?: { path?: string, method?: RouterMethod }, - /** - * What method and path to call to perform the refresh. - * - * @default { path: '/refresh', method: 'post' } - */ - refresh?: { path?: string, method?: RouterMethod }, - }, - /** - * When refreshOnlyToken is set, only the token will be refreshed - * - * @default true - */ - refreshOnlyToken?: boolean; - /** - * Pages that `nuxt-auth` needs to know the location off for redirects. - */ - pages?: { - /** - * Path of the login-page that the user should be redirected to, when they try to access a protected page without being logged in. This page will also not be blocked by the global middleware. - * - * @default '/login' - */ - login?: string - }, - /** - * Settings for the authentication-token that `nuxt-auth` receives from the `signIn` endpoint and that can be used to authenticate subsequent requests. - */ - token?: { - /** - * How to extract the authentication-token from the sign-in response. - * - * E.g., setting this to `/token/bearer` and returning an object like `{ token: { bearer: 'THE_AUTH_TOKEN' }, timestamp: '2023' }` from the `signIn` endpoint will - * result in `nuxt-auth` extracting and storing `THE_AUTH_TOKEN`. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default /token Access the `token` property of the sign-in response object - * @example / Access the root of the sign-in response object, useful when your endpoint returns a plain, non-object string as the token - */ - signInResponseTokenPointer?: string - /** - * Header type to be used in requests. This in combination with `headerName` is used to construct the final authentication-header `nuxt-auth` uses, e.g, for requests via `getSession`. - * - * @default Bearer - * @example Beer - */ - type?: string, - /** - * It refers to the name of the property when it is stored in a cookie. - * - * @default auth.token - * @example auth._token - */ - cookieName?: string, - /** - * Header name to be used in requests that need to be authenticated, e.g., to be used in the `getSession` request. - * - * @default Authorization - * @example Auth - */ - headerName?: string, - /** - * Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e., in the users' browser. - * - * Note: Your backend may reject / expire the token earlier / differently. - * - * @default 1800 - * @example 60 * 60 * 24 - */ - maxAgeInSeconds?: number, - /** - * The cookie sameSite policy. Can be used as a form of csrf forgery protection. If set to `strict`, the cookie will only be passed with requests to the same 'site'. Typically, this includes subdomains. So, a sameSite: strict cookie set by app.mysite.com will be passed to api.mysite.com, but not api.othersite.com. - * - * See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7 - * - * @default 'lax' - * @example 'strict' - */ - sameSiteAttribute?: boolean | 'lax' | 'strict' | 'none' | undefined, - /** - * Whether to set the secure flag on the cookie. This is useful when the application is served over HTTPS. - * - * @default false - * @example true - */ - secureCookieAttribute?: boolean, - /** - * The cookie domain. - * See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.3 - * - * @default '' - * @example 'sidebase.io' - */ - cookieDomain?: string, - }, - /** - * Settings for the authentication-refreshToken that `nuxt-auth` receives from the `signIn` endpoint and that can be used to authenticate subsequent requests. - */ - refreshToken?: { - /** - * How to extract the authentication-refreshToken from the sign-in response. - * - * E.g., setting this to `/token/refreshToken` and returning an object like `{ token: { refreshToken: 'THE_REFRESH__TOKEN' }, timestamp: '2023' }` from the `signIn` endpoint will - * result in `nuxt-auth` extracting and storing `THE_REFRESH__TOKEN`. - * - * This follows the JSON Pointer standard, see its RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default '/refreshToken' Access the `refreshToken` property of the sign-in response object - * @example / Access the root of the sign-in response object, useful when your endpoint returns a plain, non-object string as the refreshToken - */ - signInResponseRefreshTokenPointer?: string, - /** - * How to do a fetch for the refresh token. - * - * This is especially useful when you have an external backend signing tokens. Refer to this issue to get more information: https://github.com/sidebase/nuxt-auth/issues/635. - * - * ### Example - * Setting this to `/refresh/token` would make Nuxt Auth send the `POST /api/auth/refresh` with the following BODY: `{ "refresh": { "token": "..." } } - * - * ### Notes - * This follows the JSON Pointer standard, see its RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default '/refreshToken' - */ - refreshRequestTokenPointer?: string; - /** - * It refers to the name of the property when it is stored in a cookie. - * - * @default auth.refresh-token - * @example auth._refresh-token - */ - cookieName?: string, - /** - * Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e., in the users' browser. - * - * Note: Your backend may reject / expire the refreshToken earlier / differently. - * - * @default 1800 - * @example 60 * 60 * 24 - */ - maxAgeInSeconds?: number, - /** - * The cookie domain. See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.3 - * - * @default '' - * @example sidebase.io - */ - cookieDomain?: string; - }, - /* - * Settings for the session-data that `nuxt-auth` receives from the `getSession` endpoint. - */ - session?: { - /** - * Define an interface for the session data object that `nuxt-auth` expects to receive from the `getSession` endpoint. - * - * @default { id: 'string | number' } - * @example { id: 'string', name: 'string', email: 'string' } - * @advanced_array_example { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account', subscriptions: "{ id: number, status: 'ACTIVE' | 'INACTIVE' }[]" } - */ - dataType?: SessionDataObject; - /** - * How to extract the session-data from the session response. - * - * E.g., setting this to `/data/user` and returning an object like `{ data: { user: { id:number, name: string } }, status: 'ok' }` from the `getSession` endpoint will - * storing the 'User' object typed as the type created via the 'dataType' prop. - * - * This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 - * - * @default / Access the root of the session response object - * @example /data/user Access the `data/user` property of the session response object - */ - dataResponsePointer?: string; - } -} -``` -```ts [SessionRefreshConfig] -/** - * Configuration for the application-side session. - */ -type SessionRefreshConfig = { - /** - * Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists. - * - * Setting this to `true` will refresh the session every second. - * Setting this to `false` will turn off session refresh. - * Setting this to a number `X` will refresh the session every `X` milliseconds. - * - * @example 1000 - * @default false - * - */ - enablePeriodically: number | boolean, - /** - * Whether to refresh the session every time the browser window is refocused. - * - * @example false - * @default true - */ - enableOnWindowFocus: boolean - /** - * A custom refresh handler to use. This can be used to implement custom session refresh logic. - * If not set, the default refresh handler will be used. - * - * @example ./src/runtime/utils/refreshHandler.ts - * @default undefined - */ - handler?: RefreshHandler; -} -``` -```ts [GlobalMiddlewareOptions] -/** - * Configuration for the global application-side authentication-middleware. - */ -interface GlobalMiddlewareOptions { - /** - * Whether to add a global authentication middleware that protects all pages. - * - * @example true - * @default false - */ - isEnabled: boolean - /** - * Whether to enforce authentication if the target-route does not exist. Per default the middleware redirects - * to Nuxts' default 404 page instead of forcing a sign-in if the target does not exist. This is to avoid a - * user-experience and developer-experience of having to sign-in only to see a 404 page afterwards. - * - * Note: Setting this to `false` this may lead to `vue-router` + node related warnings like: "Error [ERR_HTTP_HEADERS_SENT] ...", - * this may be related to https://github.com/nuxt/framework/issues/9438. - * - * @example false - * @default true - */ - allow404WithoutAuth?: boolean - /** - * Whether to automatically set the callback url to the page the user tried to visit when the middleware stopped them. This is useful to disable this when using the credentials provider, as it does not allow a `callbackUrl`. Setting this - * to a string-value will result in that being used as the callbackUrl path. Note: You also need to set the global `addDefaultCallbackUrl` setting to `false` if you want to fully disable this. - * - * @example false - * @example /i-caught-you-but-now-you-are-signed-in - * @default true - */ - addDefaultCallbackUrl?: boolean | string -} -``` -:: - -## Additional Information - -### Provider `authjs` - baseURL - -**You must set the `baseURL` in production for this provider!** You have two options to set it: -1. Set the `AUTH_ORIGIN` environment variable _at runtime_ -2. Set the `baseURL` baseURL-config key inside the `nuxt.config.ts` (see an example above) _at build-time_ - - Note: Setting this at build time will result in this being hard-backed on the application side if you do not use server-side-rendering - - Note: It's called `baseURL` because it must contain both `origin` and `path` that point to the auth handler, see the long docstring above - -It is advised to use the `AUTH_ORIGIN` environment variable if possible. The `AUTH_ORIGIN` environment variable takes precendence over the `baseURL` configuration key, so you can always overwrite the origin at deploy-time. - -The `baseURL` must be set so that `nuxt-auth` can ensure that callbacks for authentication are correct. Examples of a `baseURL` with both origin and path are: -- `https://example.org/api/auth` -- `http://localhost:2345/_auth` -- `https://app.company.com/api/auth` - -#### Explanation: What is an origin? - -The `origin` consists out of (up to) 3 parts: -- scheme: `http` or `https` -- host: e.g., `localhost`, `example.org`, `www.sidebase.io` -- port: e.g., `:3000`, `:4444`; leave empty to implicitly set `:80` for http, and `:443` for https (this is an internet convention, don't ask) - -For the demo-app at https://nuxt-auth-example.sidebase.io we set the `origin` to `https://nuxt-auth-example.sidebase.io`. If for some reason required, you can explicitly set the `origin` to `http://localhost:3000` to stop `nuxt-auth` from aborting `npm run build` when the origin is unset. - -#### Explanation: What is the path? - -This is what tells the module where you added the authentication endpoints. By default this documentation recommends to use `/api/auth`, so that means that the module expects that all requests to `/api/auth/*` will be handled by the `NuxtAuthHandler`. - -To statify this, you need to create a [catch-all server-route](https://nuxt.com/docs/guide/directory-structure/pages/#catch-all-route) at that location by creating a file `~/server/api/auth/[...].ts` that exports the `NuxtAuthHandler`, see more on this in the [Quick Start](/nuxt-auth/getting-started/quick-start) or in the [`NuxtAuthHandler` documentation](/nuxt-auth/configuration/nuxt-auth-handler) - -If you want to have the authentication at another location, you should change the path, e.g., when setting the path-part of the `baseURL` to: -- `path: "/api/_auth"` -> you need to add the authentication catch-all endpoints into `~/server/api/_auth/[...].ts` and this is where `nuxt-auth` will look -- `path: "/_auth"` -> you need to add the authentication catch-all endpoints into `~/server/routes/_auth/[...].ts` and this is where `nuxt-auth` will look - -See [Nuxt server-routes docs on catch-all routes for a further explanation.](https://nuxt.com/docs/guide/directory-structure/server#server-routes) diff --git a/docs/content/2.configuration/3.route-config.md b/docs/content/2.configuration/3.route-config.md deleted file mode 100644 index 6a1fb965..00000000 --- a/docs/content/2.configuration/3.route-config.md +++ /dev/null @@ -1,14 +0,0 @@ -# Route Rules - -Use the `auth`-key inside the `nuxt.config.ts` `routeRules` to configure page-specific settings. - -```ts -interface RouteOptions { - /** - * Forces your server to send a "loading" status on a route, prompting the client to fetch on the client. If a specific page has caching, this prevents the server from caching someone's authentication status. - * - * @default false - */ - disableServerSideAuth: boolean; -} -``` diff --git a/docs/content/2.configuration/4.nuxt-auth-handler.md b/docs/content/2.configuration/4.nuxt-auth-handler.md deleted file mode 100644 index 673d6d01..00000000 --- a/docs/content/2.configuration/4.nuxt-auth-handler.md +++ /dev/null @@ -1,137 +0,0 @@ ---- -description: "Learn how to configure the NuxtAuthHandler that handles all authentication requests on the server-side" ---- - -::alert{type="info"} -This page is only relevant to you, if you are using the `authjs`-provider. -:: - -# NuxtAuthHandler - -After following the [quick-start setup](/nuxt-auth/v0.6/getting-started/quick-start) and then optionally diving even deeper into the [`nuxt-auth` config inside your `nuxt.config.ts`](/nuxt-auth/v0.6/configuration/nuxt-config) you can begin defining providers and other options inside your `NuxtAuthHandler`. - -For development, using the quick-start setup will already bring you quite far. For a production deployment, you will have to additionally: -- set the `origin` by exporting the `AUTH_ORIGIN`-environment variable _at runtime_ -- set a secret inside the `NuxtAuthHandler` config - -## Creating the `NuxtAuthHandler` - -In order to create your own `NuxtAuthHandler`, create the file `~/server/api/auth/[...].ts`. This file will automatically set up the authentication API that responds to all requests going to `https://localhost/api/auth/*`. If you wish you can also use a custom file location and api path, however this change will need to be reflected in the [basePath](/nuxt-auth/v0.6/configuration/nuxt-config#basepath), which is configured in the [nuxt.config.ts](/nuxt-auth/v0.6/configuration/nuxt-config). - -::alert{type="warning"} -The filename must be `[...].ts` - this is a so-called "catch-all" route, read more [in the Nuxt catch-all docs](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route). -:: - -## Configuring the `NuxtAuthHandler` - -After creating the file, add the `NuxtAuthHandler({ ... })` to it. The `NuxtAuthHandler({ ... })` is used to configure how the authentication itself behaves, what [callbacks should be called](https://next-auth.js.org/configuration/callbacks), what [database adapters should be used](https://next-auth.js.org/adapters) and more: - -::code-group -```ts [Empty NuxtAuthHandler] -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // your authentication configuration here! -}) -``` -```ts [NuxtAuthHandler with Github Provider] -// file: ~/server/api/auth/[...].ts -import GithubProvider from 'next-auth/providers/github' -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - secret: 'your-secret-here', - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - GithubProvider.default({ - clientId: 'your-client-id', - clientSecret: 'your-client-secret' - }) - ] -}) -``` -```ts [NuxtAuthHandler with Credentials Provider] -// file: ~/server/api/auth/[...].ts -import CredentialsProvider from 'next-auth/providers/credentials' -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - secret: 'your-secret-here', - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - // The name to display on the sign in form (e.g. 'Sign in with...') - name: 'Credentials', - // The credentials is used to generate a suitable form on the sign in page. - // You can specify whatever fields you are expecting to be submitted. - // e.g. domain, username, password, 2FA token, etc. - // You can pass any HTML attribute to the tag through the object. - credentials: { - username: { label: 'Username', type: 'text', placeholder: '(hint: jsmith)' }, - password: { label: 'Password', type: 'password', placeholder: '(hint: hunter2)' } - }, - authorize (credentials: any) { - // You need to provide your own logic here that takes the credentials - // submitted and returns either a object representing a user or value - // that is false/null if the credentials are invalid. - // NOTE: THE BELOW LOGIC IS NOT SAFE OR PROPER FOR AUTHENTICATION! - - const user = { id: '1', name: 'J Smith', username: 'jsmith', password: 'hunter2' } - - if (credentials?.username === user.username && credentials?.password === user.password) { - // Any object returned will be saved in `user` property of the JWT - return user - } else { - // eslint-disable-next-line no-console - console.error('Warning: Malicious login attempt registered, bad credentials provided') - - // If you return null then an error will be displayed advising the user to check their details. - return null - - // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter - } - } - }) - ] -}) -``` -:: - -The `NuxtAuthHandler` accepts [all options that NextAuth.js accepts for its API initialization](https://next-auth.js.org/configuration/options#options). Use this place to configure authentication providers (oauth-Google, credential flow, ...), your `secret`, add callbacks for authentication events, configure a custom logger and more. Read the [`NextAuth.js` docs to see all possible options](https://next-auth.js.org/configuration/options#options). - -### secret - -In theory the only required configuration key for production is the `secret`. You can set it directly inside the `NuxtAuthHandler`. In practice however you may also want to configure at least one provider so that your users can actually login. - -To avoid hard-coding of the secret you can make it configurable at runtime by using an environment variable and exporting it at runtime or by using the nuxt runtimeconfig (and then also setting the correct environment at runtime): -::code-group -```ts [Environment Variable] -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - required in production - secret: process.env.AUTH_SECRET - - // rest of your authentication configuration here! -}) -``` -```ts [useRuntimeConfig] -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - required in production - secret: useRuntimeConfig().authSecret - - // rest of your authentication configuration here! -}) -``` -:: - -## Further Examples - -Checkout the [provider recipes](/nuxt-auth/v0.6/recipes) to see different examples of provider configurations, e.g., for Strapi or Directus. diff --git a/docs/content/2.configuration/5.session-config.md b/docs/content/2.configuration/5.session-config.md deleted file mode 100644 index 98a43be4..00000000 --- a/docs/content/2.configuration/5.session-config.md +++ /dev/null @@ -1,73 +0,0 @@ -# Session Config - -Use this section to configure the application-side session. The following type defines the options you can use inside your auths `session`-key: - -```ts -/** - * Configuration for the application-side session. - */ -type SessionRefreshConfig = { - /** - * Whether to refresh the session every `X` milliseconds. Set this to `false` to turn it off. The session will only be refreshed if a session already exists. - * - * Setting this to `true` will refresh the session every second. - * Setting this to `false` will turn off session refresh. - * Setting this to a number `X` will refresh the session every `X` milliseconds. - * - * @example 1000 - * @default false - * - */ - enablePeriodically: number | boolean - /** - * Whether to refresh the session every time the browser window is refocused. - * - * @example false - * @default true - */ - enableOnWindowFocus: boolean - /** - * A custom refresh handler to use. This can be used to implement custom session refresh logic. - * If not set, the default refresh handler will be used. - * - * @example ./src/runtime/utils/refreshHandler.ts - * @default undefined - */ - handler?: RefreshHandler; -} -``` - -## Application side session - -Per default nuxt-auth will use the set values for `enablePeriodically` & `enableOnWindowFocus` to refresh your application-side session. If you don't provide a configuration nuxt-auth won't trigger a job that refreshes the session periodically but it will always refresh the session if the window is refocussed. - -If you set `enablePeriodically` simply to true a job will be run every second (1000ms) that will fetch your specified `getSession` endpoint. You can customize the interval if you provide a number instead of a boolean value. - -To disable the session refresh when the window is refocussed simply set `enableOnWindowFocus` to `false`. - -## Using a custom RefreshHandler - -To customize the session refreshing you can provide a refresh handler. A custom `RefreshHandler` requires an `init`- and a `destroy`-function. - -`init` will be called when the nuxt application is mounted. Here you may add event listeners and initialize custom refresh behaviour. The method will receive a `RefreshHandlerConfig`. The type consists of `enablePeriodically` & `enableOnWindowFocus`. - -`destroy` will be called when your app is unmounted. Here you may run your clean up routine e.g. to remove your event listeners. - -```ts -export type RefreshHandler = { - /** - * Initializes the refresh handler with the given configuration. - * init will be called inside app:mouted lifecycle hook. - * - * @param config The configuration to use for the refresh handler. - */ - init: (config: RefreshHandlerConfig) => void; - - /** - * Handles cleanup of the refresh handler. This method will be called when the app is destroyed. - */ - destroy: () => void; -}; -``` - -To get an idea the `defaultRefreshHandler` could be useful. diff --git a/docs/content/2.configuration/_dir.yml b/docs/content/2.configuration/_dir.yml deleted file mode 100644 index 266566b7..00000000 --- a/docs/content/2.configuration/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Configuration -icon: heroicons-outline:cog diff --git a/docs/content/3.application-side/1.index.md b/docs/content/3.application-side/1.index.md deleted file mode 100644 index dc9ec9d4..00000000 --- a/docs/content/3.application-side/1.index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: "Application-side usage of `nuxt-auth` for Vue / Nuxt 3 apps." ---- - -# Overview - -On the application-side this module offers: -::list{type="success"} -- [`useAuth` composable for session access and management](/nuxt-auth/v0.6/application-side/session-access-and-management) -- [Creation of custom sign-in pages](/nuxt-auth/v0.6/application-side/custom-sign-in-page) -- [Middleware to protect your application on the application side](/nuxt-auth/v0.6/application-side/protecting-pages) -:: - -Application-side usage refers to any code like pages, components or composables that are part of the universal server- and client-side rendering of Nuxt, see more in the [glossary](/nuxt-auth/v0.6/resources/glossary). diff --git a/docs/content/3.application-side/2.session-access-and-management.md b/docs/content/3.application-side/2.session-access-and-management.md deleted file mode 100644 index aba38dc4..00000000 --- a/docs/content/3.application-side/2.session-access-and-management.md +++ /dev/null @@ -1,369 +0,0 @@ -# Session Access and Management - -## `useAuth` Composable - -The `useAuth` composable is your main gateway to accessing and manipulating session-state and data. Here's the main methods you can use: -::code-group -```ts [authjs] -const { - status, - data, - lastRefreshedAt, - getCsrfToken, - getProviders, - getSession, - signIn, - signOut, -} = useAuth() - -// Session status, either `unauthenticated`, `loading`, `authenticated` -status.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), `loading` (= session loading in progress), see https://next-auth.js.org/getting-started/client#signout -data.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value - -// Get / Reload the current session from the server, pass `{ required: true }` to force a login if no session exists -await getSession() - -// Get the current CSRF token, usually you do not need this function, see https://next-auth.js.org/getting-started/client#signout -await getCsrfToken() - -// Get the supported providers, e.g., to build your own login page, see https://next-auth.js.org/getting-started/client#getproviders -await getProviders() - -// Trigger a sign-in, see https://next-auth.js.org/getting-started/client#signin -await signIn() - -// Trigger a sign-in with a redirect afterwards, see https://next-auth.js.org/getting-started/client#signin -await signIn(undefined, { callbackUrl: '/protected' }) - -// Trigger a sign-in via a specific authentication provider with a redirect afterwards, see https://next-auth.js.org/getting-started/client#signin -await signIn('github', { callbackUrl: '/protected' }) - -// Trigger a sign-in with username and password already passed, e.g., from your own custom-made sign-in form -await signIn('credentials', { username: 'jsmith', password: 'hunter2' }) - -// Trigger a sign-out, see https://next-auth.js.org/getting-started/client#signout -await signOut() - -// Trigger a sign-out and send the user to the sign-out page afterwards -await signOut({ callbackUrl: '/signout' }) -``` -```ts [local] -const { - status, - data, - token, - lastRefreshedAt, - getSession, - signUp, - signIn, - signOut, -} = useAuth() - -// Session status, either `unauthenticated`, `loading`, `authenticated` -status.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns -data.value - -// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` -token.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value - -// Get / Reload the current session from the server, pass `{ required: true }` to force a login if no session exists -await getSession() - -// Trigger a sign-up, where `credentials` are the credentials your sign-up endpoint expects, e.g. `{ name:'Alice', email: 'alice@example.com', password: 'securepassword' }` -await signUp(credentials); - -// Trigger a sign-up with auto sign-in and redirect to the profile page within the application -await signUp(credentials, { callbackUrl: '/profile', redirect: true }); - -// Trigger a sign-up with auto sign-in and redirect to an external website (https://external.example.com) -await signUp(credentials, { callbackUrl: 'https://external.example.com', redirect: true, external: true }); - -// Trigger a sign-up without auto sign-in and redirect to the home page within the application -await signUp(credentials, { callbackUrl: '/', redirect: true }, { preventLoginFlow: true }); - -// Trigger a sign-up without auto sign-in and doesn't redirect anywhere -await signUp(credentials, undefined, { preventLoginFlow: true }); - -// Trigger a sign-in, where `credentials` are the credentials your sign-in endpoint expected, e.g. `{ username: 'bernd', password: 'hunter2' }` -await signIn(credentials) - -// Trigger a sign-in with a redirect afterwards -await signIn(credentials, { callbackUrl: '/protected' }) - -// Trigger a sign-in with a redirect afterwards to an external page (if set, this will cause a hard refresh of the page) -await signIn(credentials, { callbackUrl: 'https://sidebase.io', external: true }) - -// Trigger a sign-out -await signOut() - -// Trigger a sign-out and send the user to the sign-out page afterwards -await signOut({ callbackUrl: '/signout' }) -``` -```ts [refresh] -const { - status, - data, - token, - lastRefreshedAt, - getSession, - signUp, - signIn, - signOut, - refresh, - refreshToken -} = useAuth() - -// Session status, either `unauthenticated`, `loading`, `authenticated` -status.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns -data.value - -// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` -token.value - -// The fetched refreshToken that can be used to token . E.g., a refreshToken like so: `eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` -refreshToken.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value - -// Get / Reload the current session from the server, pass `{ required: true }` to force a login if no session exists -await getSession() - -// Trigger a sign-in, where `credentials` are the credentials your sign-in endpoint expected, e.g. `{ username: 'bernd', password: 'hunter2' }` -await signIn(credentials) - -// Trigger a sign-in with a redirect afterwards -await signIn(credentials, { callbackUrl: '/protected' }) - -// Trigger a sign-in with a redirect afterwards to an external page (if set, this will cause a hard refresh of the page) -await signIn(credentials, { callbackUrl: 'https://sidebase.io', external: true }) - -// Trigger a refresh, this will set token to new value -await refresh() - -// Trigger a sign-out -await signOut() - -// Trigger a sign-out and send the user to the sign-out page afterwards -await signOut({ callbackUrl: '/signout' }) -``` -:: - -## `SessionData` - -As described above you can use: -```ts -const { data } = useAuth() -``` - -to access the session-data of the currently logged in user. Depending on the provider you use, this data will be typed differently: -::code-group -```ts [authjs] -interface SessionData { - user?: { - name?: string | null; - email?: string | null; - image?: string | null; - }; - expires: ISODateString; -} -``` -```ts [local] -// Option A: No explicit configuration -inferface SessionData { - id: string | number -} - -// Option B: You configured `auth.provider.session.dataType` to something like ` { id: 'string', email: 'string', name: 'string', role: 'admin | guest | account' }` -inferface SessionData { - id: string - email: string - name: string - role: 'admin' | 'guest' | 'account' -} -``` -:: - -### About `auth.provider.session.dataType` - -This is a configuration option available to dynamically type the `SessionData` that the `local` provider will return when accessing `data.value`. Read more about this in the [nuxt.config.ts configuration documentation](/nuxt-auth/v0.6/configuration/nuxt-config) of the `local` provider. - -`nuxt-auth` uses [unjs/knitwork](https://github.com/unjs/knitwork) to generate the correct typescript interface from the type you provide. - -## Force refetching the session (`local` provider only) - -Calling `getSession` will by default **only** refetch the current session if the token returned by `useAuthState` is defined. -Passing the `{ force: true }` option will always update the current session: - -::code-group -```ts [local] -// force update the current session -await getSession({ force: true }) -``` -:: - -## Automatic session refreshing -To activate or deactivate a periodical session refresh or session refresh when refocusing the window, please consult the [nuxt.config.ts configuration documentation](/nuxt-auth/configuration/nuxt-config) of the `session` object. - -## Redirects - -You can also pass the `callbackUrl` option to both the `signIn`, the `signOut` and the `getSession` methods. This allows you to redirect a user to a certain pages, after they've completed the action. This can be useful when a user attempts to open a page (`/protected`) but has to go through external authentication (e.g., via their google account) first. - -You can use it like: -::code-group -```ts [authjs] -await signIn(undefined, { callbackUrl: '/protected' }) - -await signOut({ callbackUrl: '/protected' }) - -await getSession({ callbackUrl: '/protected' }) -``` -```ts [local] -const credentials = { username: 'bernd', password: 'hunter2' } -await signIn(credentials, { callbackUrl: '/protected' }) - -await signOut(credentials, { callbackUrl: '/protected' }) - -await getSession(credentials, { callbackUrl: '/protected' }) -``` -```ts [refresh] -const credentials = { username: 'bernd', password: 'hunter2' } -await signIn(credentials, { callbackUrl: '/protected' }) - -await signOut(credentials, { callbackUrl: '/protected' }) - -await getSession(credentials, { callbackUrl: '/protected' }) -``` -:: - -## `useAuthState` Composable - -The `useAuthState` composable is the underlying storage layer to access the session-state and data. Here's the main methods and properties you can use: - -::code-group -```ts [authjs] -const { - status, - loading, - data, - lastRefreshedAt -} = useAuthState() - -// Session status, either `unauthenticated`, `loading`, `authenticated` -status.value - -// Whether any http request is still pending -loading.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), `loading` (= session loading in progress), see https://next-auth.js.org/getting-started/client#signout -data.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value -``` -```ts [local] -const { - status, - loading, - data, - lastRefreshedAt, - token, - rawToken, - setToken, - clearToken -} = useAuthState() - -// Session status, either `unauthenticated`, `loading`, `authenticated` -status.value - -// Whether any http request is still pending -loading.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns -data.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value - -// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` -token.value - -// Cookie that containes the raw fetched token string. This token won't contain any modification or prefixes like `Bearer` or any other. -rawToken.value - -// Helper method to quickly set a new token (alias for rawToken.value = 'xxx') -setToken('new token') - -// Helper method to quickly delete the token cookie (alias for rawToken.value = null) -clearToken() -``` - -```ts [refresh] -const { - status, - loading, - data, - lastRefreshedAt, - token, - rawToken, - setToken, - clearToken, - rawRefreshToken, - refreshToken -} = useAuthState() - -// Session status, either `unauthenticated`, `loading`, `authenticated` -status.value - -// Whether any http request is still pending -loading.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns -data.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value - -// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` -token.value - -// The fetched refreshToken that can be used to refresh the Token with refresh() methode. -refreshToken.value - -// Cookie that containes the raw fetched token string. This token won't contain any modification or prefixes like `Bearer` or any other. -rawToken.value - -// Cookie that containes the raw fetched refreshToken string. -rawRefreshToken.value - -// Helper method to quickly set a new token (alias for rawToken.value = 'xxx') -setToken('new token') - -// Helper method to quickly delete the token and refresh Token cookie (alias for rawToken.value = null and rawRefreshToken.value = null) -clearToken() -``` -:: - -::alert{type="warning"} -Local provider: Note that you will have to manually call `getSession` from `useAuth` composable in order to refresh the new user state when using `setToken`, `clearToken` or manually updating `rawToken.value`: -:: - -```ts -const { getSession } = useAuth() -const { setToken } = useAuthState() -// ... -setToken('...') -await getSession() -``` diff --git a/docs/content/3.application-side/3.custom-sign-in-page.md b/docs/content/3.application-side/3.custom-sign-in-page.md deleted file mode 100644 index 8bfce01d..00000000 --- a/docs/content/3.application-side/3.custom-sign-in-page.md +++ /dev/null @@ -1,160 +0,0 @@ -# Custom sign-in Page - -## Provider: `authjs` - -To create a custom sign-in page you will need to: -1. Create the custom sign-in page: Creating the actual page your user will enter their credentials on OR select their oauth provider (e.g., google, azure, ...) -2. Configure the `authjs`-backend-auth-handler to redirect to the custom sign-in page: If a sign-in is triggered or a session check fails, the `authjs`-backend-auth-handler has to forward you to your custom sign-in page, instead of the the `authjs`-backend-auth-handler builtin sign-in page -3. Optional: Disable the `nuxt-auth` global protection middleware for the custom page if you have it enabled - -### Create the Custom sign-in Page - -To create your custom sign-in page you can use `signIn` to directly start a provider-flow once the user selected it, e.g., by clicking on a button on your custom sign-in page. Here is a very simple sign-in page that either directly starts a github-oauth sign-in flow or directly signs in the user via the credentials flow: -```vue - - - - -``` - -Note: -- In the above example `username` and `password` are hard-coded. In your own custom page, these two fields should probably come from inputs on your page. -- We disable the global `nuxt-auth` middleware for this page by using the [guest mode](/nuxt-auth/v0.6/application-side/guest-mode) `definePageMeta({ auth: { unauthenticatedOnly: true, ... } })`, this is only necessary if you have set `globalAppMiddleware: true` in [the `nuxt-auth` module configuration](/nuxt-auth/v0.6/configuration/nuxt-config) - -If you want to create a custom sign-in page that dynamically offers sign-in options based on your configured providers, you can call `getProviders()` first and then iterate over the supported providers to generate your sign-in page. - -::alert{type="info"} -Above we use the [guest mode](/nuxt-auth/v0.6/application-side/guest-mode). This option was introduced in `nuxt-auth@0.5.0`. If you are on a previous version, use `auth: false` instead. This has the downside of not automatically redirecting authenticated users away from the login page. -:: - -### Configure `authjs` to redirect to the Custom sign-in Page - -Redirects to the sign-in page happen automatically, e.g., when a `getSession()` call fails to get a session or when `signIn()` is called. By default this redirect sends the user to `/api/auth/signin`. To use a custom sign-in page we will have to configure `authjs` to send the user to the custom sign-in page instead. - -We can apply this configuration in the `NuxtAuthHandler`: -```ts -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - pages: { - // Change the default behavior to use `/login` as the path for the sign-in page - signIn: '/login' - }, - providers: [ - // ... your provider configuration - ] -}) -``` - -We can also configure the default-location for other pages in the `pages` configuration, see [the NextAuth.js pages docs for more](https://next-auth.js.org/configuration/pages). - -### Disable the global page protection middleware - -As already outlined in the first step, you will need to add: -```ts -definePageMeta({ - auth: { - unauthenticatedOnly: true, - navigateAuthenticatedTo: '/', - } -}) -``` -to your login page if you have enabled the [global page protection middleware](/nuxt-auth/v0.6/application-side/protecting-pages). This is the [guest mode](/nuxt-auth/v0.6/application-side/guest-mode) and allows unauthenticated users to access the login-page, while authenticated users will be redirected to `/`. Not disabling the global middleware would result in an endless loop of redirects. You can change `navigateAuthenticatedTo` to any route you like, e.g., `/profile` to show authenticated users their profile instead. - -If you have not set `globalAppMiddleware` or have set it to `false` this step does not apply to your application. - -::alert{type="info"} -Above we use the [guest mode](/nuxt-auth/v0.6/application-side/guest-mode). This option was introduced in `nuxt-auth@0.5.0`. If you are on a previous version, use `auth: false` instead. This has the downside of not automatically redirecting authenticated users away from the login page. -:: - -### Optional: Custom Error handling - -You can handle sign-in errors yourself by calling `signIn` with `redirect: false` and inspecting its result for errors. For example: -```ts -const { signIn } = useAuth() - -const mySignInHandler = async ({ username, password }: { username: string, password: string }) => { - const { error, url } = await signIn('credentials', { username, password, redirect: false }) - - if (error) { - // Do your custom error handling here - alert('You have made a terrible mistake while entering your credentials') - } else { - // No error, continue with the sign in, e.g., by following the returned redirect: - return navigateTo(url, { external: true }) - } -} -``` - -Then call the `mySignInHandler({ username, password })` on login instead of the default `signIn(...)` method. You can find [all possible errors here](https://github.com/nextauthjs/next-auth/blob/aad0b8db0e8a163b3c3ae7dec3e9158e20d368f4/packages/next-auth/src/core/pages/signin.tsx#L4-L19). This file also contains the default error-messages that `nuxt-auth` would show to the user if you would not handle the error manually using `redirect: false`. - -## Provider: `local or refresh` - -As the `local` and `refresh` providers do not come with a pre-made login page, you will have to build one yourself. To do so: - -1. Create the page, e.g., `pages/login.vue` -2. Call the `signIn` method with the username and password your user has to enter on this page -3. Disable the page protection, if you have the global middleware enabled -4. Configure `nuxt-auth` to know the place of your login page - -A full example login page could look like this: -```vue - - - - -``` - -Then the full `nuxt.config.ts` for this would look like this: -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - provider: { - type: 'local', - pages: { - login: '/my-cool-login' - }, - }, - globalAppMiddleware: { - isEnabled: true - } - } -}) -``` - -Note: -- if your global app middleware is disabled, you have to add `auth: true` to the `definePageMeta`-code above to correctly enable the guest-mode -- the default location set for the `login` page is `/login` diff --git a/docs/content/3.application-side/4.protecting-pages.md b/docs/content/3.application-side/4.protecting-pages.md deleted file mode 100644 index 8930b37b..00000000 --- a/docs/content/3.application-side/4.protecting-pages.md +++ /dev/null @@ -1,160 +0,0 @@ -# Protecting Pages - -`nuxt-auth` offers different approaches to protect pages: -1. Global protection: Protects all pages with manual exceptions -2. Local protection: Protects specific pages -3. Custom middleware: Create your own middleware -4. Guest mode: Only logged out people can view the page, [more here](/nuxt-auth/v0.6/application-side/guest-mode) - -Briefly summarized, you can enable global protection (1) in your `nuxt.config.ts`: -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - globalAppMiddleware: true - } -}) -``` - -Now *all pages*, aside from the login pages will require sign-in. Learn how to add excepted pages [below](/nuxt-auth/v0.6/application-side/protecting-pages#disabling-the-global-middleware-locally) - -To enable page-local protection (2), add the following `definePageMeta` directive to a page: -```vue - - - - -``` - -You cannot mix approach (1) and (2). So, if the global middleware is enabled, you cannot additionally add another protection middleware to a specific page. - -## Global middleware - -To create a global authentication middleware that ensures that your user is authenticated no matter which page they visit, you configure `nuxt-auth` as follows: -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - globalAppMiddleware: true - } -}) -``` - -That's it! Every page of your application will now need authentication for the user to visit it. - -### Middleware Options - -#### `unauthenticatedOnly` - -Whether to only allow unauthenticated users to access this page. Authenticated users will be redirected to `/` or the route defined in `navigateAuthenticatedTo` - -#### `navigateAuthenticatedTo` - -Where to redirect authenticated users if `unauthenticatedOnly` is set to true - -#### `navigateUnauthenticatedTo` - -Where to redirect unauthenticated users if this page is protected - -### Disabling the global middleware locally - -To disable the global middleware on a specific page only, you can use the [`definePageMeta` macro](https://nuxt.com/docs/api/utils/define-page-meta#definepagemeta) to turn `auth` off: -```vue - - - - -``` - -Note: This only works on `pages/`. It notably does not work inside the `app.vue`. - - -## Local middleware - -To protect specific pages with a middleware, you can use the [`definePageMeta` macro](https://nuxt.com/docs/api/utils/define-page-meta#definepagemeta) to turn `auth` on: -```vue - - - - -``` - -Note: You cannot use local protection when you turned on the global middleware by setting `globalAppMiddleware: true` in the `nuxt-auth` configuration. You will get an error along the lines of "Error: Unknown route middleware: 'auth'". This is because the `auth` middleware is then added globally and not available to use as a local, page-specific middleware. - -## Custom middleware - -You may create your own application-side middleware in order to implement custom, more advanced authentication logic. - -::alert{type="warning"} -Creating a custom middleware is an advanced, experimental option and may result in unexpected or undesired behavior if you are not familiar with advanced Nuxt 3 concepts. -:: - -To implement your custom middleware: -1. Create an application-side middleware that applies either globally or is named (see [the Nuxt docs for more](https://nuxt.com/docs/guide/directory-structure/middleware#middleware-directory)) -2. Add logic based on `useAuth` to it - -When adding the logic, you need to watch out when calling other `async` composable functions. This can lead to `context`-problems in Nuxt, see [the explanation for this here](https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529). In order to avoid these problems, you will need to either: -- use the undocumented `callWithNuxt` utility when `await`ing other composables, -- return an async function where possible instead of `await`ing it to avoid `callWithNuxt` - -Following are examples of both kinds of usage: -::code-group -```ts [direct return] -// file: ~/middleware/authentication.global.ts -export default defineNuxtRouteMiddleware((to) => { - const { status, signIn } = useAuth() - - // Return immediately if user is already authenticated - if (status.value === 'authenticated') { - return - } - - /** - * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529 - * - * So to avoid calling it, we return it immediately. - */ - return signIn(undefined, { callbackUrl: to.path }) as ReturnType -}) -``` -```ts [callWithNuxt] -// file: ~/middleware/authentication.global.ts -import { useNuxtApp } from '#imports' -import { callWithNuxt } from '#app/nuxt' - -export default defineNuxtRouteMiddleware((to) => { - // It's important to do this as early as possible - const nuxtApp = useNuxtApp() - - const { status, signIn } = useAuth() - - // Return immediately if user is already authenticated - if (status.value === 'authenticated') { - return - } - - /** - * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529 - * - * So to avoid calling it, we call it via `callWithNuxt`. - */ - await callWithNuxt(nuxtApp, signIn, [undefined, { callbackUrl: to.path }]) -}) -``` -:: - -## Guest Mode - -Since v0.5.0 `nuxt-auth` also offers a guest mode. Checkout the [guest mode](/nuxt-auth/v0.6/application-side/guest-mode) documentation. diff --git a/docs/content/3.application-side/5.guest-mode.md b/docs/content/3.application-side/5.guest-mode.md deleted file mode 100644 index 6829875c..00000000 --- a/docs/content/3.application-side/5.guest-mode.md +++ /dev/null @@ -1,30 +0,0 @@ -# Guest Mode - -::alert{type="success"} -This feature was added in v0.5.0 of `nuxt-auth`. -:: - -You can use `nuxt-auth` to setup pages that are accessible only when the user is **not logged in**. This is sometimes called "guest mode". The behavior of such a page is as follows: -- A logged in user visits the page -> redirect to another (likely protected) page, -- A logged out user visits the page -> they are allowed to stay and view it - -This behavior is useful for login pages that you don't want to be visitable by logged in users: Why should they go through a login flow again? - -## Usage - -Briefly summarized, you can enable guest mode on a page by passing the following configuration: -```ts -definePageMeta({ - middleware: 'auth', // Only required if globalAppMiddleware is disabled - auth: { - unauthenticatedOnly: true, - navigateAuthenticatedTo: '/profile', - }, -}) -``` - -The above will: -- allow only guest (== users that are not logged in) to visit the page -- redirect everyone who is already logged in to `/profile` - -Note: Setting `unauthenticatedOnly: false` above is equivalent to setting `auth: false` from the user-perspective, but requires some extra middleware-steps, so is a bit less efficient. Therefore it is recommended to always use `auth: false` instead. diff --git a/docs/content/3.application-side/_dir.yml b/docs/content/3.application-side/_dir.yml deleted file mode 100644 index 201d148b..00000000 --- a/docs/content/3.application-side/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Application-Side -icon: heroicons-outline:computer-desktop diff --git a/docs/content/4.server-side/1.index.md b/docs/content/4.server-side/1.index.md deleted file mode 100644 index 10dab522..00000000 --- a/docs/content/4.server-side/1.index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: "Server-side usage of `nuxt-auth` for Vue / Nuxt 3 apps." ---- - -# Overview - -Only the `authjs` provider contains server-side functionality. On the server-side the `authjs` provider of this module offers: -::list{type="success"} -- [Session Access and Route Protection](/nuxt-auth/v0.6/server-side/session-access-and-route-protection) -- [JWT Access](/nuxt-auth/v0.6/server-side/jwt-access) -- [REST API](/nuxt-auth/v0.6/server-side/rest-api) -:: - -Server-side usage refers to any code that only runs on the server, e.g., in your `~/server/` directory, read the [glossary](/nuxt-auth/v0.6/resources/glossary) for more. diff --git a/docs/content/4.server-side/4.rest-api.md b/docs/content/4.server-side/4.rest-api.md deleted file mode 100644 index 86a6f6cf..00000000 --- a/docs/content/4.server-side/4.rest-api.md +++ /dev/null @@ -1,22 +0,0 @@ -::alert{type="info"} -This page is only relevant to you, if you are using the `authjs`-provider. -:: - -# REST API - -All endpoints that NextAuth.js supports are also supported by `nuxt-auth`: - -| Endpoint | Request | -|--------------------------------|:-------------| -| `${basePath}/signin` | `GET` | -| `${basePath}/signin/:provider` | `POST` | -| `${basePath}/refresh/:provider` | `POST` | -| `${basePath}/callback/:provider` | `GET` `POST` | -| `${basePath}/signout` | `GET` `POST` | -| `${basePath}/session` | `GET` | -| `${basePath}/csrf` | `GET` | -| `${basePath}/providers` | `GET` | - -The `basePath` is `/api/auth` per default and [can be configured in the `nuxt.config.ts`](/nuxt-auth/v0.6/configuration/nuxt-config). - -You can directly interact with these API endpoints if you wish to, it's probably a better idea to use `useAuth` where possible though. [See the full rest API documentation of NextAuth.js here](https://next-auth.js.org/getting-started/rest-api). diff --git a/docs/content/4.server-side/_dir.yml b/docs/content/4.server-side/_dir.yml deleted file mode 100644 index 06a72e86..00000000 --- a/docs/content/4.server-side/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Server-Side -icon: heroicons-outline:computer-desktop diff --git a/docs/content/5.recipes/1.index.md b/docs/content/5.recipes/1.index.md deleted file mode 100644 index 0113b4b2..00000000 --- a/docs/content/5.recipes/1.index.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -description: "`nuxt-auth` recipes on how to use Strapi, Directus, expand user session data and more for Vue / Nuxt 3 apps." ---- -# Overview - -The following pages contain some recipes for commonly asked patterns, questions and implementations. The recipes are mostly provided by the community and can serve as guidelines to implement something similar in your Nuxt 3 application. The recipes are not all tested through by the sidebase team. If you have any concerns, questions or improvement proposals or want to contribute a recipe yourself, we'd be very happy if you open an issue on our repository: https://github.com/sidebase/nuxt-auth/issues/new/choose - -Thanks to everybody who contributed so far ❤️ diff --git a/docs/content/5.recipes/2.strapi.md b/docs/content/5.recipes/2.strapi.md deleted file mode 100644 index 3aec076c..00000000 --- a/docs/content/5.recipes/2.strapi.md +++ /dev/null @@ -1,76 +0,0 @@ -# Strapi + Provider `authjs` - -This section gives an example of how the `NuxtAuthHandler` can be configured to use Strapi JWTs for authentication via the `CredentialsProvider` provider. - -You have to configure the following places to make `nuxt-auth` work with Strapi: -- `STRAPI_BASE_URL` in `.env`: Add the Strapi environment variable to your .env file -- [`runtimeConfig.STRAPI_BASE_URL`-key in `nuxt.config.ts`](/nuxt-auth/v0.6/configuration/nuxt-config): Add the Strapi base url env variable to the runtime config -- [`auth`-key in `nuxt.config.ts`](/nuxt-auth/v0.6/configuration/nuxt-config): Configure the module itself, e.g., where the auth-endpoints are, what origin the app is deployed to, ... -- [NuxtAuthHandler](/nuxt-auth/v0.6/configuration/nuxt-auth-handler): Configure the authentication behavior, e.g., what authentication providers to use - -For a production deployment, you will have to at least set the: -- `STRAPI_BASE_URL` Strapi base URL for all API endpoints by default http://localhost:1337 - -1. Create a `.env` file with the following lines: -```env -// Strapi v4 url, out of the box -AUTH_ORIGIN=http://localhost:3000 -NUXT_SECRET=a-not-so-good-secret -STRAPI_BASE_URL=http://localhost:1337/api -``` - -2. Set the following options in your `nuxt.config.ts`: -```ts -export default defineNuxtConfig({ - runtimeConfig: { - // The private keys which are only available server-side - NUXT_SECRET: process.env.NUXT_SECRET, - // Default http://localhost:1337/api - STRAPI_BASE_URL: process.env.STRAPI_BASE_URL, - }, -}); -``` - -3. Create the catch-all `NuxtAuthHandler` and add the this custom Strapi credentials provider: -```ts -// file: ~/server/api/auth/[...].ts -import CredentialsProvider from "next-auth/providers/credentials"; -import { NuxtAuthHandler } from "#auth"; -const config = useRuntimeConfig() - -export default NuxtAuthHandler({ - secret: config.NUXT_SECRET, - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - name: "Credentials", - credentials: {}, // Object is required but can be left empty. - async authorize(credentials: any) { - const response = await $fetch( - `${config.STRAPI_BASE_URL}/auth/local/`, - { - method: "POST", - body: JSON.stringify({ - identifier: credentials.username, - password: credentials.password, - }), - } - ); - - if (response.user) { - const u = { - id: response.id, - name: response.user.username, - email: response.user.email, - }; - return u; - } else { - return null - } - }, - }), - ] -}); -``` - -Checkout this blog-post for further notes and explanation: https://darweb.nl/foundry/article/nuxt3-sidebase-strapi-user-auth diff --git a/docs/content/5.recipes/3.directus.md b/docs/content/5.recipes/3.directus.md deleted file mode 100644 index 7732e611..00000000 --- a/docs/content/5.recipes/3.directus.md +++ /dev/null @@ -1,201 +0,0 @@ -# Directus + Provider `authjs` - -This section gives an example of how the `NuxtAuthHandler` can be configured to use Directus JWTs for authentication via the `CredentialsProvider` provider and how to implement a token refresh for the Directus JWT. - -The below is a code-example that needs to be adapted to your specific configuration: -```ts -import CredentialsProvider from "next-auth/providers/credentials"; -import { NuxtAuthHandler } from "#auth"; - -/** - * Takes a token, and returns a new token with updated - * `accessToken` and `accessTokenExpires`. If an error occurs, - * returns the old token and an error property - */ -async function refreshAccessToken(refreshToken: { - accessToken: string; - accessTokenExpires: string; - refreshToken: string; -}) { - try { - console.warn("trying to post to refresh token"); - - const refreshedTokens = await $fetch<{ - data: { - access_token: string; - expires: number; - refresh_token: string; - }; - } | null>("https://domain.directus.app/auth/refresh", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: { - refresh_token: refreshToken.refreshToken, - mode: "json", - }, - }); - - if (!refreshedTokens || !refreshedTokens.data) { - console.warn("No refreshed tokens"); - throw refreshedTokens; - } - - console.warn("Refreshed tokens successfully"); - return { - ...refreshToken, - accessToken: refreshedTokens.data.access_token, - accessTokenExpires: Date.now() + refreshedTokens.data.expires, - refreshToken: refreshedTokens.data.refresh_token, - }; - } catch (error) { - console.warn("Error refreshing token", error); - return { - ...refreshToken, - error: "RefreshAccessTokenError", - }; - } -} - -export default NuxtAuthHandler({ - // secret needed to run nuxt-auth in production mode (used to encrypt data) - secret: process.env.NUXT_SECRET, - - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - // The name to display on the sign in form (e.g. 'Sign in with...') - name: "Credentials", - // The credentials is used to generate a suitable form on the sign in page. - // You can specify whatever fields you are expecting to be submitted. - // e.g. domain, username, password, 2FA token, etc. - // You can pass any HTML attribute to the tag through the object. - credentials: { - email: { label: "Email", type: "text" }, - password: { label: "Password", type: "password" }, - }, - async authorize(credentials: any) { - // You need to provide your own logic here that takes the credentials - // submitted and returns either a object representing a user or value - // that is false/null if the credentials are invalid. - // NOTE: THE BELOW LOGIC IS NOT SAFE OR PROPER FOR AUTHENTICATION! - - try { - const payload = { - email: credentials.email, - password: credentials.password, - }; - - const userTokens = await $fetch<{ - data: { access_token: string; expires: number; refresh_token: string }; - } | null>("https://domain.directus.app/auth/login", { - method: "POST", - body: payload, - headers: { - "Content-Type": "application/json", - "Accept-Language": "en-US", - }, - }); - - const userDetails = await $fetch<{ - data: { - id: string; - email: string; - first_name: string; - last_name: string; - role: string; - phone?: string; - cvr?: string; - company_name?: string; - }; - } | null>("https://domain.directus.app/users/me", { - method: "GET", - headers: { - "Content-Type": "application/json", - "Accept-Language": "en-US", - Authorization: `Bearer ${userTokens?.data?.access_token}`, - }, - }); - - if (!userTokens || !userTokens.data || !userDetails || !userDetails.data) { - throw createError({ - statusCode: 500, - statusMessage: "Next auth failed", - }); - } - - const user = { - id: userDetails.data.id, - email: userDetails.data.email, - firstName: userDetails.data.first_name, - lastName: userDetails.data.last_name, - role: userDetails.data.role, - phone: userDetails.data.phone, - cvr: userDetails.data.cvr, - companyName: userDetails.data.company_name, - accessToken: userTokens.data.access_token, - accessTokenExpires: Date.now() + userTokens.data.expires, - refreshToken: userTokens.data.refresh_token, - }; - - const allowedRoles = [ - "53ed3a6a-b236-49aa-be72-f26e6e4857a0", - "d9b59a92-e85d-43e2-8062-7a1242a8fce6", - ]; - - // Only allow admins and sales - if (!allowedRoles.includes(user.role)) { - throw createError({ - statusCode: 403, - statusMessage: "Not allowed", - }); - } - - return user; - } catch (error) { - console.warn("Error logging in", error); - - return null; - } - }, - }), - ], - - session: { - strategy: "jwt", - }, - - callbacks: { - async jwt({ token, user, account }) { - if (account && user) { - console.warn("JWT callback", { token, user, account }); - return { - ...token, - ...user, - }; - } - - // Handle token refresh before it expires of 15 minutes - if (token.accessTokenExpires && Date.now() > token.accessTokenExpires) { - console.warn("Token is expired. Getting a new"); - return refreshAccessToken(token); - } - - return token; - }, - async session({ session, token }) { - session.user = { - ...session.user, - ...token, - }; - - return session; - }, - }, -}); -``` - -This was contributes by [@madsh93 from Github](https://github.com/madsh93) here: -- Github Comment: https://github.com/sidebase/nuxt-auth/v0.6/issues/64#issuecomment-1330308402 -- Gist: https://gist.github.com/madsh93/b573b3d8f070e62eaebc5c53ae34e2cc diff --git a/docs/content/5.recipes/4.custom-session-data.md b/docs/content/5.recipes/4.custom-session-data.md deleted file mode 100644 index e23294f8..00000000 --- a/docs/content/5.recipes/4.custom-session-data.md +++ /dev/null @@ -1,107 +0,0 @@ -# Custom Session Data + Provider `authjs` - -This guide explains how to add custom data to the user session. - -To expand / change / customize the session data you need to to follow the following steps: -1. Return the custom data in the `authorize` function -2. Add callbacks to the `NuxtAuthProvider` to alter the session data - -In the following example we'll add 2 new fields to the session: `accessToken` and `role`. - -## Return the custom data in the Authorize function - - -```ts -// file: ~/server/api/auth/[...].ts - -export default NuxtAuthHandler({ - secret: config.NUXT_SECRET, - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - ... - async authorize(credentials: any) { - const data = { - user: { - name: 'John Doe', - email: 'john@email.com', - id: 1, - role: 'admin' - }, - token: '123456890', - } - - if (data.user) { - const u = { - id: data.user.id, - name: data.user.username, - email: data.user.email, - access_token: data.token, // additional field - role: response.user.role // additional field - }; - return u; - } else { - throw createError({ - statusCode: 403, - statusMessage: "Credentials not working", - }); - } - }, - )} - ] - ... // other config - -``` -We can see the new fields in the return object `u` `role` and `access_token`. Now we need to pass them in the callbacks. - -## Add callbacks to the `NuxtAuthProvider` to alter the session data - -First, you must add jwt and session callbacks to [the `NuxtAuthHandler`](/nuxt-auth/v0.6/configuration/nuxt-auth-handler): -```ts -// file: ~/server/api/auth/[...].ts -export default NuxtAuthHandler({ - // ... remainder of your config ... - callbacks: { - // Callback when the JWT is created / updated, see https://next-auth.js.org/configuration/callbacks#jwt-callback - jwt: async ({token, user}) => { - const isSignIn = user ? true : false; - if (isSignIn) { - token.jwt = user ? (user as any).access_token || '' : ''; - token.id = user ? user.id || '' : ''; - token.role = user ? (user as any).role || '' : ''; - } - return Promise.resolve(token); - }, - // Callback whenever session is checked, see https://next-auth.js.org/configuration/callbacks#session-callback - session: async ({session, token}) => { - (session as any).role = token.role; - (session as any).uid = token.id; - return Promise.resolve(session); - }, - }, - ..., // other config - providers: [...], // Your provider config -}) -``` -Notice that in the `jwt` callback we pass both the `access_token` and the `role` but in the session callback we leave out the `access_token` and we only pass the role since this would expose the token on the frontend through the session object. Exposing the `access_token` is not recommended, but you can pass it to the session if needed. - -After that you can access the new fields in the data portion of the session object: -```ts -const { status, data } = useAuth(); - -console.log(data); - -``` - -output: -``` -{ "user": { "name": "xxxxxxx", "email": "xxxxx@xxxxx.gr" }, "expires": "2023-02-28T20:36:12.584Z", "role": "admin", "uid": 1 } -``` - - -With this approach `data` will automatically be correctly typed. - - -Thanks to -- [@JoaoPedroAS51 from Github](https://github.com/JoaoPedroAS51) for his contribution here: https://github.com/sidebase/nuxt-auth/v0.6/issues/61#issuecomment-1330747199 -- [@pvlastaridis from Github](https://github.com/pvlastaridis) for his contribution here: https://github.com/sidebase/nuxt-auth/v0.6/issues/148#issuecomment-1407490921 diff --git a/docs/content/5.recipes/5.nuxt-auth-example.md b/docs/content/5.recipes/5.nuxt-auth-example.md deleted file mode 100644 index f3fdc3c1..00000000 --- a/docs/content/5.recipes/5.nuxt-auth-example.md +++ /dev/null @@ -1,6 +0,0 @@ -# Nuxt Auth Example Code + Provider `authjs` - -Minimal example of using `nuxt-auth` for http://nuxt-auth-example.sidebase.io/. See the repo [here](https://github.com/sidebase/nuxt-auth-example). - -::sandbox{repo="sidebase/nuxt-auth-example" branch="main" dir="/" file="app.vue"} -:: diff --git a/docs/content/5.recipes/6.laravel-passport.md b/docs/content/5.recipes/6.laravel-passport.md deleted file mode 100644 index 72e43d28..00000000 --- a/docs/content/5.recipes/6.laravel-passport.md +++ /dev/null @@ -1,119 +0,0 @@ -# Laravel Passport + Provider `authjs` - -This section gives an example of how the `NuxtAuthHandler` can be configured to use Laravel Passport Oauth2 and SSO. - -## 1. Creating an API client on Laravel Passport - -You can refer to the official [Laravel documentation](https://laravel.com/docs/10.x/passport#managing-clients) to add new client to Passport. - -By default, you can simply create one using the command: - -```sh -php artisan passport:client -``` - -It will ask you to choose a -- `client ID`, and -- a `redirect URI`. - -Keep the client ID for the next step and set the redirect URI to `http://localhost:3000/api/auth/callback/laravelpassport` (default value for dev environement, modify it according to your environement, you can add several URI comma separated). - -## 2. Add a Laravel API route returning the user data - -Next create a route that is returned to the user. In the example given here, we will use `/api/v1/me`. - -The route will return the field of your user data. You **must** return a field with the key `id`. - -## 3. Setting configuration and the provider - -### 3.1. Storing the config in your .env - -You can add the following variables to your .env: -- `PASSPORT_BASE_URL`: the URL of your passport APP -- `PASSPORT_CLIENT_ID`: the client ID you set in the previous step -- `PASSPORT_CLIENT_SECRET`: the client secret Laravel generated for you at the end of step 1 - -```bash -# .env -PASSPORT_BASE_URL=http://www.my_passport_app.test -PASSPORT_CLIENT_ID=123456789 -PASSPORT_CLIENT_SECRET=123456789 -``` - -### 3.2. Adding your config to the runtimeConfig - -Then add these values to your runtimeConfig: - -```ts -// ~/nuxt.config.ts -export default defineNuxtConfig({ - //... - modules: [ - //... - '@sidebase/nuxt-auth', - ], - runtimeConfig: { - //... - passport: { - baseUrl: process.env.PASSPORT_BASE_URL, - clientId: process.env.PASSPORT_CLIENT_ID, - clientSecret: process.env.PASSPORT_CLIENT_SECRET, - } - - }, -}); -``` - -### 2.3. Create the catch-all `NuxtAuthHandler` and add the this custom provider: - -```ts -// ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' -const { passport } = useRuntimeConfig(); //get the values from the runtimeConfig - -export default NuxtAuthHandler({ - //... - providers: [ - { - id: "laravelpassport", //ID is only used for the callback URL - name: "Passport", // name is used for the login button - type: "oauth", // connexion type - version: "2.0",// oauth version - authorization: { - url: `${passport.baseUrl}/oauth/authorize`, // this is the route created by passport by default to get the autorization code - params: { - scope: "*", // this is the wildcard for all scopes in laravel passport, you can specify scopes separated by a space - } - }, - token: { - url: `${passport.baseUrl}/oauth/token`, // this is the default route created by passport to get and renew the tokens - }, - clientId: passport.clientId, // the client Id - clientSecret: passport.clientSecret,// the client secret - userinfo: { - url: `${passport.baseUrl}/api/v1/me`,// this is a custom route that must return the current user that must be created in laravel - }, - profile: (profile) => { - // map the session fields with you laravel fields - // profile is the user coming from the laravel app - // update the return with your own fields names - return { - id: profile.id, - name: profile.username, - email: profile.email, - image: profile.image, - }; - }, - idToken: false, - } - ], -}); -``` - -## 4. Reference - -You can find the full discussion in the issue [#149](https://github.com/sidebase/nuxt-auth/v0.6/issues/149) - -## 5. Credits - -Solution provided by [@Jericho1060](https://github.com/Jericho1060) diff --git a/docs/content/5.recipes/7.vercel-deployment.md b/docs/content/5.recipes/7.vercel-deployment.md deleted file mode 100644 index 4085b885..00000000 --- a/docs/content/5.recipes/7.vercel-deployment.md +++ /dev/null @@ -1,43 +0,0 @@ -# Vercel Deployment + Provider `authjs` - -This section gives an example of how to deploy `nuxt-auth` to Vercel. The deployed application used in this example is the offical [nuxt-auth-example](https://github.com/sidebase/nuxt-auth-example/). - -You can find a deployed example of the `nuxt-auth-example` [here](https://nuxt-auth-example.vercel.app/). - -## Cloning project to Vercel - -Begin by creating a new project in your Vercel dashboard. In this example we will be deploying from a GitHub repository. Select your `nuxt-auth` enabled project and import it. - -![Clone repository in Vercel](/nuxt-auth/vercel/create-project.png) - -## Setting environment variables - -In order to run the `nuxt-auth-example` the following environment variables are required. These may however change depending on your project. - -![Env variables](/nuxt-auth/vercel/env.png) - -### NUXT_SECRET - - When deploying `nuxt-auth` to Vercel you cannot set a random string as your nuxt secret. You must use a 32-bit generated secret. You can use [this](https://generate-secret.vercel.app/32) website to generate a custom secret. - -### AUTH_ORIGIN - -In order to set the correct origin go into your Vercel project settings and navigate to the `Domains` tab. Once there you will all the assigned domains and their assigned environment (production, dev). - -Copy the correct domain for every enviroment and assign the environment variables `AUTH_ORIGIN` to match the correct environment. - -### Github_* - -Assign your generated GitHub 0Auth application id and secret. When generating your 0Auth application set the homepage URL to your vercel domain and the callback url to: - -https://YOUR_VERCEL_APP.vercel.com/api/auth/callback/github - -## Final steps - -You can now re-deploy your add to vercel. Ensure that all the enviroment variables and domains match your enviroment to ensure the correct data is passed. - -You can set the environment of a deployment to production by right clicking it and selecting, promote to production. - -![Promote to Production](/nuxt-auth/vercel/promote_production.png) - -You can clone the repository used in this example [here](https://github.com/sidebase/nuxt-auth-example/) to deploy your own version of `nuxt-auth` to Vercel! diff --git a/docs/content/5.recipes/_dir.yml b/docs/content/5.recipes/_dir.yml deleted file mode 100644 index 70c9f704..00000000 --- a/docs/content/5.recipes/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Recipes -icon: heroicons-outline:queue-list diff --git a/docs/content/6.resources/0.index.md b/docs/content/6.resources/0.index.md deleted file mode 100644 index 0b8c8337..00000000 --- a/docs/content/6.resources/0.index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Overview - -The following ressources should help with using nuxt-auth and tackle some topics that are frequently encountered during app-development. We will continue to expand this with more guides, recipes and references based on the questions, feedback and problems we see and receive from our community: https://discord.gg/VzABbVsqAc diff --git a/docs/content/6.resources/1.external-blogs-guides-examples.md b/docs/content/6.resources/1.external-blogs-guides-examples.md deleted file mode 100644 index 07ac1905..00000000 --- a/docs/content/6.resources/1.external-blogs-guides-examples.md +++ /dev/null @@ -1,9 +0,0 @@ -# External Blogs, Guides and Examples - -This is a collection of resources that we'll expand over time: -- [Example of Nuxt 3, Strapi and `nuxt-auth`](https://darweb.nl/foundry/article/nuxt3-sidebase-strapi-user-auth) -- [Example NuxtAuthHandler configuration for Directus](https://gist.github.com/madsh93/b573b3d8f070e62eaebc5c53ae34e2cc) -- [How to expand the user data object](https://github.com/sidebase/nuxt-auth/v0.6/issues/61#issuecomment-1330946022) -- [Docus NuxtAuthHandler that shows how to manually fetch an external JWT token, how to expand the user data and how to track session expiry manually](https://gist.github.com/madsh93/b573b3d8f070e62eaebc5c53ae34e2cc) - -Note: If you're looking to make something work in `nuxt-auth` the nice thing is that you can use most `NextAuth.js` resources to help you along the way, as the `NuxtAuthHandler` behaves identical to the [`NextAuth` handler](https://next-auth.js.org/configuration/initialization). diff --git a/docs/content/6.resources/2.glossary.md b/docs/content/6.resources/2.glossary.md deleted file mode 100644 index 153cb2a4..00000000 --- a/docs/content/6.resources/2.glossary.md +++ /dev/null @@ -1,12 +0,0 @@ -# Glossary - -There are some terms we use in this documentation that may not be known to every reader. Here is an explanation for some of them: -- `application` / `application-side` / `universal-application`: This references all Nuxt code of your app that is [universally rendered](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering). In short this means that that code is rendered on the server-side and on the client-side, so all JS in it is executed twice. This is an important distinction, as some things may behave different on the server-side than on the client-side. We use `application...` to denote something that will be universally rendered -- `server` / `server-side`: This references all Nuxt code of your app that will run **only** on your server. For example, all code inside the `~/server` directory should only ever run on the server -- `sessions`: A set of information that persists for a longer duration, e.g., the username and email that persists while your user is logged in -- `authentication`: Verifying that someone is who they claims to be, e.g., by asking them for a username and password or by asking Google to verify it (OAuth) and then trust their result -- `provider`: Could mean two things in the context of `nuxt-auth`: - - The authentication provider you select on the `nuxt-auth` module level by setting the `provider.type` key to either `local` or `authjs` - - The OAuth authentication provider you choose to use in combination with the `authjs` module - -[Let us know if you're missing something from this list](https://github.com/sidebase/nuxt-auth/v0.6/issues/new/choose) diff --git a/docs/content/6.resources/4.prior-work.md b/docs/content/6.resources/4.prior-work.md deleted file mode 100644 index 6ae33fdf..00000000 --- a/docs/content/6.resources/4.prior-work.md +++ /dev/null @@ -1,19 +0,0 @@ -# Prior Work and Module Concept - -The idea of this library is to re-use all the open-source implementation that already exist in the JS ecosystem instead of rolling our own. The idea was born when researching through the ecosystem of framework-specific authentication libraries to figure out what the best implementation approach for a state-of-the-art Nuxt 3 authentication library would be. - -During research it became clear that implementing everything from scratch will be: -- a lot of work that has already been open-sourced by others, -- error prone as authentication has a lot of intricacies that need to be resolved in order to get it right, -- hard to maintain as authentication providers come and go, -- hard to build initial trust for as authentication is important and cannot go wrong, - -In order to avoid these problems without taking forever (leaving Nuxt without an authentication library in the meantime), we decided to investigate if we can wrap [`NextAuth.js`](https://github.com/nextauthjs/next-auth), the most popular authentication library in the Next.js ecosystem by far and a trusted, well maintained one at that! - -In our investigation we found prior attempts to make NextAuth.js framework agnostic. These have more or less come to fruition, so far mostly resulting in some PoCs and example apps. Looking at these was quite helpful to get started. In particular, big pushes in the right direction came from: -- [NextAuth.js app examples](https://github.com/nextauthjs/next-auth/tree/main/apps) -- [Various comments, proposals, ... of this thread](https://github.com/nextauthjs/next-auth/discussions/3942), special thanks to @brillout for starting the discussion, @balazsorban44 for NextAuth.js and encouraging the discussion, @wobsoriano for adding PoCs for multiple languages - -The main part of the work was to piece everything together, resolve some outstanding issues with existing PoCs, add new things where nothing existed yet, e.g., for the `useAuth` composable by going through the NextAuth.js client code and translating it to a Nuxt 3 approach. - -The module had another big iteration in collaboration with @JoaoPedroAS51 to make `useAuth` a sync operation and trigger the session lifecycle from a plugin rather than the `useAuth` composable itself. diff --git a/docs/content/6.resources/6.nuxt-security b/docs/content/6.resources/6.nuxt-security deleted file mode 100644 index 75258aa4..00000000 --- a/docs/content/6.resources/6.nuxt-security +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: "How to integrate nuxt-security with nuxt-auth" ---- - -In order to use the [NuxtSecurity](https://nuxt.com/modules/security) module with Nuxtauth, please use the following config, to disable the checks on the NuxtAuth routes: - -```js{}[nuxt.config.ts] -export default defineNuxtConfig({ - routeRules: { - "/api/auth/**": { - security: { - xssValidator: false, - }, - }, - } -} -``` - -Solution provided by [tmlmt](https://github.com/tmlmt) in https://github.com/sidebase/nuxt-auth/issues/324#issuecomment-1757010620 diff --git a/docs/content/6.resources/_dir.yml b/docs/content/6.resources/_dir.yml deleted file mode 100644 index 7994afb7..00000000 --- a/docs/content/6.resources/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Resources -icon: heroicons-outline:play diff --git a/docs/content/_dir.yml b/docs/content/_dir.yml deleted file mode 100644 index c44a83bd..00000000 --- a/docs/content/_dir.yml +++ /dev/null @@ -1,3 +0,0 @@ -title: nuxt-auth -icon: heroicons-outline:lock-closed -layout: module diff --git a/docs/content/v0.5/1.getting-started/1.index.md b/docs/content/v0.5/1.getting-started/1.index.md deleted file mode 100644 index 77825ba4..00000000 --- a/docs/content/v0.5/1.getting-started/1.index.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -description: "Introduction to `nuxt-auth` and its features as an authentication module for your Vue / Nuxt 3 application." ---- - -# Introduction - -::alert{type="info"} -[Checkout the `0.6`-docs](/nuxt-auth/v0.6/getting-started) if you want to get get started with the `local`-provider or have a static Nuxt application. -:: - -`nuxt-auth` is an open source Nuxt module that provides authentication for non-static Nuxt 3 applications. - -The easiest way to get started with `nuxt-auth` is using [the sidebase Merino stack](/sidebase): -::code-group -```bash [npm] -npm create sidebase@latest -``` -```bash [pnpm] -pnpm create sidebase@latest -``` -```bash [yarn] -# Note: Due to a known problem with `yarn`, it is not possible to force yarn to always use `@latest`: https://github.com/yarnpkg/yarn/issues/6587 -yarn create sidebase -``` -:: -This will create a Nuxt 3 project with `nuxt-auth` already setup & working. - -`nuxt-auth` supports three main types of authentication out of the box: -::list{type="success"} -- OAuth: Authentication via third-party OAuth providers like Google, Azure, Github, Discord, ... -- Credentials: Authentication via a username and password that the user supplies and custom logic that you implement yourself -- Emails: Authentication via "Magic URL" emails, like Slack or Notion -:: - -`nuxt-auth` is able to provide the above and more (like database adapters, callback hooks, ...) by wrapping [NextAuth.js](https://github.com/nextauthjs/next-auth) under the hood. This gives the reliability & convenience of a >12.000 github star library to the Nuxt 3 ecosystem with a native nuxt developer experience (DX). Wrapping `NextAuth.js` has the second advantage that many OAuth providers, database adapters, callbacks and more are supported out-of-the-box. This means that whenever you want to achieve something within the `NuxtAuthHandler` you can use all NextAuth guides and documentation to do so - the authentication handlers behave identical. - -`nuxt-auth` also provides Nuxt 3 specific features like a convenient application-side composable to login, logout, access user-authentication data or an authentication middleware and plugin that take care of managing the user authentication lifecycle by fetching authentication data on initial load, refreshing the user authentication on re-focusing the tab and more. - -::callout -#summary -Show me the code! - -#content -Visit the [quick start](/nuxt-auth/getting-started/quick-start) page to see code examples. - -Checkout the example `nuxt-auth` app: https://nuxt-auth-example.sidebase.io/ - -Here's the source-code https://github.com/sidebase/nuxt-auth-example of the example app. -:: - -## Authentication providers - -::list{type="success"} -- OAuth (e.g., Github, Google, Twitter, Azure, ...) -- Custom OAuth (write it yourself) -- Credentials (password + username) -- Email Magic URLs -:: - -## Application Side Session Management -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: -::list{type="success"} -- composable `const { signIn, signOut, status, data, lastRefreshedAt, ... } = useAuth()` -- Auto-refresh the session periodically -- Auto-refresh the session on tab-refocus -- Efficient session fetching: One time on page load, afterwards for specific actions (e.g., on navigation) -- Full typescript support for all methods and properties -:: - -## Application Protection - -::list{type="success"} -- Application-side middleware protection either for the full application or specific pages -- Server-side middleware and endpoint protection -:: - -## REST API - -::list{type="success"} -- `GET /signin`, -- `POST /signin/:provider`, -- `GET/POST /callback/:provider`, -- `GET /signout`, -- `POST /signout`, -- `GET /session`, -- `GET /csrf`, -- `GET /providers` -:: diff --git a/docs/content/v0.5/1.getting-started/2.installation.md b/docs/content/v0.5/1.getting-started/2.installation.md deleted file mode 100644 index 9d2d91a3..00000000 --- a/docs/content/v0.5/1.getting-started/2.installation.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: "How to install nuxt-auth." ---- - -# Installation - -You can install `nuxt-auth` via `npm`, `yarn` or `pnpm`: -::code-group -```bash [npm] -npm i -D @sidebase/nuxt-auth@0.5.0 -``` -```bash [yarn] -yarn add --dev @sidebase/nuxt-auth@0.5.0 -``` -```bash [pnpm] -pnpm i -D @sidebase/nuxt-auth@0.5.0 -``` -:: - -::alert{type="info"} -Note that we try our best to keep `nuxt-auth` stable, but it is also a young module that is in active development. If you want to be extra sure nothing breaks, you should pin the patch version, e.g., by using `--save-exact` when running the install command. -:: - -`nuxt-auth` has `next-auth` as a peer-dependency. With all package managers except `npm` you must install the peer dependency alongside `nuxt-auth`: -::code-group -```bash [yarn] -yarn add next-auth@4.21.1 -``` -```bash [pnpm] -pnpm i next-auth@4.21.1 -``` -:: - -You can find all available `next-auth` versions [on npm](https://www.npmjs.com/package/next-auth?activeTab=versions). You do not need to install any other peer-dependencies in order to use `nuxt-auth`. - -## Requirements - -`nuxt-auth` only needs Nuxt 3 to run. In the future Nuxt 2 or Nuxt Bridge may be supported. diff --git a/docs/content/v0.5/1.getting-started/3.quick-start.md b/docs/content/v0.5/1.getting-started/3.quick-start.md deleted file mode 100644 index ec8ae56a..00000000 --- a/docs/content/v0.5/1.getting-started/3.quick-start.md +++ /dev/null @@ -1,59 +0,0 @@ -# Quick Start - -After [installing the package](/nuxt-auth/getting-started/installation), add the package to your `nuxt.config.ts`: - -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], -}) -``` - -Then create the authentication handler (`NuxtAuthHandler`) that will expose the API endpoints for handling all authentication-related requests and add at least one [authentication provider](https://next-auth.js.org/providers/): - -```ts -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' -import GithubProvider from 'next-auth/providers/github' - -export default NuxtAuthHandler({ - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - GithubProvider.default({ - clientId: 'enter-your-client-id-here', - clientSecret: 'enter-your-client-secret-here' - }) - ] -}) -``` - -That's it! You can now use all user-related functionality, for example: - -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: -::code-group -```ts [Application side] -// file: e.g ~/pages/login.vue -const { status, data, signIn, signOut } = useAuth() - -status.value // Session status: `unauthenticated`, `loading`, `authenticated` -data.value // Session data, e.g., expiration, user.email, ... - -await signIn() // Sign in the user -await signOut() // Sign out the user -``` -```ts [Server side] -// file: e.g: ~/server/api/session.get.ts -import { getServerSession } from '#auth' - -export default eventHandler(async (event) => { - const session = await getServerSession(event) - if (!session) { - return { status: 'unauthenticated!' } - } - return { status: 'authenticated!', text: 'im protected by an in-endpoint check', session } -}) -``` -:: - -To learn how to protect pages read [about the application-side usage](/nuxt-auth/application-side), to learn how to protect server-routes and API endpoints read [about the server-side usage](/nuxt-auth/server-side). diff --git a/docs/content/v0.5/1.getting-started/4.getting-help.md b/docs/content/v0.5/1.getting-started/4.getting-help.md deleted file mode 100644 index aa03b4ec..00000000 --- a/docs/content/v0.5/1.getting-started/4.getting-help.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: "How to get help when using `nuxt-auth` in your Vue / Nuxt 3 application." ---- - -# Getting Help - -At some point, you may find that there's an issue you need some help with. - -But don't worry! We're a friendly community of developers and we'd love to help. Concretely this means to: -- Checkout the docs (page that you are currently viewing), -- Search open issues and discussions: https://github.com/sidebase/nuxt-auth/issues -- Hop on Discord to ask us directly: https://discord.gg/VzABbVsqAc, -- Open an issue to file a bug, ask for an enhancement or get an answer to a question: https://github.com/sidebase/nuxt-auth/issues/new/choose - -We aim to follow the getting-help standards of the nuxt-project as described here and ask you to do the same when opening an issue or pinging us for help: https://nuxt.com/docs/community/getting-help#getting-help. diff --git a/docs/content/v0.5/1.getting-started/_dir.yml b/docs/content/v0.5/1.getting-started/_dir.yml deleted file mode 100644 index 76238cf5..00000000 --- a/docs/content/v0.5/1.getting-started/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Getting Started -icon: heroicons-outline:sparkles diff --git a/docs/content/v0.5/2.configuration/1.index.md b/docs/content/v0.5/2.configuration/1.index.md deleted file mode 100644 index 277fd946..00000000 --- a/docs/content/v0.5/2.configuration/1.index.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: "Overview of the configuration options of `nuxt-auth` for Vue / Nuxt 3." ---- - -# Overview - -There's two places to configure `nuxt-auth`: -- [auth key in `nuxt.config.ts`](/nuxt-auth/configuration/nuxt-config): Configure the module itself, e.g., whether global page protection is enabled -- [NuxtAuthHandler](/nuxt-auth/configuration/nuxt-auth-handler): Configure the authentication behavior, e.g., what authentication providers to use - -For development, using the [Quick Start configuration](/nuxt-auth/getting-started/quick-start) will already bring you quite far. For a production deployment, you will have to: -- set the `origin` by: - 1. exporting the `AUTH_ORIGIN`-environment variable _at runtime_ (higher precedence) - 2. setting the `origin`-key inside the `nuxt.config.ts` config _at build-time_ (lower precendence) -- set a secret inside the `NuxtAuthHandler` config - -See the detailed possible configuration options on the next pages. diff --git a/docs/content/v0.5/2.configuration/2.nuxt-config.md b/docs/content/v0.5/2.configuration/2.nuxt-config.md deleted file mode 100644 index e7d6d9fb..00000000 --- a/docs/content/v0.5/2.configuration/2.nuxt-config.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -description: "Learn how to configure nuxt-auth inside of the nuxt.config.ts" -toc: true ---- - -# Module (nuxt.config.ts) - -Use the `auth`-key inside the `nuxt.config.ts` to configure the `nuxt-auth` module itself. Here are the available configuration options and their default values: -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - // The module is enabled. Change this to disable the module - isEnabled: true, - - // The origin is set to the development origin. Change this when deploying to production by setting `origin` in this config before build-time or by exporting `AUTH_ORIGIN` by running `export AUTH_ORIGIN=...` - origin: 'http://localhost:3000', - - // The base path to the authentication endpoints. Change this if you want to add your auth-endpoints at a non-default location - basePath: '/api/auth', - - // Whether to periodically refresh the session. Change this to `true` for a refresh every seconds or set this to a number like `5000` for a refresh every 5000 milliseconds (aka: 5 seconds) - enableSessionRefreshPeriodically: false, - - // Whether to refresh the session whenever a window focus event happens, i.e, when your user refocuses the window. Set this to `false` to turn this off - enableSessionRefreshOnWindowFocus: true, - - // Whether to add a global authentication middleware that will protect all pages without exclusion - globalAppMiddleware: false, - - // Select the default-provider to use when `signIn` is called. Setting this here will also effect the global middleware behavior: E.g., when you set it to `github` and the user is unauthorized, they will be directly forwarded to the Github OAuth page instead of seeing the app-login page - defaultProvider: undefined, - - // Whether to automatically set the callback url to the page the user tried to visit when the middleware stopped them. This is useful to disable this when using the credentials provider, as it does not allow a `callbackUrl`. Setting this to a string-value will result in that being used as the callbackUrl path. - addDefaultCallbackUrl: true, - - - // Configuration of the global auth-middleware (only applies if you set `globalAppMiddleware: true` above!) - globalMiddlewareOptions: { - - // Whether to allow access to 404 pages without authentication. Set this to `false` to force users to sign-in before seeing `404` pages. Setting this to false may lead to vue-router problems (as the target page does not exist) - allow404WithoutAuth: true, - - // Whether to automatically set the callback url to the page the user tried to visit when the middleware stopped them. This is useful to disable this when using the credentials provider, as it does not allow a `callbackUrl`. Setting this to a string-value will result in that being used as the callbackUrl path. Note: You also need to set the global `addDefaultCallbackUrl` setting to `false` if you want to fully disable this for the global middleware. - addDefaultCallbackUrl: true - } - } -}) -``` - -The `origin` and the `basePath` together are equivalent to the [`NEXTAUTH_URL` environment variable of NextAuth.js](https://next-auth.js.org/configuration/options#nextauth_url) - -## origin - -**You must set the `origin` in production!** You have two options to set it: -1. Set the `AUTH_ORIGIN` environment variable _at runtime_ -2. Set the `origin` origin-config key inside the `nuxt.config.ts` (see an example above) _at build-time_ - -The `AUTH_ORIGIN` environment variable takes precendence over the `origin` configuration key, so you can always overwrite the origin at deploy-time. - -The origin must be set so that `nuxt-auth` can ensure that callbacks for authentication are correct. The `origin` consists out of (up to) 3 parts: -- scheme: `http` or `https` -- host: e.g., `localhost`, `example.org`, `www.sidebase.io` -- port: e.g., `:3000`, `:4444`; leave empty to implicitly set `:80` for http, and `:443` for https (this is an internet convention, don't ask) - -For the demo-app at https://nuxt-auth-example.sidebase.io we set the `origin` to `https://nuxt-auth-example.sidebase.io`. If for some reason required, you can explicitly set the `origin` to `http://localhost:3000` to stop `nuxt-auth` from aborting `npm run build` when the origin is unset. - -## basePath - -This is what tells the module where you added the authentication endpoints. Per default the `basePath` is set to `/api/auth`, so that means that the module expects that all requests to `/api/auth/*` will be handled by the `NuxtAuthHandler`. - -To statify this, you need to create a [catch-all server-route](https://nuxt.com/docs/guide/directory-structure/pages/#catch-all-route) at that location by creating a file `~/server/api/auth/[...].ts` that exports the `NuxtAuthHandler`, see more on this in the [Quick Start](/nuxt-auth/getting-started/quick-start) or in the [`NuxtAuthHandler` documentation](/nuxt-auth/configuration/nuxt-auth-handler) - -If you want to have the authentication at another location, you can overwrite the `basePath`, e.g., when setting: -- `basePath: "/api/_auth"` -> add the authentication catch-all endpoints into `~/server/api/_auth/[...].ts` -- `basePath: "/_auth"` -> add the authentication catch-all endpoints into `~/server/routes/_auth/[...].ts` - -See [Nuxt server-routes docs on catch-all routes for a further explanation.](https://nuxt.com/docs/guide/directory-structure/server#server-routes) - -## globalAppMiddleware - -This is a middleware that comes included with `nuxt-auth`. When you enable it, it will protect _all_ pages, so even your index page (`/`) will not be accessible without a login anymore. - -Read more on this topic [in the page protection docs](/nuxt-auth/application-side/protecting-pages#global-middleware). diff --git a/docs/content/v0.5/2.configuration/3.nuxt-auth-handler.md b/docs/content/v0.5/2.configuration/3.nuxt-auth-handler.md deleted file mode 100644 index abd7f823..00000000 --- a/docs/content/v0.5/2.configuration/3.nuxt-auth-handler.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -description: "Learn how to configure the NuxtAuthHandler that handles all authentication requests on the server-side" ---- - -# NuxtAuthHandler - -After setting up [nuxt-auth inside your `nuxt.config.ts`](/nuxt-auth/configuration/nuxt-config) you can begin defining providers and other options inside your NuxtAuthHandler. - -## Creating the `NuxtAuthHandler` - -In order to create your own `NuxtAuthHandler`, create the file `~/server/api/auth/[...].ts`. This file will automatically set up the authentication API that responds to all requests going to `https://localhost/api/auth/*`. If you wish you can also use a custom file location and api path, however this change will need to be reflected in the [basePath](/nuxt-auth/configuration/nuxt-config#basepath), which is configured in the [nuxt.config.ts](/nuxt-auth/configuration/nuxt-config). - -::alert{type="warning"} -The filename must be `[...].ts` - this is a so-called "catch-all" route, read more [in the Nuxt catch-all docs](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route). -:: - -## Configuring the `NuxtAuthHandler` - -After creating the file, add the `NuxtAuthHandler({ ... })` to it. The `NuxtAuthHandler({ ... })` is used to configure how the authentication itself behaves, what [callbacks should be called](https://next-auth.js.org/configuration/callbacks), what [database adapters should be used](https://next-auth.js.org/adapters) and more: - -::code-group -```ts [Empty NuxtAuthHandler] -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // your authentication configuration here! -}) -``` -```ts [NuxtAuthHandler with Github Provider] -// file: ~/server/api/auth/[...].ts -import GithubProvider from 'next-auth/providers/github' -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - secret: 'your-secret-here', - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - GithubProvider.default({ - clientId: 'your-client-id', - clientSecret: 'your-client-secret' - }) - ] -}) -``` -```ts [NuxtAuthHandler with Credentials Provider] -// file: ~/server/api/auth/[...].ts -import CredentialsProvider from 'next-auth/providers/credentials' -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - secret: 'your-secret-here', - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - // The name to display on the sign in form (e.g. 'Sign in with...') - name: 'Credentials', - // The credentials is used to generate a suitable form on the sign in page. - // You can specify whatever fields you are expecting to be submitted. - // e.g. domain, username, password, 2FA token, etc. - // You can pass any HTML attribute to the tag through the object. - credentials: { - username: { label: 'Username', type: 'text', placeholder: '(hint: jsmith)' }, - password: { label: 'Password', type: 'password', placeholder: '(hint: hunter2)' } - }, - authorize (credentials: any) { - // You need to provide your own logic here that takes the credentials - // submitted and returns either a object representing a user or value - // that is false/null if the credentials are invalid. - // NOTE: THE BELOW LOGIC IS NOT SAFE OR PROPER FOR AUTHENTICATION! - - const user = { id: '1', name: 'J Smith', username: 'jsmith', password: 'hunter2' } - - if (credentials?.username === user.username && credentials?.password === user.password) { - // Any object returned will be saved in `user` property of the JWT - return user - } else { - // eslint-disable-next-line no-console - console.error('Warning: Malicious login attempt registered, bad credentials provided') - - // If you return null then an error will be displayed advising the user to check their details. - return null - - // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter - } - } - }) - ] -}) -``` -:: - -::alert{type="info"} -The `NuxtAuthHandler` accepts [all options that NextAuth.js accepts for its API initialization](https://next-auth.js.org/configuration/options#options). Use this place to configure authentication providers (oauth-Google, credential flow, ...), your `secret`, add callbacks for authentication events, configure a custom logger and more. Read the [`NextAuth.js` docs to see all possible options](https://next-auth.js.org/configuration/options#options). -:: - -### secret - -In theory the only required configuration key for production is the `secret`. You can set it directly inside the `NuxtAuthHandler`. In practice however you may also want to configure at least one provider so that your users can actually login. - -To avoid hard-coding of the secret you can make it configurable at runtime by using an environment variable and exporting it at runtime or by using the nuxt runtimeconfig (and then also setting the correct environment at runtime): -::code-group -```ts [Environment Variable] -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - required in production - secret: process.env.AUTH_SECRET - - // rest of your authentication configuration here! -}) -``` -```ts [useRuntimeConfig] -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - // A secret string you define, to ensure correct encryption - required in production - secret: useRuntimeConfig().authSecret - - // rest of your authentication configuration here! -}) -``` -:: - -## Further Examples - -Checkout the [provider examples](/nuxt-auth/recipes) to see different examples of provider configurations, e.g., for Strapi or Directus. diff --git a/docs/content/v0.5/2.configuration/_dir.yml b/docs/content/v0.5/2.configuration/_dir.yml deleted file mode 100644 index 266566b7..00000000 --- a/docs/content/v0.5/2.configuration/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Configuration -icon: heroicons-outline:cog diff --git a/docs/content/v0.5/3.application-side/1.index.md b/docs/content/v0.5/3.application-side/1.index.md deleted file mode 100644 index a527d581..00000000 --- a/docs/content/v0.5/3.application-side/1.index.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: "Application-side usage of `nuxt-auth` for Vue / Nuxt 3 apps." ---- - -# Overview - -On the application-side this module offers: -::list{type="success"} -- [`useAuth` composable for session access and management](/nuxt-auth/application-side/session-access-and-management) -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: -- [Creation of custom sign-in pages](/nuxt-auth/application-side/custom-sign-in-page) -- [Middleware to protect your application on the application side](/nuxt-auth/application-side/protecting-pages) -:: - -Application-side usage refers to any code like pages, components or composables that are part of the universal server- and client-side rendering of Nuxt, see more in the [glossary](/nuxt-auth/resources/glossary). diff --git a/docs/content/v0.5/3.application-side/2.session-access-and-management.md b/docs/content/v0.5/3.application-side/2.session-access-and-management.md deleted file mode 100644 index c5bfd318..00000000 --- a/docs/content/v0.5/3.application-side/2.session-access-and-management.md +++ /dev/null @@ -1,92 +0,0 @@ -# Session Access and Management -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: - -The `useAuth` composable is your main gateway to accessing and manipulating session-state and data. Here's the main methods you can use: - -```ts -const { - status, - data, - lastRefreshedAt, - getCsrfToken, - getProviders, - getSession, - signIn, - signOut, -} = useAuth() - -// Session status, either `unauthenticated`, `loading`, `authenticated`, see https://next-auth.js.org/getting-started/client#signout -status.value - -// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), `loading` (= session loading in progress), see https://next-auth.js.org/getting-started/client#signout -data.value - -// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date`-object of the time the refresh happened -lastRefreshedAt.value - -// Get / Reload the current session from the server, pass `{ required: true }` to force a login if no session exists, see https://next-auth.js.org/getting-started/client#getsession -await getSession() - -// Get the current CSRF token, usually you do not need this function, see https://next-auth.js.org/getting-started/client#signout -await getCsrfToken() - -// Get the supported providers, e.g., to build your own login page, see https://next-auth.js.org/getting-started/client#getproviders -await getProviders() - -// Trigger a sign-in, see https://next-auth.js.org/getting-started/client#signin -await signIn() - -// Trigger a sign-in with a redirect afterwards, see https://next-auth.js.org/getting-started/client#signin -await signIn(undefined, { callbackUrl: '/protected' }) - -// Trigger a sign-in via a specific authentication provider with a redirect afterwards, see https://next-auth.js.org/getting-started/client#signin -await signIn('github', { callbackUrl: '/protected' }) - -// Trigger a sign-in with username and password already passed, e.g., from your own custom-made sign-in form -await signIn('credentials', { username: 'jsmith', password: 'hunter2' }) - -// Trigger a sign-out, see https://next-auth.js.org/getting-started/client#signout -await signOut() - -// Trigger a sign-out and send the user to the sign-out page afterwards -await signOut({ callbackUrl: '/signout' }) -``` - -Session `data.value` has the following interface: -```ts -interface DefaultSession { - user?: { - name?: string | null; - email?: string | null; - image?: string | null; - }; - expires: ISODateString; -} -``` - -Note that this is only set when the use is logged-in and when the provider used to login the user provides the fields. - -## Redirects - -You can also pass the `callbackUrl` option to both the `signIn`, the `signOut` and the `getSession` method. This allows you to redirect a user to a certain pages, after they've completed the action. This can be useful when a user attempts to open a page (`/protected`) but has to go through external authentication (e.g., via their google account) first. - -You can use it like: -```ts -await signIn({ callbackUrl: '/protected' }) -``` - -to redirect the user to the protected page they wanted to access _after_ they've been authenticated. - -You can do the same for signing out the user: -```ts -await signOut({ callbackUrl: '/protected' }) -``` - -E.g., to redirect the user away from the already loaded, protected, page after signout (else, you will have to handle the redirect yourself). - -You may also pass specify a callback for the `getSession` utility: -```ts -await getSession({ callbackUrl: '/protected' }) -``` diff --git a/docs/content/v0.5/3.application-side/3.custom-sign-in-page.md b/docs/content/v0.5/3.application-side/3.custom-sign-in-page.md deleted file mode 100644 index 5bba78ae..00000000 --- a/docs/content/v0.5/3.application-side/3.custom-sign-in-page.md +++ /dev/null @@ -1,110 +0,0 @@ -# Custom sign-in Page - -To create a custom sign-in page you will need to: -1. Create the custom sign-in page: Creating the actual page your user will enter their credentials on OR select their oauth provider (e.g., google, azure, ...) -2. Configure `nuxt-auth` to redirect to the custom sign-in page: If a sign-in is triggered or a session check fails, `nuxt-auth` has to forward you to your custom sign-in page, instead of the `nuxt-auth` builtin sign-in page -3. Optional: Disable the `nuxt-auth` global protection middleware for the custom page if you have it enabled - -## Create the Custom sign-in Page - -To create your custom sign-in page you can use `signIn` to directly start a provider-flow once the user selected it, e.g., by clicking on a button on your custom sign-in page. Here is a very simple sign-in page that either directly starts a github-oauth sign-in flow or directly signs in the user via the credentials flow: -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: -```vue - - - - -``` - -Note: -- In the above example `username` and `password` are hard-coded. In your own custom page, these two fields should probably come from inputs on your page. -- We disable the global `nuxt-auth` middleware for this page by using the [guest mode](/nuxt-auth/application-side/guest-mode) `definePageMeta({ auth: { unauthenticatedOnly: true, ... } })`, this is only necessary if you have set `globalAppMiddleware: true` in [the `nuxt-auth` module configuration](/nuxt-auth/configuration/nuxt-config) - -If you want to create a custom sign-in page that dynamically offers sign-in options based on your configured providers, you can call `getProviders()` first and then iterate over the supported providers to generate your sign-in page. - -::alert{type="info"} -Above we use the [guest mode](/nuxt-auth/application-side/guest-mode). This option was introduced in `nuxt-auth@0.5.0`. If you are on a previous version, use `auth: false` instead. This has the downside of not automatically redirecting authenticated users away from the login page. -:: - -## Configure `nuxt-auth` to redirect to the Custom sign-in Page - -Redirects to the sign-in page happen automatically, e.g., when a `getSession()` call fails to get a session or when `signIn()` is called. By default this redirect sends the user to `/api/auth/signin`. To use a custom sign-in page we will have to configure `nuxt-auth` to send the user to the custom sign-in page instead. - -We can apply this configuration in the `NuxtAuthHandler`: -```ts -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - pages: { - // Change the default behavior to use `/login` as the path for the sign-in page - signIn: '/login' - }, - providers: [ - // ... your provider configuration - ] -}) -``` - -We can also configure the default-location for other pages in the `pages` configuration, see [the NextAuth.js pages docs for more](https://next-auth.js.org/configuration/pages). - -## Disable the global page protection middleware - -As already outlined in the first step, you will need to add: -```ts -definePageMeta({ - auth: { - unauthenticatedOnly: true, - navigateAuthenticatedTo: '/', - } -}) -``` -to your login page if you have enabled the [global page protection middleware](/nuxt-auth/application-side/protecting-pages). This is the [guest mode](/nuxt-auth/application-side/guest-mode) and allows unauthenticated users to access the login-page, while authenticated users will be redirected to `/`. Not disabling the global middleware would result in an endless loop of redirects. You can change `navigateAuthenticatedTo` to any route you like, e.g., `/profile` to show authenticated users their profile instead. - -If you have not set `globalAppMiddleware` or have set it to `false` this step does not apply to your application. - -::alert{type="info"} -Above we use the [guest mode](/nuxt-auth/application-side/guest-mode). This option was introduced in `nuxt-auth@0.5.0`. If you are on a previous version, use `auth: false` instead. This has the downside of not automatically redirecting authenticated users away from the login page. -:: - -## Optional: Custom Error handling - -You can handle sign-in errors yourself by calling `signIn` with `redirect: false` and inspecting its result for errors. For example: -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: -```ts -const { signIn } = useAuth() - -const mySignInHandler = async ({ username, password }: { username: string, password: string }) => { - const { error, url } = await signIn('credentials', { username, password, redirect: false }) - - if (error) { - // Do your custom error handling here - alert('You have made a terrible mistake while entering your credentials') - } else { - // No error, continue with the sign in, e.g., by following the returned redirect: - return navigateTo(url, { external: true }) - } -} -``` - -Then call the `mySignInHandler({ username, password })` on login instead of the default `signIn(...)` method. You can find [all possible errors here](https://github.com/nextauthjs/next-auth/blob/aad0b8db0e8a163b3c3ae7dec3e9158e20d368f4/packages/next-auth/src/core/pages/signin.tsx#L4-L19). This file also contains the default error-messages that `nuxt-auth` would show to the user if you would not handle the error manually using `redirect: false`. diff --git a/docs/content/v0.5/3.application-side/4.protecting-pages.md b/docs/content/v0.5/3.application-side/4.protecting-pages.md deleted file mode 100644 index f8e5128c..00000000 --- a/docs/content/v0.5/3.application-side/4.protecting-pages.md +++ /dev/null @@ -1,149 +0,0 @@ -# Protecting Pages - -`nuxt-auth` offers different approaches to protect pages: - -1. Global protection: Protects all pages with manual exceptions -2. Local protection: Protects specific pages -3. Custom middleware: Create your own middleware - -Briefly summarized, you can enable global protection (1) in your `nuxt.config.ts`: -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - globalAppMiddleware: true - } -}) -``` - -Now *all pages* will require sign-in. Learn how to add excepted pages [below](/nuxt-auth/application-side/protecting-pages#disabling-the-global-middleware-locally) - -To enable page-local protection (2), add the following `definePageMeta` directive to a page: -```vue - - - - -``` - -You cannot mix approach (1) and (2). So, if the global middleware is enabled, you cannot additionally add another protection middleware to a specific page. - -## Global middleware - -To create a global authentication middleware that ensures that your user is authenticated no matter which page they visit, you configure `nuxt-auth` as follows: -```ts -export default defineNuxtConfig({ - modules: ['@sidebase/nuxt-auth'], - auth: { - globalAppMiddleware: true - } -}) -``` - -That's it! Every page of your application will now need authentication for the user to visit it. - -### Disabling the global middleware locally - -To disable the global middleware on a specific page only, you can use the [`definePageMeta` macro](https://nuxt.com/docs/api/utils/define-page-meta#definepagemeta) to turn `auth` off: - -```vue - - - - -``` - -Note: This only works on `pages/`. It notably does not work inside the `app.vue`. - -## Local middleware - -To protect specific pages with a middleware, you can use the [`definePageMeta` macro](https://nuxt.com/docs/api/utils/define-page-meta#definepagemeta) to turn `auth` on: -```vue - - - - -``` - -Note: You cannot use local protection when you turned on the global middleware by setting `globalAppMiddleware: true` in the `nuxt-auth` configuration. You will get an error along the lines of "Error: Unknown route middleware: 'auth'". This is because the `auth` middleware is then added globally and not available to use as a local, page-specific middleware. - -## Custom middleware - -You may create your own application-side middleware in order to implement custom, more advanced authentication logic. - -::alert{type="warning"} -Creating a custom middleware is an advanced, experimental option and may result in unexpected or undesired behavior if you are not familiar with advanced Nuxt 3 concepts. -:: - -To implement your custom middleware: -1. Create an application-side middleware that applies either globally or is named (see [the Nuxt docs for more](https://nuxt.com/docs/guide/directory-structure/middleware#middleware-directory)) -2. Add logic based on `useAuth` to it - -When adding the logic, you need to watch out when calling other `async` composable functions. This can lead to `context`-problems in Nuxt, see [the explanation for this here](https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529). In order to avoid these problems, you will need to either: -- use the undocumented `callWithNuxt` utility when `await`ing other composables, -- return an async function where possible instead of `await`ing it to avoid `callWithNuxt` - -Following are examples of both kinds of usage: -::code-group -```ts [direct return] -// file: ~/middleware/authentication.global.ts -export default defineNuxtRouteMiddleware((to) => { - const { status, signIn } = useAuth() - - // Return immediately if user is already authenticated - if (status.value === 'authenticated') { - return - } - - /** - * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529 - * - * So to avoid calling it, we return it immediately. - */ - return signIn(undefined, { callbackUrl: to.path }) as ReturnType -}) -``` -```ts [callWithNuxt] -// file: ~/middleware/authentication.global.ts -import { useNuxtApp } from '#imports' -import { callWithNuxt } from '#app/nuxt' - -export default defineNuxtRouteMiddleware((to) => { - // It's important to do this as early as possible - const nuxtApp = useNuxtApp() - - const { status, signIn } = useAuth() - - // Return immediately if user is already authenticated - if (status.value === 'authenticated') { - return - } - - /** - * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529 - * - * So to avoid calling it, we call it via `callWithNuxt`. - */ - await callWithNuxt(nuxtApp, signIn, [undefined, { callbackUrl: to.path }]) -}) -``` -:: -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: - -## Guest Mode - -Since v0.5.0 `nuxt-auth` also offers a guest mode. Checkout the [guest mode](/nuxt-auth/application-side/guest-mode) documentation. diff --git a/docs/content/v0.5/3.application-side/5.guest-mode.md b/docs/content/v0.5/3.application-side/5.guest-mode.md deleted file mode 100644 index 585eaa4a..00000000 --- a/docs/content/v0.5/3.application-side/5.guest-mode.md +++ /dev/null @@ -1,40 +0,0 @@ -# Guest Mode - -::alert{type="success"} -This feature was added in v0.5.0 of `nuxt-auth`. -:: - -You can use `nuxt-auth` to setup pages that are accessible only when the user is **not logged in**. This is sometimes called "guest mode". The behavior of such a page is as follows: -- A logged in user visits the page -> redirect to another (likely protected) page, -- A logged out user visits the page -> they are allowed to stay and view it - -This behavior is useful for login pages that you don't want to be visitable by logged in users: Why should they go through a login flow again? - -## Usage - -Briefly summarized, you can enable guest mode on a page by passing the following configuration: -::code-group -```ts [Global middleware enabled] -definePageMeta({ - auth: { - unauthenticatedOnly: true, - navigateAuthenticatedTo: '/profile', - }, -}) -``` -```ts [Global middleware disabled] -definePageMeta({ - middleware: 'auth', - auth: { - unauthenticatedOnly: true, - navigateAuthenticatedTo: '/profile', - }, -}) -``` -:: - -The above will: -- allow only guest (== users that are not logged in) to visit the page -- redirect everyone who is already logged in to `/profile` - -Note: Setting `unauthenticatedOnly: false` above is equivalent to setting `auth: false` from the user-perspective, but requires some extra middleware-steps, so is a bit less efficient. Therefore it is recommended to always use `auth: false` instead. diff --git a/docs/content/v0.5/3.application-side/_dir.yml b/docs/content/v0.5/3.application-side/_dir.yml deleted file mode 100644 index 201d148b..00000000 --- a/docs/content/v0.5/3.application-side/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Application-Side -icon: heroicons-outline:computer-desktop diff --git a/docs/content/v0.5/4.server-side/1.index.md b/docs/content/v0.5/4.server-side/1.index.md deleted file mode 100644 index 034af827..00000000 --- a/docs/content/v0.5/4.server-side/1.index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: "Server-side usage of `nuxt-auth` for Vue / Nuxt 3 apps." ---- - -# Overview - -On the server-side this module offers: -::list{type="success"} -- [Session Access and Route Protection](/nuxt-auth/server-side/session-access-and-route-protection) -- [JWT Access](/nuxt-auth/server-side/jwt-access) -- [REST API](/nuxt-auth/server-side/rest-api) -:: - -Server-side usage refers to any code that only runs on the server, e.g., in your `~/server/` directory, read the [glossary](/nuxt-auth/resources/glossary) for more. diff --git a/docs/content/v0.5/4.server-side/2.session-access-and-route-protection.md b/docs/content/v0.5/4.server-side/2.session-access-and-route-protection.md deleted file mode 100644 index 8697d6b4..00000000 --- a/docs/content/v0.5/4.server-side/2.session-access-and-route-protection.md +++ /dev/null @@ -1,50 +0,0 @@ -# Session Access and Route Protection - -On the server side you can get access to the current session like this: -```ts -import { getServerSession } from '#auth' - -export default eventHandler(async (event) => { - const session = await getServerSession(event) -}) -``` - -This is inspired by [the `getServerSession`](https://next-auth.js.org/tutorials/securing-pages-and-api-routes#securing-api-routes) of NextAuth.js. It also avoids an external, internet call to the `GET /api/auth/sessions` endpoint, instead directly calling a pure JS-method. - -Note: If you use [Nuxts' `useFetch`](https://nuxt.com/docs/api/composables/use-fetch) from your app-components to fetch data from an endpoint that uses `getServerSession` or [`getToken`](/nuxt-auth/server-side/jwt-access) you will need to manually pass along cookies as [Nuxt 3 universal rendering](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering) will not do this per-default when it runs on the server-side. Not passing along cookies will result in `getServerSession` returning `null` when it is called from the server-side as no auth-cookies will exist. Here's an example that manually passes along cookies: -```ts -const headers = useRequestHeaders(['cookie']) as HeadersInit -const { data: token } = await useFetch('/api/token', { headers }) -``` - -## Endpoint Protection - -To protect an endpoint, check the session after fetching it: -```ts -// file: ~/server/api/protected.get.ts -import { getServerSession } from '#auth' - -export default eventHandler(async (event) => { - const session = await getServerSession(event) - if (!session) { - return { status: 'unauthenticated!' } - } - return { status: 'authenticated!' } -}) - -``` - -## Server Middleware - -You can also use this in a [Nuxt server middleware](https://nuxt.com/docs/guide/directory-structure/server#server-middleware) to protect multiple pages at once and keep the authentication logic out of your endpoints: -```ts -// file: ~/server/middleware/auth.ts -import { getServerSession } from '#auth' - -export default eventHandler(async (event) => { - const session = await getServerSession(event) - if (!session) { - throw createError({ statusMessage: 'Unauthenticated', statusCode: 403 }) - } -}) -``` diff --git a/docs/content/v0.5/4.server-side/3.jwt-access.md b/docs/content/v0.5/4.server-side/3.jwt-access.md deleted file mode 100644 index ecc6e345..00000000 --- a/docs/content/v0.5/4.server-side/3.jwt-access.md +++ /dev/null @@ -1,44 +0,0 @@ -# JWT Access - -Getting the (decoded) JWT token of the current user can be helpful, e.g., to use it to access an external api that requires this token for authentication or authorization. - -You can get the JWT token that was passed along with the request using `getToken`: -```ts -// file: ~/server/api/token.get.ts -import { getToken } from '#auth' - -export default eventHandler(async (event) => { - const token = await getToken({ event }) - - return token || 'no token present' -}) -``` - -The function behaves identical to the [`getToken` function from NextAuth.js](https://next-auth.js.org/tutorials/securing-pages-and-api-routes#using-gettoken) with one change: you have to pass in the h3-`event` instead of `req`. This is due to how cookies can be accessed on h3: not via `req.cookies` but rather via `useCookies(event)`. - -You do not need to pass in any further parameters like `secret`, `secureCookie`, ... They are automatically inferred to the values you configured if not set and reading the token will work out of the box. You _may_ pass these options, e.g., to get the raw, encoded JWT token you can pass `raw: true`. - -## Application-side JWT token access - -To access the JWT token application-side, e.g., in a `.vue` page, you can create an endpoint like this: -```ts -// file: ~/server/api/token.get.ts -import { getToken } from '#auth' - -export default eventHandler(event => getToken({ event })) -``` - -Then from your application-side code you can fetch it like this: -```vue -// file: app.vue - - - -``` - -Note that you have to pass the cookie-header manually. You also have to pass it using [`useRequestHeaders`](https://nuxt.com/docs/api/composables/use-request-headers/) so that the cookies are also correctly passed when this page is rendered server-side during the [universal-rendering process](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering). diff --git a/docs/content/v0.5/4.server-side/4.rest-api.md b/docs/content/v0.5/4.server-side/4.rest-api.md deleted file mode 100644 index 863ca7e9..00000000 --- a/docs/content/v0.5/4.server-side/4.rest-api.md +++ /dev/null @@ -1,20 +0,0 @@ -# REST API - -All endpoints that NextAuth.js supports are also supported by `nuxt-auth`: - -| Endpoint | Request | -|--------------------------------|:-------------| -| `${basePath}/signin` | `GET` | -| `${basePath}/signin/:provider` | `POST` | -| `${basePath}/callback/:provider` | `GET` `POST` | -| `${basePath}/signout` | `GET` `POST` | -| `${basePath}/session` | `GET` | -| `${basePath}/csrf` | `GET` | -| `${basePath}/providers` | `GET` | - -The `basePath` is `/api/auth` per default and [can be configured in the `nuxt.config.ts`](/nuxt-auth/configuration/nuxt-config). - -You can directly interact with these API endpoints if you wish to, it's probably a better idea to use `useAuth` where possible though. [See the full rest API documentation of NextAuth.js here](https://next-auth.js.org/getting-started/rest-api). -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: diff --git a/docs/content/v0.5/4.server-side/_dir.yml b/docs/content/v0.5/4.server-side/_dir.yml deleted file mode 100644 index 06a72e86..00000000 --- a/docs/content/v0.5/4.server-side/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Server-Side -icon: heroicons-outline:computer-desktop diff --git a/docs/content/v0.5/5.recipes/1.index.md b/docs/content/v0.5/5.recipes/1.index.md deleted file mode 100644 index 0113b4b2..00000000 --- a/docs/content/v0.5/5.recipes/1.index.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -description: "`nuxt-auth` recipes on how to use Strapi, Directus, expand user session data and more for Vue / Nuxt 3 apps." ---- -# Overview - -The following pages contain some recipes for commonly asked patterns, questions and implementations. The recipes are mostly provided by the community and can serve as guidelines to implement something similar in your Nuxt 3 application. The recipes are not all tested through by the sidebase team. If you have any concerns, questions or improvement proposals or want to contribute a recipe yourself, we'd be very happy if you open an issue on our repository: https://github.com/sidebase/nuxt-auth/issues/new/choose - -Thanks to everybody who contributed so far ❤️ diff --git a/docs/content/v0.5/5.recipes/2.strapi.md b/docs/content/v0.5/5.recipes/2.strapi.md deleted file mode 100644 index f272bf53..00000000 --- a/docs/content/v0.5/5.recipes/2.strapi.md +++ /dev/null @@ -1,79 +0,0 @@ -# Strapi - -This section gives an example of how the `NuxtAuthHandler` can be configured to use Strapi JWTs for authentication via the `CredentialsProvider` provider. - -You have to configure the following places to make `nuxt-auth` work with Strapi: -- `STRAPI_BASE_URL` in `.env`: Add the Strapi environment variable to your .env file -- [`runtimeConfig.STRAPI_BASE_URL`-key in `nuxt.config.ts`](/nuxt-auth/configuration/nuxt-config): Add the Strapi base url env variable to the runtime config -- [`auth`-key in `nuxt.config.ts`](/nuxt-auth/configuration/nuxt-config): Configure the module itself, e.g., where the auth-endpoints are, what origin the app is deployed to, ... -- [NuxtAuthHandler](/nuxt-auth/configuration/nuxt-auth-handler): Configure the authentication behavior, e.g., what authentication providers to use - -For a production deployment, you will have to at least set the: -- `STRAPI_BASE_URL` Strapi base URL for all API endpoints by default http://localhost:1337 - -1. Create a `.env` file with the following lines: -```env -// Strapi v4 url, out of the box -ORIGIN=http://localhost:3000 -NUXT_SECRET=a-not-so-good-secret -STRAPI_BASE_URL=http://localhost:1337/api -``` - -2. Set the following options in your `nuxt.config.ts`: -```ts -export default defineNuxtConfig({ - runtimeConfig: { - // The private keys which are only available server-side - NUXT_SECRET: process.env.NUXT_SECRET, - // Default http://localhost:1337/api - STRAPI_BASE_URL: process.env.STRAPI_BASE_URL, - }, - auth: { - origin: process.env.ORIGIN, - }, -}); -``` - -3. Create the catch-all `NuxtAuthHandler` and add the this custom Strapi credentials provider: -```ts -// file: ~/server/api/auth/[...].ts -import CredentialsProvider from "next-auth/providers/credentials"; -import { NuxtAuthHandler } from "#auth"; -const config = useRuntimeConfig() - -export default NuxtAuthHandler({ - secret: config.NUXT_SECRET, - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - name: "Credentials", - credentials: {}, // Object is required but can be left empty. - async authorize(credentials: any) { - const response = await $fetch( - `${config.STRAPI_BASE_URL}/auth/local/`, - { - method: "POST", - body: JSON.stringify({ - identifier: credentials.username, - password: credentials.password, - }), - } - ); - - if (response.user) { - const u = { - id: response.id, - name: response.user.username, - email: response.user.email, - }; - return u; - } else { - return null - } - }, - }), - ] -}); -``` - -Checkout this blog-post for further notes and explanation: https://darweb.nl/foundry/article/nuxt3-sidebase-strapi-user-auth diff --git a/docs/content/v0.5/5.recipes/3.directus.md b/docs/content/v0.5/5.recipes/3.directus.md deleted file mode 100644 index 6016d7e3..00000000 --- a/docs/content/v0.5/5.recipes/3.directus.md +++ /dev/null @@ -1,201 +0,0 @@ -# Directus - -This section gives an example of how the `NuxtAuthHandler` can be configured to use Directus JWTs for authentication via the `CredentialsProvider` provider and how to implement a token refresh for the Directus JWT. - -The below is a code-example that needs to be adapted to your specific configuration: -```ts -import CredentialsProvider from "next-auth/providers/credentials"; -import { NuxtAuthHandler } from "#auth"; - -/** - * Takes a token, and returns a new token with updated - * `accessToken` and `accessTokenExpires`. If an error occurs, - * returns the old token and an error property - */ -async function refreshAccessToken(refreshToken: { - accessToken: string; - accessTokenExpires: string; - refreshToken: string; -}) { - try { - console.warn("trying to post to refresh token"); - - const refreshedTokens = await $fetch<{ - data: { - access_token: string; - expires: number; - refresh_token: string; - }; - } | null>("https://domain.directus.app/auth/refresh", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: { - refresh_token: refreshToken.refreshToken, - mode: "json", - }, - }); - - if (!refreshedTokens || !refreshedTokens.data) { - console.warn("No refreshed tokens"); - throw refreshedTokens; - } - - console.warn("Refreshed tokens successfully"); - return { - ...refreshToken, - accessToken: refreshedTokens.data.access_token, - accessTokenExpires: Date.now() + refreshedTokens.data.expires, - refreshToken: refreshedTokens.data.refresh_token, - }; - } catch (error) { - console.warn("Error refreshing token", error); - return { - ...refreshToken, - error: "RefreshAccessTokenError", - }; - } -} - -export default NuxtAuthHandler({ - // secret needed to run nuxt-auth in production mode (used to encrypt data) - secret: process.env.NUXT_SECRET, - - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - // The name to display on the sign in form (e.g. 'Sign in with...') - name: "Credentials", - // The credentials is used to generate a suitable form on the sign in page. - // You can specify whatever fields you are expecting to be submitted. - // e.g. domain, username, password, 2FA token, etc. - // You can pass any HTML attribute to the tag through the object. - credentials: { - email: { label: "Email", type: "text" }, - password: { label: "Password", type: "password" }, - }, - async authorize(credentials: any) { - // You need to provide your own logic here that takes the credentials - // submitted and returns either a object representing a user or value - // that is false/null if the credentials are invalid. - // NOTE: THE BELOW LOGIC IS NOT SAFE OR PROPER FOR AUTHENTICATION! - - try { - const payload = { - email: credentials.email, - password: credentials.password, - }; - - const userTokens = await $fetch<{ - data: { access_token: string; expires: number; refresh_token: string }; - } | null>("https://domain.directus.app/auth/login", { - method: "POST", - body: payload, - headers: { - "Content-Type": "application/json", - "Accept-Language": "en-US", - }, - }); - - const userDetails = await $fetch<{ - data: { - id: string; - email: string; - first_name: string; - last_name: string; - role: string; - phone?: string; - cvr?: string; - company_name?: string; - }; - } | null>("https://domain.directus.app/users/me", { - method: "GET", - headers: { - "Content-Type": "application/json", - "Accept-Language": "en-US", - Authorization: `Bearer ${userTokens?.data?.access_token}`, - }, - }); - - if (!userTokens || !userTokens.data || !userDetails || !userDetails.data) { - throw createError({ - statusCode: 500, - statusMessage: "Next auth failed", - }); - } - - const user = { - id: userDetails.data.id, - email: userDetails.data.email, - firstName: userDetails.data.first_name, - lastName: userDetails.data.last_name, - role: userDetails.data.role, - phone: userDetails.data.phone, - cvr: userDetails.data.cvr, - companyName: userDetails.data.company_name, - accessToken: userTokens.data.access_token, - accessTokenExpires: Date.now() + userTokens.data.expires, - refreshToken: userTokens.data.refresh_token, - }; - - const allowedRoles = [ - "53ed3a6a-b236-49aa-be72-f26e6e4857a0", - "d9b59a92-e85d-43e2-8062-7a1242a8fce6", - ]; - - // Only allow admins and sales - if (!allowedRoles.includes(user.role)) { - throw createError({ - statusCode: 403, - statusMessage: "Not allowed", - }); - } - - return user; - } catch (error) { - console.warn("Error logging in", error); - - return null; - } - }, - }), - ], - - session: { - strategy: "jwt", - }, - - callbacks: { - async jwt({ token, user, account }) { - if (account && user) { - console.warn("JWT callback", { token, user, account }); - return { - ...token, - ...user, - }; - } - - // Handle token refresh before it expires of 15 minutes - if (token.accessTokenExpires && Date.now() > token.accessTokenExpires) { - console.warn("Token is expired. Getting a new"); - return refreshAccessToken(token); - } - - return token; - }, - async session({ session, token }) { - session.user = { - ...session.user, - ...token, - }; - - return session; - }, - }, -}); -``` - -This was contributes by [@madsh93 from Github](https://github.com/madsh93) here: -- Github Comment: https://github.com/sidebase/nuxt-auth/issues/64#issuecomment-1330308402 -- Gist: https://gist.github.com/madsh93/b573b3d8f070e62eaebc5c53ae34e2cc diff --git a/docs/content/v0.5/5.recipes/4.custom-session-data.md b/docs/content/v0.5/5.recipes/4.custom-session-data.md deleted file mode 100644 index 5a128b45..00000000 --- a/docs/content/v0.5/5.recipes/4.custom-session-data.md +++ /dev/null @@ -1,111 +0,0 @@ -# Custom Session Data - -This guide explains how to add custom data to the user session. - -To expand / change / customize the session data you need to to follow the following steps: -1. Return the custom data in the `authorize` function -2. Add callbacks to the `NuxtAuthProvider` to alter the session data - -In the following example we'll add 2 new fields to the session: `accessToken` and `role`. - -## Return the custom data in the Authorize function - - -```ts -// file: ~/server/api/auth/[...].ts - -export default NuxtAuthHandler({ - secret: config.NUXT_SECRET, - providers: [ - // @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point - CredentialsProvider.default({ - ... - async authorize(credentials: any) { - const data = { - user: { - name: 'John Doe', - email: 'john@email.com', - id: 1, - role: 'admin' - }, - token: '123456890', - } - - if (data.user) { - const u = { - id: data.user.id, - name: data.user.username, - email: data.user.email, - access_token: data.token, // additional field - role: response.user.role // additional field - }; - return u; - } else { - throw createError({ - statusCode: 403, - statusMessage: "Credentials not working", - }); - } - }, - }) - ] - ... // other config - -``` -We can see the new fields in the return object `u` `role` and `access_token`. Now we need to pass them in the callbacks. - -## Add callbacks to the `NuxtAuthProvider` to alter the session data - -First, you must add jwt and session callbacks to [the `NuxtAuthHandler`](/nuxt-auth/configuration/nuxt-auth-handler): -```ts -// file: ~/server/api/auth/[...].ts -export default NuxtAuthHandler({ - // ... remainder of your config ... - callbacks: { - // Callback when the JWT is created / updated, see https://next-auth.js.org/configuration/callbacks#jwt-callback - jwt: async ({token, user}) => { - const isSignIn = user ? true : false; - if (isSignIn) { - token.jwt = user ? (user as any).access_token || '' : ''; - token.id = user ? user.id || '' : ''; - token.role = user ? (user as any).role || '' : ''; - } - return Promise.resolve(token); - }, - // Callback whenever session is checked, see https://next-auth.js.org/configuration/callbacks#session-callback - session: async ({session, token}) => { - (session as any).role = token.role; - (session as any).uid = token.id; - return Promise.resolve(session); - }, - }, - ..., // other config - providers: [...], // Your provider config -}) -``` -Notice that in the `jwt` callback we pass both the `access_token` and the `role` but in the session callback we leave out the `access_token` and we only pass the role since this would expose the token on the frontend through the session object. Exposing the `access_token` is not recommended, but you can pass it to the session if needed. - -After that you can access the new fields in the data portion of the session object: - -::alert{type="info"} -Prior to v0.5.0 `useAuth()` was called `useSession()`. -:: -```ts -const { status, data } = useAuth(); - -console.log(data); - -``` - -output: -``` -{ "user": { "name": "xxxxxxx", "email": "xxxxx@xxxxx.gr" }, "expires": "2023-02-28T20:36:12.584Z", "role": "admin", "uid": 1 } -``` - - -With this approach `data` will automatically be correctly typed. - - -Thanks to -- [@JoaoPedroAS51 from Github](https://github.com/JoaoPedroAS51) for his contribution here: https://github.com/sidebase/nuxt-auth/issues/61#issuecomment-1330747199 -- [@pvlastaridis from Github](https://github.com/pvlastaridis) for his contribution here: https://github.com/sidebase/nuxt-auth/issues/148#issuecomment-1407490921 diff --git a/docs/content/v0.5/5.recipes/5.nuxt-auth-example.md b/docs/content/v0.5/5.recipes/5.nuxt-auth-example.md deleted file mode 100644 index 66493e1d..00000000 --- a/docs/content/v0.5/5.recipes/5.nuxt-auth-example.md +++ /dev/null @@ -1,6 +0,0 @@ -# Nuxt Auth Example Code - -Minimal example of using `nuxt-auth` for http://nuxt-auth-example.sidebase.io/. See the repo [here](https://github.com/sidebase/nuxt-auth-example). - -::sandbox{repo="sidebase/nuxt-auth-example" branch="main" dir="/" file="app.vue"} -:: diff --git a/docs/content/v0.5/5.recipes/6.laravel-passport.md b/docs/content/v0.5/5.recipes/6.laravel-passport.md deleted file mode 100644 index 0b4456c9..00000000 --- a/docs/content/v0.5/5.recipes/6.laravel-passport.md +++ /dev/null @@ -1,119 +0,0 @@ -# Laravel Passport - -This section gives an example of how the `NuxtAuthHandler` can be configured to use Laravel Passport Oauth2 and SSO. - -## 1. Creating an API client on Laravel Passport - -You can refer to the official [Laravel documentation](https://laravel.com/docs/10.x/passport#managing-clients) to add new client to Passport. - -By default, you can simply create one using the command: - -```sh -php artisan passport:client -``` - -It will ask you to choose a -- `client ID`, and -- a `redirect URI`. - -Keep the client ID for the next step and set the redirect URI to `http://localhost:3000/api/auth/callback/laravelpassport` (default value for dev environement, modify it according to your environement, you can add several URI comma separated). - -## 2. Add a Laravel API route returning the user data - -Next create a route that is returned to the user. In the example given here, we will use `/api/v1/me`. - -The route will return the field of your user data. You **must** return a field with the key `id`. - -## 3. Setting configuration and the provider - -### 3.1. Storing the config in your .env - -You can add the following variables to your .env: -- `PASSPORT_BASE_URL`: the URL of your passport APP -- `PASSPORT_CLIENT_ID`: the client ID you set in the previous step -- `PASSPORT_CLIENT_SECRET`: the client secret Laravel generated for you at the end of step 1 - -```bash -# .env -PASSPORT_BASE_URL=http://www.my_passport_app.test -PASSPORT_CLIENT_ID=123456789 -PASSPORT_CLIENT_SECRET=123456789 -``` - -### 3.2. Adding your config to the runtimeConfig - -Then add these values to your runtimeConfig: - -```ts -// ~/nuxt.config.ts -export default defineNuxtConfig({ - //... - modules: [ - //... - '@sidebase/nuxt-auth', - ], - runtimeConfig: { - //... - passport: { - baseUrl: process.env.PASSPORT_BASE_URL, - clientId: process.env.PASSPORT_CLIENT_ID, - clientSecret: process.env.PASSPORT_CLIENT_SECRET, - } - - }, -}); -``` - -### 2.3. Create the catch-all `NuxtAuthHandler` and add the this custom provider: - -```ts -// ~/server/api/auth/[...].ts -import {NuxtAuthHandler} from '#auth' -const {passport} = useRuntimeConfig(); //get the values from the runtimeConfig - -export default NuxtAuthHandler({ - //... - providers: [ - { - id: "laravelpassport", //ID is only used for the callback URL - name: "Passport", // name is used for the login button - type: "oauth", // connexion type - version: "2.0",// oauth version - authorization: { - url: `${passport.baseUrl}/oauth/authorize`, // this is the route created by passport by default to get the autorization code - params: { - scope: "*", // this is the wildcard for all scopes in laravel passport, you can specify scopes separated by a space - } - }, - token: { - url: `${passport.baseUrl}/oauth/token`, // this is the default route created by passport to get and renew the tokens - }, - clientId: passport.clientId, // the client Id - clientSecret: passport.clientSecret,// the client secret - userinfo: { - url: `${passport.baseUrl}/api/v1/me`,// this is a custom route that must return the current user that must be created in laravel - }, - profile: (profile) => { - // map the session fields with you laravel fields - // profile is the user coming from the laravel app - // update the return with your own fields names - return { - id: profile.id, - name: profile.username, - email: profile.email, - image: profile.image, - }; - }, - idToken: false, - } - ], -}); -``` - -## 4. Reference - -You can find the full discussion in the issue [#149](https://github.com/sidebase/nuxt-auth/issues/149) - -## 5. Credits - -Solution provided by [@Jericho1060](https://github.com/Jericho1060) diff --git a/docs/content/v0.5/5.recipes/7.vercel-deployment.md b/docs/content/v0.5/5.recipes/7.vercel-deployment.md deleted file mode 100644 index a71d39cd..00000000 --- a/docs/content/v0.5/5.recipes/7.vercel-deployment.md +++ /dev/null @@ -1,43 +0,0 @@ -# Vercel Deployment - -This section gives an example of how to deploy `nuxt-auth` to Vercel. The deployed application used in this example is the offical [nuxt-auth-example](https://github.com/sidebase/nuxt-auth-example/). - -You can find a deployed example of the `nuxt-auth-example` [here](https://nuxt-auth-example.vercel.app/). - -## Cloning project to Vercel - -Begin by creating a new project in your Vercel dashboard. In this example we will be deploying from a GitHub repository. Select your `nuxt-auth` enabled project and import it. - -![Clone repository in Vercel](/nuxt-auth/vercel/create-project.png) - -## Setting environment variables - -In order to run the `nuxt-auth-example` the following environment variables are required. These may however change depending on your project. - -![Env variables](/nuxt-auth/vercel/env.png) - -### NUXT_SECRET - - When deploying `nuxt-auth` to Vercel you cannot set a random string as your nuxt secret. You must use a 32-bit generated secret. You can use [this](https://generate-secret.vercel.app/32) website to generate a custom secret. - -### ORIGIN - -In order to set the correct origin go into your Vercel project settings and navigate to the `Domains` tab. Once there you will all the assigned domains and their assigned environment (production, dev). - -Copy the correct domain for every enviroment and assign the environment variables to match the correct environment. - -### Github_* - -Assign your generated GitHub 0Auth application id and secret. When generating your 0Auth application set the homepage URL to your vercel domain and the callback url to: - -https://YOUR_VERCEL_APP.vercel.com/api/auth/callback/github - -## Final steps - -You can now re-deploy your add to vercel. Ensure that all the enviroment variables and domains match your enviroment to ensure the correct data is passed. - -You can set the environment of a deployment to production by right clicking it and selecting, promote to production. - -![Promote to Production](/nuxt-auth/vercel/promote_production.png) - -You can clone the repository used in this example [here](https://github.com/sidebase/nuxt-auth-example/) to deploy your own version of `nuxt-auth` to Vercel! diff --git a/docs/content/v0.5/5.recipes/8.azure-ad-example.md b/docs/content/v0.5/5.recipes/8.azure-ad-example.md deleted file mode 100644 index a9afc4bd..00000000 --- a/docs/content/v0.5/5.recipes/8.azure-ad-example.md +++ /dev/null @@ -1,84 +0,0 @@ -# Azure AD - -This section gives an example of a configuration of `NuxtAuthHandler` for use with Azure AD and shows an implemention of token refresh for Azure AD JWT. - -The code below is an example and you will need to adapt it to your own setup: -```js -import { NuxtAuthHandler } from '#auth'; -import AzureADProvider from 'next-auth/providers/azure-ad'; - -async function refreshAccessToken(accessToken) { - try { - const url = `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}/oauth2/v2.0/token`; - const req = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: - `grant_type=refresh_token` + - `&client_secret=${process.env.AZURE_AD_CLIENT_SECRET}` + - `&refresh_token=${accessToken.refreshToken}` + - `&client_id=${process.env.AZURE_AD_CLIENT_ID}`, - }); - - const res = await req.json(); - return { - ...accessToken, - accessToken: res.access_token, - accessTokenExpires: Date.now() + res.expires_in * 1000, - refreshToken: res.refresh_token ?? accessToken.refreshToken, // Fall back to old refresh token - }; - } catch (error) { - console.log(error); - - return { - ...accessToken, - error: 'RefreshAccessTokenError', - }; - } -} - -export default NuxtAuthHandler({ - secret: process.env.NUXT_AUTH_SECRET, - providers: [ - AzureADProvider.default({ - clientId: process.env.AZURE_AD_CLIENT_ID, - clientSecret: process.env.AZURE_AD_CLIENT_SECRET, - tenantId: process.env.AZURE_AD_TENANT_ID, - authorization: { - params: { - scope: `offline_access openid profile email ${process.env.AZURE_AD_CLIENT_ID}/access_as_user`, - }, - }, - }), - ], - callbacks: { - async jwt({ token, account, profile }) { - // Persist the access_token in the encrypted JWT. - if (account && profile) { - token.accessToken = account.access_token; - token.accessTokenExpires = account.expires_at * 1000; - token.refreshToken = account.refresh_token; - } - - if (Date.now() < token.accessTokenExpires) { - return token; - } - return refreshAccessToken(token); - }, - }, -}); -``` - -If you need the access token on the client side the code below can be added in the callbacks object. - -```js -async session({ session, token }) { - // Make access token available on the client. - session.accessToken = token.accessToken; - - return session; -}, - -``` diff --git a/docs/content/v0.5/5.recipes/_dir.yml b/docs/content/v0.5/5.recipes/_dir.yml deleted file mode 100644 index 70c9f704..00000000 --- a/docs/content/v0.5/5.recipes/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Recipes -icon: heroicons-outline:queue-list diff --git a/docs/content/v0.5/6.resources/0.index b/docs/content/v0.5/6.resources/0.index deleted file mode 100644 index 0b8c8337..00000000 --- a/docs/content/v0.5/6.resources/0.index +++ /dev/null @@ -1,3 +0,0 @@ -# Overview - -The following ressources should help with using nuxt-auth and tackle some topics that are frequently encountered during app-development. We will continue to expand this with more guides, recipes and references based on the questions, feedback and problems we see and receive from our community: https://discord.gg/VzABbVsqAc diff --git a/docs/content/v0.5/6.resources/1.external-blogs-guides-examples.md b/docs/content/v0.5/6.resources/1.external-blogs-guides-examples.md deleted file mode 100644 index 5517cb24..00000000 --- a/docs/content/v0.5/6.resources/1.external-blogs-guides-examples.md +++ /dev/null @@ -1,9 +0,0 @@ -# External Blogs, Guides and Examples - -This is a collection of resources that we'll expand over time: -- [Example of Nuxt 3, Strapi and `nuxt-auth`](https://darweb.nl/foundry/article/nuxt3-sidebase-strapi-user-auth) -- [Example NuxtAuthHandler configuration for Directus](https://gist.github.com/madsh93/b573b3d8f070e62eaebc5c53ae34e2cc) -- [How to expand the user data object](https://github.com/sidebase/nuxt-auth/issues/61#issuecomment-1330946022) -- [Docus NuxtAuthHandler that shows how to manually fetch an external JWT token, how to expand the user data and how to track session expiry manually](https://gist.github.com/madsh93/b573b3d8f070e62eaebc5c53ae34e2cc) - -Note: If you're looking to make something work in `nuxt-auth` the nice thing is that you can use most `NextAuth.js` resources to help you along the way, as the `NuxtAuthHandler` behaves identical to the [`NextAuth` handler](https://next-auth.js.org/configuration/initialization). \ No newline at end of file diff --git a/docs/content/v0.5/6.resources/2.glossary.md b/docs/content/v0.5/6.resources/2.glossary.md deleted file mode 100644 index 4e82048f..00000000 --- a/docs/content/v0.5/6.resources/2.glossary.md +++ /dev/null @@ -1,9 +0,0 @@ -# Glossary - -There are some terms we use in this documentation that may not be known to every reader. Here is an explanation for some of them: -- `application` / `application-side` / `universal-application`: This references all Nuxt code of your app that is [universally rendered](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering). In short this means that that code is rendered on the server-side and on the client-side, so all JS in it is executed twice. This is an important distinction, as some things may behave different on the server-side than on the client-side. We use `application...` to denote something that will be universally rendered -- `server` / `server-side`: This references all Nuxt code of your app that will run **only** on your server. For example, all code inside the `~/server` directory should only ever run on the server -- `sessions`: A set of information that persists for a longer duration, e.g., the username and email that persists while your user is logged in -- `authentication`: Verifying that someone is who they claims to be, e.g., by asking them for a username and password or by asking Google to verify it (OAuth) and then trust their result - -[Let us know if you're missing something from this list](https://github.com/sidebase/nuxt-auth/issues/new/choose) diff --git a/docs/content/v0.5/6.resources/3.security.md b/docs/content/v0.5/6.resources/3.security.md deleted file mode 100644 index 5d59eb3c..00000000 --- a/docs/content/v0.5/6.resources/3.security.md +++ /dev/null @@ -1,17 +0,0 @@ -# Security - -This section mostly contains a list of possible security problems. Note that the below flaws exist with many libraries and frameworks we use in our day-to-day when building and working with APIs. Even your vanilla Nuxt app already posesses some of these shortcoming. Missing in the below list are estimates of how likely it is that one of the list-items may occur and what impact it will have on your app. This is because that heavily depends on: -- your app: Are you building a fun project? A proof of concept? The next fort-nox money management app? -- your environment: Building a freely available app for fun? Have authentication in front of your app and trust all users that successfully authenticated? Superb! Don't trust anyone? Then please be extra-careful when using this library and when building you backend in general - -Without further ado, here's some attack cases you can consider and take action against. Neither the attack vectors, the problems or the mitigations are exhaustive: -1. sending arbitrary data: Denial-of-Service by server-resource exhaustion (bandwidth, cpu, memory), arbitrary code execution (if you parse the data), ... -2. creation arbitrarily many sessions: Denial-of-Service by server-resource exhaustion (bandwidth, cpu, memory) -3. guessing correct session ids: session data can leak -4. stealing session id(s) of client(s): session data can leak - -Read up how to mitigate these and more issues if you see fit. Checkout the [`nuxt-security`](https://github.com/Baroshem/nuxt-security) module that may help with some of these. - -## Disclosure - -A last reminder: This library was not written by crypto- or security-experts. Please proceed at your own risk, inspect the code if you want to and open issues / pull requests where you see room for improvement. If you want to file a security-concern privately, please send an email to `sidebase@sidestream.tech` with the subject saying "SECURITY nuxt-auth" and we'll look into your request ASAP. \ No newline at end of file diff --git a/docs/content/v0.5/6.resources/4.prior-work.md b/docs/content/v0.5/6.resources/4.prior-work.md deleted file mode 100644 index 6ae33fdf..00000000 --- a/docs/content/v0.5/6.resources/4.prior-work.md +++ /dev/null @@ -1,19 +0,0 @@ -# Prior Work and Module Concept - -The idea of this library is to re-use all the open-source implementation that already exist in the JS ecosystem instead of rolling our own. The idea was born when researching through the ecosystem of framework-specific authentication libraries to figure out what the best implementation approach for a state-of-the-art Nuxt 3 authentication library would be. - -During research it became clear that implementing everything from scratch will be: -- a lot of work that has already been open-sourced by others, -- error prone as authentication has a lot of intricacies that need to be resolved in order to get it right, -- hard to maintain as authentication providers come and go, -- hard to build initial trust for as authentication is important and cannot go wrong, - -In order to avoid these problems without taking forever (leaving Nuxt without an authentication library in the meantime), we decided to investigate if we can wrap [`NextAuth.js`](https://github.com/nextauthjs/next-auth), the most popular authentication library in the Next.js ecosystem by far and a trusted, well maintained one at that! - -In our investigation we found prior attempts to make NextAuth.js framework agnostic. These have more or less come to fruition, so far mostly resulting in some PoCs and example apps. Looking at these was quite helpful to get started. In particular, big pushes in the right direction came from: -- [NextAuth.js app examples](https://github.com/nextauthjs/next-auth/tree/main/apps) -- [Various comments, proposals, ... of this thread](https://github.com/nextauthjs/next-auth/discussions/3942), special thanks to @brillout for starting the discussion, @balazsorban44 for NextAuth.js and encouraging the discussion, @wobsoriano for adding PoCs for multiple languages - -The main part of the work was to piece everything together, resolve some outstanding issues with existing PoCs, add new things where nothing existed yet, e.g., for the `useAuth` composable by going through the NextAuth.js client code and translating it to a Nuxt 3 approach. - -The module had another big iteration in collaboration with @JoaoPedroAS51 to make `useAuth` a sync operation and trigger the session lifecycle from a plugin rather than the `useAuth` composable itself. diff --git a/docs/content/v0.5/6.resources/5.errors.md b/docs/content/v0.5/6.resources/5.errors.md deleted file mode 100644 index 2417533d..00000000 --- a/docs/content/v0.5/6.resources/5.errors.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: "A list of errors that `nuxt-auth` throws, what they mean and how you can resolve them." ---- -# Errors and Warnings - -This is a list of errors & warnings that `nuxt-auth` throws, what each of them means and how you can resolve them. - -## AUTH_NO_SECRET - -`AUTH_NO_SECRET` will appear as a warning message during development and be thrown as an error that stops the application during production. It is safe to ignore the development warning - it is only meant as a heads-up for your later production-deployment. `AUTH_NO_SECRET` occurs when no `secret` was set inside the `NuxtAuthHandler`: -```ts -// file: ~/server/api/auth/[...].ts -import { NuxtAuthHandler } from '#auth' - -export default NuxtAuthHandler({ - secret: 'my-superb-secret' // <--- !!!! THIS IS WHAT'S MISSING - - // ... rest of your config -}) -``` - -The `secret` is important to encode the cookies / tokens of your app-users. It is mandatory for production and should be something random, long and secret that only you and priviledged personnal knows about. In development it can be omitted and will be replaced with a fake-value. To be clear: This fake value is insecure and should never be used in production. - -Read [the `NuxtAuthHandler` docs for more information and different options you can use to set the `secret` at runtime](/nuxt-auth/configuration/nuxt-auth-handler). - -## AUTH_NO_ORIGIN - -`AUTH_NO_ORIGIN` will appear as a warning message during development and be thrown as an error that stops the application during production. It is safe to ignore the development warning - it is only meant as a heads-up for your later production-deployment. `AUTH_NO_ORIGIN` occurs when the origin of your application was not set. `nuxt-auth` tries to find the origin of your application in the following order: -1. Use the `AUTH_ORIGIN` environment variable if it is set, -2. Use the `auth: { origin: 'https://your-cool-origin.com' }` config-value from the `nuxt.config.ts` if it is set, -3. Development only: Determine the origin automatically from the incoming HTTP request - -The `origin` is important for callbacks that happen to a specific origin for `oauth` flows. Note that in order for (2) to work the `origin` already has to be set at build-time, i.e., when you run `npm run build` or `npm run generate` and it will lead to the `origin` being inside your app-bundle. - -Read [the `nuxt.config.ts` docs for more information and different options you can use to set the `origin` at build- and at runtime](/nuxt-auth/configuration/nuxt-auth-handler). diff --git a/docs/content/v0.5/6.resources/_dir.yml b/docs/content/v0.5/6.resources/_dir.yml deleted file mode 100644 index 7994afb7..00000000 --- a/docs/content/v0.5/6.resources/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Resources -icon: heroicons-outline:play diff --git a/docs/content/v0.5/_dir.yml b/docs/content/v0.5/_dir.yml deleted file mode 100644 index 8592c74c..00000000 --- a/docs/content/v0.5/_dir.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: v0.5 -icon: icon-park-outline:future-build-one -layout: module -aside.level: 2 diff --git a/docs/content/1.getting-started/4.caching-content.md b/docs/guide/advanced/caching.md similarity index 77% rename from docs/content/1.getting-started/4.caching-content.md rename to docs/guide/advanced/caching.md index ae49a3cd..0bf76f6d 100644 --- a/docs/content/1.getting-started/4.caching-content.md +++ b/docs/guide/advanced/caching.md @@ -1,12 +1,4 @@ ---- -description: "Learn how to configure your project to support caching" ---- - -::alert{type="info"} -If you are using the following routeRules (`swr`, `isr`, `prerender`), you will need to read this. When prerendering your entire site using `nuxi generate`, this is done automatically. -:: - -# Caching Content +# Caching content Often hosting providers offer caching on the edge. Most websites can experience incredible speeds (and cost savings) by taking advantage of caching. No cold start, no processing requests, no parsing Javascript... just HTML served immediately from a CDN. @@ -14,15 +6,17 @@ By default we send the user's authentication data down to the client in the HTML To add caching to your Nuxt app, follow the [Nuxt documentation on hybrid rendering](https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering). -## Configuration +:::warning +If you find yourself needing to server-rendered auth methods like `getProviders()`, you must set the `baseURL` option on the `auth` configuration object. **This applies in development too.** +::: -::alert{type="warning"} -If you find yourself needing to server-rendered auth methods like `getProviders()`, you must set the `baseURL` option on the `auth` object. This applies in development too. -:: +:::tip Acknowledgements +A big thanks to [KyleSmith0905](https://github.com/KyleSmith0905) for implementing routes rules into NuxtAuth. View their PR [here](https://github.com/sidebase/nuxt-auth/pull/610). +::: -### Page Specific Cache Rules +## Page specific cache rules -If only a few of your pages are cached. Head over to the Nuxt config `routeRules`, add the `auth` key to your cached routes. Set `disableServerSideAuth` to `true`. +If only a few of your pages are cached. Head over to the [Nuxt config `routeRules`](https://nuxt.com/docs/guide/concepts/rendering#route-rules), add the `auth` key to your cached routes. Set `disableServerSideAuth` to true. ```ts export default defineNuxtConfig({ @@ -42,9 +36,9 @@ export default defineNuxtConfig({ }) ``` -### Module Cache Rules +## Global cache rules -If all/most pages on your site are cached. Head over to the Nuxt config, add the `auth` key if not already there. Set `disableServerSideAuth` to `true`. +If all/most pages on your site are cached. Head over to the Nuxt config, add the `auth` key if not already there. Set `disableServerSideAuth` to true. ```ts export default defineNuxtConfig({ @@ -57,7 +51,7 @@ export default defineNuxtConfig({ }) ``` -### Combining Configurations +## Combining rules Route-configured options take precedent over module-configured options. If you disabled server side auth in the module, you may still enable server side auth back by setting `auth.disableServerSideAuth` to `false`. diff --git a/docs/guide/advanced/deployment/netlify.md b/docs/guide/advanced/deployment/netlify.md new file mode 100644 index 00000000..6527742e --- /dev/null +++ b/docs/guide/advanced/deployment/netlify.md @@ -0,0 +1,30 @@ +# Deploying on Netlify + +When deploying on Netlify ensure all required environment variables are set. Read more about general deployment [here](/guide/advanced/deployment/self-hosted). + +## Differences to Self hosted deployments + +Netlify can automatically assign domain names for your application. If you would like to access the generated domain through your environment variables you can access the [read-only variable `URL` or `DEPLOY_URL`](https://docs.netlify.com/configure-builds/environment-variables/#deploy-urls-and-metadata). + +- `URL`: URL representing the main address to your site. It can be either a Netlify subdomain or your own custom domain if you set one (e.g. `https://your-auth-app.netlify.app`) +- `DEPLOY_URL`: URL representing the unique URL for an individual deploy. It starts with a unique ID that identifies the deploy (e.g. `https://5b243e66dd6a547b4fee73ae--your-auth-app.netlify.app`) + +Depending on the environment and the use-case, you may want to use one of the two variables. + +These variables are avalible at build-time. Therefore you can references this variable inside your `nuxt.config.ts` to set the dynamic url for netlify deployments: + +```ts +export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'], + auth: { + baseURL: process.env.DEPLOY_URL ? `${DEPLOY_URL}/api/auth` : undefined + } +}) +``` + +At build time this will check if your application is running inside a Netlify environment (using `process.env.DEPLOY_URL` or `process.env.URL`). If this environment variable is set the `baseURL` is infered by combining the `host` and `path` together. + + +::: warning +Securing a preview deployment (with an OAuth provider) comes with some critical obstacles. Most OAuth providers only allow a single redirect/callback URL, or at least a set of full static URLs. Meaning you cannot set the value before publishing the site and you cannot use wildcard subdomains in the callback URL settings of your OAuth provider. To avoid this, AuthJS has a few suggestions you can find [here](https://next-auth.js.org/deployment#securing-a-preview-deployment). +::: diff --git a/docs/guide/advanced/deployment/self-hosted.md b/docs/guide/advanced/deployment/self-hosted.md new file mode 100644 index 00000000..48067b4d --- /dev/null +++ b/docs/guide/advanced/deployment/self-hosted.md @@ -0,0 +1,46 @@ +# Self Hosting + +This guide will explain how you can self-host a Nuxt3 application running NuxtAuth. + +## Authjs Provider + +When deploying the Auth.JS provider, the application must be informed what URL it is running at. This is to properly determine callback urls when navigating users to external OAuth providers. Depending on your setup, NuxtAuth allows you to set this value at either [**Runtime**](https://nuxt.com/docs/guide/going-further/hooks#app-hooks-runtime) or [**Buildtime**](https://nuxt.com/docs/guide/going-further/hooks#nuxt-hooks-build-time). + +- **Runtime:** Set the `AUTH_ORIGIN` environment variable. +- **Buildtime:** Set the `baseURL`-config key inside the `nuxt.config.ts` + +The origin consists out of: + +- **scheme:** http / https +- **host:** e.g., localhost, example.org, google.com +- **port:** empty (implies `:80` for http and `:443` for https), :3000, :8888 + +An example of the `AUTH_ORIGIN` would be: `https://my-awesome-app.com` + +:::info Origin Order +When [attempting to determine the server origin](https://github.com/sidebase/nuxt-auth/blob/main/src/runtime/server/services/utils.ts#L11), NuxtAuth checks the available options in the following order: +- **Prio 1**: Using `AUTH_ORIGIN` +- **Prio 2**: Using `baseURL`-config key from inside the `nuxt.config.ts` +- **Prio 3**: Infer the origin _(Only in development)_ +::: + +:::tip +We recommend setting the `AUTH_ORIGIN` during runtime and leaving the `baseURL`-config key empty, to avoid using a potentially incorrect ORIGIN. +::: + +In addition to verifying that the origin is correctly set, also ensure that you have a secure [`secret` set in the NuxtAuthHandler](/guide/authjs/nuxt-auth-handler#secret). + +## Local / Refresh Provider + +When deploying a Local or Refresh -provider based app, you will only need to set the correct `baseURL` to your authentication backend. + +This path can either be: + +- **Reletive**: Pointing at a path inside your own application (e.g. `/api/auth`) +- **Absolute**: Pointing at a path inside an external application (e.g. `https://my-auth-backend/api`) + +:::warning +For the `local` and `refresh` providers, this value will need to be set at build time. This is required to support static applications. + +For this, ensure that you either directly set the `baseURL` inside the `nuxt.config.ts`, or provide a build-time environment variable that overwrites the value inside the `nuxt.config.ts`. +::: diff --git a/docs/guide/advanced/deployment/vercel.md b/docs/guide/advanced/deployment/vercel.md new file mode 100644 index 00000000..440a1f35 --- /dev/null +++ b/docs/guide/advanced/deployment/vercel.md @@ -0,0 +1,25 @@ +# Deploying on Vercel + +When deploying on Vercel ensure all required environment variables are set. Read more about general deployment [here](/guide/advanced/deployment/self-hosted). + +## Differences to Self hosted deployments + +Vercel can automatically assign domain names for your application. If you would like to access the generated domain through your environment variables you can access the [system environment variable `VERCEL_URL`](https://vercel.com/docs/projects/environment-variables/system-environment-variables). + +This variable is avalible at both build and run-time. Therefore you can references this variable inside your `nuxt.config.ts` to set the dynamic url for vercel deployments: + +```ts +export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'], + auth: { + baseURL: process.env.VERCEL_URL ? `https://${VERCEL_URL}/api/auth` : undefined + } +}) +``` + +At build time this will check if your application is running inside a Vercel environment (using `process.env.VERCEL_URL`). If this environment variable is set the `baseURL` is infered by combining the `schema`, `host` and `path` together. + + +::: warning +Securing a preview deployment (with an OAuth provider) comes with some critical obstacles. Most OAuth providers only allow a single redirect/callback URL, or at least a set of full static URLs. Meaning you cannot set the value before publishing the site and you cannot use wildcard subdomains in the callback URL settings of your OAuth provider. To avoid this, AuthJS has a few suggestions you can find [here](https://next-auth.js.org/deployment#securing-a-preview-deployment). +::: diff --git a/docs/guide/application-side/configuration.md b/docs/guide/application-side/configuration.md new file mode 100644 index 00000000..9255a407 --- /dev/null +++ b/docs/guide/application-side/configuration.md @@ -0,0 +1,157 @@ +# Configuration + +NuxtAuth offers a wide range of configuration options that can be defined inside the `nuxt.config.ts`. You can find an example of a fully configured `authjs` app below: + +```ts +export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'], + auth: { + isEnabled: true, + disableServerSideAuth: false, + baseURL: 'http://localhost:3000/api/auth', + provider: { /* your provider config */ }, + sessionRefresh: { + enablePeriodically: true, + enableOnWindowFocus: true, + } + } +}) +``` + +## `isEnabled` + +- **Type**: `boolean` +- **Default**: `true` + +Whether the module is enabled at all + +## `disableServerSideAuth` + +- **Type**: `boolean` +- **Default**: `false` + +Forces your server to send a "loading" authentication status on all requests, thus prompting the client to do a fetch. If your website has caching, this prevents the server from caching someone's authentication status. This affects the entire site; for route-specific rules add `disableServerSideAuth` on `routeRules`. Read more [here](/guide/advanced/caching). + +## `baseURL` + +- **Type**: `string | undefined` +- **Default**: + - AuthJS Provider: + - _Development_: `http://localhost:3000/api/auth` + - _Production_: `undefined` + - Local / Refresh Provider: `/api/auth` + +The full url at which the app will run combined with the path to authentication. You can set this differently depending on your selected authentication-provider: + +- `authjs`: You must set the full URL, with origin and path in production. You can leave this empty in development +- `local`: You can set a full URL, but can also leave this empty to fallback to the default value of `/api/auth` or set only the path. + +### `authjs` Provider + +`baseURL` can be `undefined` during development but _must_ be set to the combination of origin + path that points to your `NuxtAuthHandler` for production. The origin consists out of: +- **scheme**: http / https +- **host**: e.g., localhost, example.org, google.com +- **port**: _empty_ (implies `:80` for http and `:443` for https), :3000, :8888 +- **path**: the path that directs to the location of your `NuxtAuthHandler` e.g. `/api/auth` + +### `local` and `refresh` Providers + +Defaults to `/api/auth` for both development and production. Setting this is optional, if you set it you can set it to either: +- just a path: Will lead to `nuxt-auth` using `baseURL` as a relative path appended to the origin you deploy to. Example: `/backend/auth` +- an origin and a path: Will lead to `nuxt-auth` using `baseURL` as an absolute request path to perform requests to. Example: `https://example.com/auth` + +:::warning +If you point to a different origin than the one you deploy to you likely have to take care of CORS: Allowing cross origin requests. +::: + +## `provider` + +- **Type**: `ProviderAuthjs | ProviderLocal | ProviderRefresh` +- **Default**: `undefined` + +Configuration of the authentication provider. Different providers are supported: +- AuthJS: See [configuration options here](/guide/authjs/quick-start#configuration) +- Local / Refresh: See [configuration options here](/guide/local/quick-start) + +## `sessionRefresh` + +- **Type**: `SessionConfig | boolean` +- **Default**: `{ enablePeriodically: false, enableOnWindowFocus: true, refreshHandler: RefreshHandler }` + +Configuration of the application-side session. You can configure the following attributes: + +### `enablePeriodically` + +- **Type**: `boolean | number` +- **Default**: `undefined` + +Whether to refresh the session every `X` milliseconds. The refresh will only happen if a session already exists. +Setting this to a number `X` will refresh the session every `X` milliseconds. +Setting this to `true` is equivalent to `enablePeriodically: 1000`, the session will be refreshed every second. +Setting this to `false` will turn the session refresh off. + +### `enableOnWindowFocus` + + +- **Type**: `boolean` +- **Default**: `true` + +Whether to refresh the session every time the browser window is refocused. + +### `refreshHandler` + +- **Type**: `string` +- **Default:** `undefined` + +To customize the session refreshing you can provide the path to your refresh handler. When setting this option, `enablePeriodically` and `enableOnWindowFocus` are ignored. + +A custom `RefreshHandler` requires `init` and `destroy` functions: + +- `init` will be called when the nuxt application is mounted. Here you may add event listeners and initialize custom refresh behaviour. +- `destroy` will be called when your app is unmounted. Here you may run your clean up routine e.g. to remove your event listeners. + +::: code-group +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + auth: { + sessionRefresh: { + // You can place it anywhere and name as you wish + handler: './config/AuthRefreshHandler' + } + } +}) +``` + +```ts [~/config/AuthRefreshHandler.ts] +import type { RefreshHandler } from '@sidebase/nuxt-auth' + +// You may also use a plain object with `satisfies RefreshHandler` +class CustomRefreshHandler implements RefreshHandler { + init (): void { + console.info('Use the full power of the refreshHandler!') + } + + destroy (): void { + console.info( + 'Hover above class properties or go to their definition ' + + 'to learn more about how to craft a refreshHandler' + ) + } +} + +export default new CustomRefreshHandler() +``` + +If no custom RefreshHandler is defined, the [built-in-handler](https://github.com/sidebase/nuxt-auth/blob/main/src/runtime/utils/refreshHandler.ts) will be used to handle refreshes. + +### `globalAppMiddleware` + +- **Type:** `GlobalMiddlewareOptions | boolean` +- **Default**: `false` + +Whether to add a global authentication middleware that protects all pages. Can be either `false` to disable, `true` to enable with defaults or an object to enable with provided options. + +- If you **enable** this, everything is going to be protected and you can selectively disable protection for some pages by specifying `definePageMeta({ auth: false })` +- If you **disable** this, everything is going to be public and you can selectively enable protection for some pages by specifying `definePageMeta({ auth: true })` + +Read more about [protecting pages](/guide/application-side/protecting-pages). diff --git a/docs/guide/application-side/protecting-pages.md b/docs/guide/application-side/protecting-pages.md new file mode 100644 index 00000000..d2635476 --- /dev/null +++ b/docs/guide/application-side/protecting-pages.md @@ -0,0 +1,182 @@ +--- +outline: 'deep' +--- + +# Protecting Pages + +NuxtAuth offers different approaches to protect pages: + +- **Global middleware:** Protects all pages with manual exceptions +- **Local middleware:** Protects specific pages +- **Custom middleware:** Create your own middleware + +## Global Middleware + +To enable the global middleware on your application, you can configure the middleware inside the `nuxt.config.ts`. + +```ts +export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'], + auth: { + globalAppMiddleware: true + } +}) +``` + +If you like to further customize the global middleware, you can pass an object of configurations to `globalAppMiddleware`. See the API reference here. + +### Disabling the Global Middleware + +If the global middleware is disabled, you can manually add the middleware to individual pages. This is only available if the global middleware is disabled, as you will get an error along the lines of `Error: Unknown route middleware: 'auth'`. This is because the auth middleware is then added globally and not available to use as a local, page-specific middleware. + +```vue + + + +``` + +## Local Middleware + +To locally enable or disable the middleware on a single page, you can use the [`definePageMeta`](https://nuxt.com/docs/api/utils/define-page-meta) macro to set the authentication metadata for a single page. + +```vue + + + +``` + +### Middleware options + +`auth` can be either a boolean or an object of further middleware configurations. + +```vue + + + +``` + +#### `unauthenticatedOnly` + +Whether to only allow unauthenticated users to access this page. Authenticated users will be redirected to / or the route defined in `navigateAuthenticatedTo`. + +:::tip +Setting `unauthenticatedOnly: false` is equivalent to setting `auth: false` from the user perspective, but requires some extra middleware steps, so it is a bit less efficient. Therefore it is recommended to use `auth: false` instead. +::: + +#### `navigateAuthenticatedTo` + +Where to redirect authenticated users if `unauthenticatedOnly` is set to `true`. + +#### `navigateUnauthenticatedTo` + +Where to redirect unauthenticated users if this page is protected. + +### Guest mode + +You can use NuxtAuth to setup pages that are accessible only when the user **is not logged in**. This is sometimes called _"guest mode"_. The behavior of such a page is as follows: + +- A logged in user visits the page -> redirect to another (likely protected) page, +- A logged out user visits the page -> they are allowed to stay and view it + +This behavior is useful for login pages that you don't want to be visitable by logged in users: Why should they go through a login flow again? + +```vue + + + +``` + +## Custom Middleware + +You may create your own application-side middleware in order to implement custom, more advanced authentication logic. + +:::warning +Creating a custom middleware is an advanced, experimental option and may result in unexpected or undesired behavior if you are not familiar with advanced Nuxt 3 concepts. +::: + +To implement your custom middleware: +- Create an application-side middleware that applies either globally or is named (see the Nuxt docs for more) +- Add logic based on [`useAuth`](/guide/application-side/session-access) to it + +When adding the logic, you need to watch out when calling other `async` composable functions. This can lead to `context`-problems in Nuxt, see [the explanation for this here](https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529). In order to avoid these problems, you will need to either: + +- use the undocumented `callWithNuxt` utility when awaiting other composables +- return an async function where possible instead of awaiting it to avoid `callWithNuxt` + +::: code-group + +```ts [Direct return] +// file: ~/middleware/authentication.global.ts +export default defineNuxtRouteMiddleware((to) => { + const { status, signIn } = useAuth() + + // Return immediately if user is already authenticated + if (status.value === 'authenticated') { + return + } + + /** + * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529 + * + * So to avoid calling it, we return it immediately. + */ + return signIn(undefined, { callbackUrl: to.path }) as ReturnType +}) +``` + +```ts [Call with Nuxt] +// file: ~/middleware/authentication.global.ts +import { useNuxtApp } from '#imports' +import { callWithNuxt } from '#app/nuxt' + +export default defineNuxtRouteMiddleware((to) => { + // It's important to do this as early as possible + const nuxtApp = useNuxtApp() + + const { status, signIn } = useAuth() + + // Return immediately if user is already authenticated + if (status.value === 'authenticated') { + return + } + + /** + * We cannot directly call and/or return `signIn` here as `signIn` uses async composables under the hood, leading to "nuxt instance undefined errors", see https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529 + * + * So to avoid calling it, we call it via `callWithNuxt`. + */ + await callWithNuxt(nuxtApp, signIn, [undefined, { callbackUrl: to.path }]) +}) +``` + +::: diff --git a/docs/guide/application-side/session-access.md b/docs/guide/application-side/session-access.md new file mode 100644 index 00000000..d7e94e46 --- /dev/null +++ b/docs/guide/application-side/session-access.md @@ -0,0 +1,398 @@ +--- +outline: 'deep' +--- + +# Session Access and Management + +After setting up your provider of choice, you can begin integrating NuxtAuth into your frontend. For this NuxtAuth provides two application-side composables that can be used to interact with the authentication session. + +## `useAuth` composable + +The `useAuth` composable is your main gateway to accessing and manipulating session-state and data. Here are the main methods you can use: + +::: code-group + +```ts [authjs] +const { + status, + data, + lastRefreshedAt, + getCsrfToken, + getProviders, + getSession, + signIn, + signOut +} = useAuth() +``` + +```ts [local] +const { + status, + data, + lastRefreshedAt, + token, + getSession, + signUp, + signIn, + signOut +} = useAuth() +``` + +```ts [refresh] +const { + status, + data, + lastRefreshedAt, + token, + getSession, + signUp, + signIn, + signOut, + refresh, + refreshToken +} = useAuth() +``` + +::: + +### `status` + +Computed value that returns the current session status. Options: `unauthenticated`, `loading` or `authenticated`. + +```vue + + + +``` + +### `data` + +The current data inside the session. Options: `undefined` when no authentication attempt was made, `null` when the user is unauthenticated, `SessionData` when the user is authenticated. + +To customize your `SessionData` see the following docs for [authjs](/guide/authjs/session-data) and [local / refresh](/guide/local/session-data). + +```vue + + + +``` + +### `token` + +The fetched token that can be used to authenticate further requests. This could be e.g. a JWT-Bearer token. + +```ts +function useAPI() { + const { token } = useAuth() + + return $fetch.create({ + baseURL: '/api', + headers: { + Authorization: `Bearer ${token.value}` + } + }) +} +``` + +:::warning Local / Refresh Only +`token` is only avalible for the refresh and local providers! +::: + +### `lastRefreshedAt` + +Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date` of the time the refresh happened. + +### `getCsrfToken` + +Returns the current Cross Site Request Forgery Token (CSRF Token) required to make POST requests (e.g. for signing in and signing out). + +You likely only need to use this if you are not using the built-in `signIn()` and `signOut()` methods. Read more: https://next-auth.js.org/getting-started/client#getcsrftoken + +:::warning AuthJS Only +`getCsrfToken` is only avalible for the authjs provider! +::: + + +### `getProviders` + +Get a list of all the configured OAuth providers. Useful for creating a [custom login page](/guide/authjs/custom-pages#sign-in-page). Returns an array of `Provider`. + +```ts +export interface Provider { + id: string + name: string + type: ProviderType + signinUrl: string + callbackUrl: string +} +``` + +:::warning AuthJS Only +`getProviders` is only avalible for the authjs provider! +::: + +### `getSession` + +Get or reload the current session from the server. Optionally pass `required` to force a signIn if the session doesn't exist. + +```vue + + + +``` + +### `signUp` + +```ts +// `credentials` are the credentials your sign-up endpoint expects, +const credentials = { username: 'jsmith', password: 'hunter2' } + +// Trigger a sign-up +await signUp(credentials) + +// Trigger a sign-up with auto sign-in and redirect to the profile page within the application +await signUp(credentials, { callbackUrl: '/profile', redirect: true }) + +// Trigger a sign-up with auto sign-in and redirect to an external website (https://external.example.com) +await signUp(credentials, { callbackUrl: 'https://external.example.com', redirect: true, external: true }) + +// Trigger a sign-up without auto sign-in and redirect to the home page within the application +await signUp(credentials, { callbackUrl: '/', redirect: true }, { preventLoginFlow: true }) + +// Trigger a sign-up without auto sign-in and doesn't redirect anywhere +await signUp(credentials, undefined, { preventLoginFlow: true }) +``` + +:::info +You can also pass the `callbackUrl` option to redirect a user to a certain page, after he's completed the action. This can be useful when a user attempts to open a page (`/protected`) but has to go through external authentication (e.g., via their google account) first. +::: + +:::warning Local / Refresh Only +`signUp` is only avalible for the refresh and local providers! +::: + +### `signIn` + +`signIn` uses a different syntax depending on your provider. + +::: code-group + +```ts [authjs] +// Trigger a signIn on the signIn page +await signIn() + +// Trigger a signIn with a specific oAuth provider +await signIn('github') + +// Trigger a signIn with the credentials provider +await signIn('credentials', { username: 'jsmith', password: 'hunter2' }) + +// Trigger a signIn with a redirect afterwards +await signIn(undefined, { callbackUrl: '/protected' }) + +// Trigger a signIn with a redirect to an external page afterwards +await signIn(undefined, { callbackUrl: 'https://nuxt.org', external: true }) +``` + +```ts [local / refresh] +// `credentials` are the credentials your sign-in endpoint expects, +const credentials = { username: 'jsmith', password: 'hunter2' } + +// Trigger a signIn +await signIn(credentials) + +// Trigger a signIn with a redirect afterwards +await signIn(credentials, { callbackUrl: '/protected' }) + +// Trigger a signIn with a redirect to an external page afterwards +await signIn(credentials, { callbackUrl: 'https://nuxt.org', external: true }) +``` + +::: + +:::info +You can also pass the `callbackUrl` option to redirect a user to a certain page, after he's completed the action. This can be useful when a user attempts to open a page (`/protected`) but has to go through external authentication (e.g., via their google account) first. +::: + +### `signOut` + +Sign a user out of the application. Optionally pass a `callbackUrl` to redirect a user to afterwards. + +```vue + + + +``` + +:::info +You can also pass the `callbackUrl` option to redirect a user to a certain page, after he's completed the action. This can be useful when a user attempts to open a page (`/protected`) but has to go through external authentication (e.g., via their google account) first. +::: + +### `refreshToken` + +The fetched refreshToken that can be used to obtain a new access token . E.g. a refreshToken looks like this: `eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` + +:::warning Refresh Only +`refreshToken` is only avalible for the refresh provider! +::: + +### `refresh` + +Trigger a refresh, this will do a provider-specific session refresh. + +## `useAuthState` composable + +The `useAuthState` composable is the underlying storage layer to access the session-state and data. Here're the main methods and properties you can use: + +::: code-group + +```ts [authjs] +const { + status, + loading, + data, + lastRefreshedAt +} = useAuthState() + +// Session status, either `unauthenticated`, `loading`, `authenticated` +status.value + +// Whether any http request is still pending +loading.value + +// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), `loading` (= session loading in progress), see https://next-auth.js.org/getting-started/client#signout +data.value + +// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date` of the time the refresh happened +lastRefreshedAt.value + +``` + +```ts [local] +const { + status, + loading, + data, + lastRefreshedAt, + token, + rawToken, + setToken, + clearToken +} = useAuthState() + +// Session status, either `unauthenticated`, `loading`, `authenticated` +status.value + +// Whether any http request is still pending +loading.value + +// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns +data.value + +// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date` of the time the refresh happened +lastRefreshedAt.value + +// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` +token.value + +// Cookie that containes the raw fetched token string. This token won't contain any modification or prefixes like `Bearer` or any other. +rawToken.value + +// Helper method to quickly set a new token (alias for rawToken.value = 'xxx') +setToken('new token') + +// Helper method to quickly delete the token cookie (alias for rawToken.value = null) +clearToken() +``` + +```ts [refresh] +const { + status, + loading, + data, + lastRefreshedAt, + token, + rawToken, + setToken, + clearToken, + rawRefreshToken, + refreshToken +} = useAuthState() + +// Session status, either `unauthenticated`, `loading`, `authenticated` +status.value + +// Whether any http request is still pending +loading.value + +// Session data, either `undefined` (= authentication not attempted), `null` (= user unauthenticated), or session / user data your `getSession`-endpoint returns +data.value + +// Time at which the session was last refreshed, either `undefined` if no refresh was attempted or a `Date` of the time the refresh happened +lastRefreshedAt.value + +// The fetched token that can be used to authenticate future requests. E.g., a JWT-Bearer token like so: `Bearer eyDFSJKLDAJ0-3249PPRFK3P5234SDFL;AFKJlkjdsjd.dsjlajhasdji89034` +token.value + +// The fetched refreshToken that can be used to refresh the Token with refresh() methode. +refreshToken.value + +// Cookie that containes the raw fetched token string. This token won't contain any modification or prefixes like `Bearer` or any other. +rawToken.value + +// Cookie that containes the raw fetched refreshToken string. +rawRefreshToken.value + +// Helper method to quickly set a new token (alias for rawToken.value = 'xxx') +setToken('new token') + +// Helper method to quickly delete the token and refresh Token cookie (alias for rawToken.value = null and rawRefreshToken.value = null) +clearToken() +``` +::: + +:::warning Local and refresh providers: +Note that you will have to manually call getSession from useAuth composable in order to refresh the new user state when using setToken, clearToken or manually updating rawToken.value: +::: + +```ts +const { getSession } = useAuth() + +const { setToken } = useAuthState() +// ...setToken('...') +await getSession() +``` diff --git a/docs/guide/authjs/custom-pages.md b/docs/guide/authjs/custom-pages.md new file mode 100644 index 00000000..773a8e32 --- /dev/null +++ b/docs/guide/authjs/custom-pages.md @@ -0,0 +1,100 @@ +# Custom pages + +NuxtAuth delivers a set of prebuilt authentication pages that you can use out of the box. However, in most cases you may like to customize the authentication pages to match your branding. + +:::info +Remember to disable any authentication related middleware on pages that unauthenticated users should be able to access (e.g. `signIn`) +::: + +## Sign In page + +When crafting your custom sign-in page you can use `signIn` to directly start a provider-flow. For example, when a user clicks a button on your custom sign-in page. Here is a very simple sign-in page that either directly starts a GitHub-OAuth sign-in flow or directly signs the user in via the credentials flow: + +```vue + + + +``` + +::: warning +In the above example username and password are hard-coded. In your own custom page, these two fields should probably come from inputs on your page. +::: + +If you want to create a custom sign-in page that dynamically offers sign-in options based on your configured providers, you can call `getProviders()` first and then iterate over the supported providers to generate your sign-in page. + +```vue + + + +``` + +## Error page + +When an authentication related error occurs during the flow, the user will be redirected to your error page, along with an error code describing what error occured. You can handle the error yourself, by retrieving the error code from the url. + +Example: `/auth/error?error=Configuration` + +```vue + + + +``` + +### Error codes + +- `Configuration`: There is a problem with the server configuration. +- `AccessDenied`: Usually occurs, when you restricted access through the [`signIn` or `redirect` callback](/guide/authjs/nuxt-auth-handler#callbacks). +- `Verification`: Related to the Email provider. The token has expired or has already been used +- `Default`: Catch all, will apply, if none of the above matched + +## Themes + +Looking for a quick start with some pre-made themes? Have a look at [Morpheme UI](https://ui.morpheme.design/templates/nuxt-auth.html), which provides an out of the box integration with NuxtAuth! + +![morpheme-preview](/authjs/morpheme-auth-screenshot.png) diff --git a/docs/guide/authjs/nuxt-auth-handler.md b/docs/guide/authjs/nuxt-auth-handler.md new file mode 100644 index 00000000..1975da42 --- /dev/null +++ b/docs/guide/authjs/nuxt-auth-handler.md @@ -0,0 +1,126 @@ +# NuxtAuthHandler + +The NuxtAuthHandler is an adaptation of the [NextAuthHandler](https://next-auth.js.org/configuration/options) built into AuthJS. You can learn more about how to setup a minimal version of the NuxtAuthHandler in the [Quick Start section](/guide/authjs/quick-start#nuxtauthhandler). + + +## Secret + +The secret is a random string used to hash tokens, sign and encrypt cookie and generate cryptographic keys. In development the `secret` is automatically set to a SHA hash of the `NuxtAuthHandler`. In production we recommend setting a [runtimeConfig](https://nuxt.com/docs/guide/going-further/runtime-config) value to define a more secure value. + +::: code-group +```ts [~/server/api/auth/[...].ts] +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + secret: useRuntimeConfig().authSecret, + // your authentication configuration here! +}) +``` + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + runtimeConfig: { + authSecret: '123', + } +}) +``` + +``` txt [.env] +NUXT_AUTH_SECRET="YOUR-SUPER-SECURE-SECRET" +``` + +::: + +## Providers + +The providers are the registered authentication methods that your users can use to login to your application. NuxtAuth provides a number of preconfigured providers you can use to quickly bootstrap your project. These include OAuth providers, [email-based providers](https://next-auth.js.org/configuration/providers/email) (Magic URLs) and a [credentials provider](https://next-auth.js.org/configuration/providers/credentials). In addition to using a pre-built provider, you can also create your own provider. + +You can find an overview of all the prebuilt providers [here](https://next-auth.js.org/providers/). If you want to create your own provider, please visit the [NextAuth docs](https://next-auth.js.org/configuration/providers/oauth#using-a-custom-provider). + + +## Callbacks + +The callbacks inside the NuxtAuthHandler are asynchronous functions that allow you to hook into and modify the authentication flow. This is helpful for when you need to: + +- Change the data inside the JWT token or Session Data +- Add support for refresh tokens + +Callbacks are very powerful and allow you to modify the authentication flow based on your needs. + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // your authentication configuration here! + callbacks: { + /* on before signin */ + async signIn({ user, account, profile, email, credentials }) { + return true + }, + /* on redirect to another url */ + async redirect({ url, baseUrl }) { + return baseUrl + }, + /* on session retrival */ + async session({ session, user, token }) { + return session + }, + /* on JWT token creation or mutation */ + async jwt({ token, user, account, profile, isNewUser }) { + return token + } + } +}) +``` + +Some uses-cases for each callback could be: + +- `signIn`: Check if a user is e.g. restricted from accessing the application and terminate the signin flow. +- `redirect`: Customize the callback url based on parameters that need to be dynamically calculated and cannot be set on startup (e.g. through feature flags or database values). +- `session`: Fetch and inject additional data into the session. Read more [here](/guide/authjs/session-data). +- `jwt`: Inject or update data inside the JWT token and manage refresh and access tokens. + +You can read more on each of these callbacks, what data they provide and what return value they expect on the offical [NextAuth docs](https://next-auth.js.org/configuration/callbacks). + +## Events + +Events are asynchronous callback functions invoked during certain actions in the authentication flow. They can be used to log certain events or debug your authentication flow. + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // your authentication configuration here! + events: { + async signIn(message) { /* on successful sign in */ }, + async signOut(message) { /* on signout */ }, + async createUser(message) { /* user created */ }, + async updateUser(message) { /* user updated - e.g. their email was verified */ }, + async linkAccount(message) { /* account (e.g. GitHub) linked to a user */ }, + async session(message) { /* session is active */ }, + } +}) +``` + +You can read more on each of these events and what data they provide on the offical [NextAuth docs](https://next-auth.js.org/configuration/events). + +## Pages + +Inside the pages configuration, you can define custom routes that match your authentication related pages. Setting new pages here, will override the included default authentication pages, included with the module. + +If you would like to learn more about custom pages and customization, please read the full guide [here](/guide/authjs/custom-pages). + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // your authentication configuration here! + pages: { + signIn: '/auth/signIn', + signOut: '/auth/signOut', + error: '/auth/error', + verifyRequest: '/auth/verify-request', + newUser: '/auth/new-user' + } +}) +``` diff --git a/docs/guide/authjs/quick-start.md b/docs/guide/authjs/quick-start.md new file mode 100644 index 00000000..48a946ef --- /dev/null +++ b/docs/guide/authjs/quick-start.md @@ -0,0 +1,157 @@ +# AuthJS Quickstart + +This guide is for setting up `@sidebase/nuxt-auth` with the AuthJS Provider, which is best suited for plug-and-play OAuth for established oauth-providers or magic-url based sign-ins. + +## Installation + +If you want to use the AuthJS provider, you have to install `next-auth`. With all package managers except npm you must manually install the peer dependency alongside nuxt-auth: + +::: code-group + +```bash [pnpm] +pnpm i next-auth@4.21.1 +``` + +```bash [yarn] +yarn add next-auth@4.21.1 +``` + +::: warning +Due to a breaking change in NextAuth, nuxt-auth is only compatible with NextAuth versions under v4.23.0. We recommend pinning the version to `4.21.1`. Read more [here](https://github.com/sidebase/nuxt-auth/issues/514). +::: + +## Configuration + +After installing `@sidebase/nuxt-auth` and `next-auth`, you can now configure NuxtAuth to use the AuthJS provider. Inside your `nuxt.config.ts` add the following configuration: + +```ts +export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'], + auth: { + provider: { + type: 'authjs', + trustHost: false, + defaultProvider: 'github', + addDefaultCallbackUrl: true + } + } +}) +``` + +You can also configure AuthJS specific options inside the `nuxt.config.ts`, in addition to the main [module configurations](/guide/application-side/configuration). + +### `trustHost` + +- **Type**: `boolean` +- **Default**: `false` + +If set to `true`, `authjs` will use either the `x-forwarded-host` or `host` headers instead of `auth.baseURL`. Make sure that reading `x-forwarded-host` on your hosting platform can be trusted. + +:::warning +**This is an advanced option.** Advanced options are passed the same way as basic options, but **may have complex implications** or side effects. You should try to avoid using advanced options unless you are very comfortable using them. +::: + +### `defaultProvider` + +- **Type**: `SupportedProviders` +- **Default**: `undefined` + +Select the default-provider to use when `signIn` is called. Setting this here will also affect the global middleware behavior. For instance, when you set it to `github` and the user is unauthorized, they will be directly forwarded to the Github OAuth page instead of seeing the app-login page. + +### `addDefaultCallbackUrl` + +- **Type:** `boolean | string` +- **Default:** `true` + +Whether to add a callbackUrl to sign in requests. Setting this to a string-value will result in that being used as the callbackUrl path. Setting this to `true` will result in the blocked original target path being chosen (if it can be determined). + + +## NuxtAuthHandler + +As a next step, create your NuxtAuthHandler under `~/server/api/auth/[...].ts`. Inside it you can configure the authentication provider you want to use, how the JWT Token is created and managed as well as how your sessions will be composed. The NuxtAuthHander will automatically create all required API endpoints to handle authentication inside your application. + +The NuxtAuthHandler is an adaptation of the [NextAuthHandler](https://next-auth.js.org/configuration/options) built into AuthJS. Inside the NuxtAuthHandler you can configure: + +- **OAuth providers**: _How can users login to your application?_ +- **Adapters**: _How are sessions saved? (e.g. JWT Token, Database etc.)_ +- **JWT Encryption**: _How is the JWT Token encrypted and read?_ +- **Callbacks**: _Hook into the authentication lifecycle hooks._ + +Begin by creating a new server route file in `~/server/api/auth/[...].ts`. You can then begin adding your NuxtAuthHandler. The filename must be `[...].ts` - this is a so-called "catch-all" route, read more in the [Nuxt catch-all docs](https://nuxt.com/docs/guide/directory-structure/server#catch-all-route). + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // your authentication configuration here! +}) +``` + +### Adding a provider + +After creating your NuxtAuthHandler, you can begin by adding a provider. You can find an overview of all the avalible providers [here](https://next-auth.js.org/providers/). For this example we will add the GitHub provider. + +```ts +import GithubProvider from 'next-auth/providers/github' +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // A secret string you define, to ensure correct encryption + secret: 'your-secret-here', + providers: [ + // @ts-expect-error Use .default here for it to work during SSR. + GithubProvider.default({ + clientId: 'your-client-id', + clientSecret: 'your-client-secret' + }) + ] +}) +``` + +:::warning +After importing your provider from `next-auth/providers/[PROVIDER]`, call it using `.default()` inside of the providers array configuration. This is required for SSR to properly function. +::: + +The NuxtAuthHandler accepts [all options that NextAuth.js](https://next-auth.js.org/configuration/options#options) accepts for its API initialization. Use this place to configure authentication providers (OAuth, credential flow, ...), your secret, add callbacks for authentication events, configure a custom logger and more. Read the [NextAuth.js docs](https://next-auth.js.org/configuration/options#options) to see all possible options. + +### Setting a secret + +In addition to setting a provider, you also need to set a `secret` which is used to encrypt the JWT Tokens. To avoid hard-coding of the secret you can make it configurable at runtime by using an environment variable and exporting it at runtime or by using the [Nuxt `useRuntimeConfig`](https://nuxt.com/docs/api/composables/use-runtime-config) (and then also setting the correct environment at runtime): + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // A secret string you define, to ensure correct encryption - NUXT_AUTH_SECRET required in production + secret: useRuntimeConfig().authSecret + + // rest of your authentication configuration here! +}) +``` + +::: details Full code +```ts +// file: ~/server/api/auth/[...].ts +import GithubProvider from 'next-auth/providers/github' +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + secret: useRuntimeConfig().authSecret, + providers: [ + // @ts-expect-error Use .default here for it to work during SSR. + GithubProvider.default({ + clientId: 'your-client-id', + clientSecret: 'your-client-secret' + }) + ] +}) +``` +::: + +## Next Steps + +Congrats! You have now configured your first authentication provider and can login to the application! We recommend the following next steps to continue configuring your application: + +- [Define custom Session Data](/guide/authjs/session-data) +- [Add more providers in your NuxtAuthHandler](/guide/authjs/nuxt-auth-handler) +- [Customize your Auth pages](/guide/authjs/custom-pages) diff --git a/docs/content/4.server-side/3.jwt-access.md b/docs/guide/authjs/server-side/jwt-access.md similarity index 61% rename from docs/content/4.server-side/3.jwt-access.md rename to docs/guide/authjs/server-side/jwt-access.md index eb130b5c..e815b98e 100644 --- a/docs/content/4.server-side/3.jwt-access.md +++ b/docs/guide/authjs/server-side/jwt-access.md @@ -1,20 +1,17 @@ -::alert{type="info"} -This page is only relevant to you, if you are using the `authjs`-provider. -:: - # JWT Access Getting the (decoded) JWT token of the current user can be helpful, e.g., to use it to access an external api that requires this token for authentication or authorization. You can get the JWT token that was passed along with the request using `getToken`: + ```ts // file: ~/server/api/token.get.ts import { getToken } from '#auth' export default eventHandler(async (event) => { - const token = await getToken({ event }) + const token = await getToken({ event }) - return token || 'no token present' + return token || 'no token present' }) ``` @@ -24,7 +21,15 @@ You do not need to pass in any further parameters like `secret`, `secureCookie`, ## Application-side JWT token access -To access the JWT token application-side, e.g., in a `.vue` page, you can create an endpoint like this: +To access the JWT token application-side, e.g., in a `.vue` page, you can either: + +- Create an API endpoint to return the decoded JWT Token +- Modify the `jwt` callback inside the NuxtAuthHandler to inject token data into the application-side session. Read more [here](/guide/authjs/session-data) + +### Accessing the JWT token through an API endpoint + +To access the JWT token through an API endpoint, you will first need to create a new server side route that can handle the requests for the JWT token. + ```ts // file: ~/server/api/token.get.ts import { getToken } from '#auth' @@ -34,9 +39,8 @@ export default eventHandler(event => getToken({ event })) Then from your application-side code you can fetch it like this: ```vue -// file: app.vue ``` -Note that you have to pass the cookie-header manually. You also have to pass it using [`useRequestHeaders`](https://nuxt.com/docs/api/composables/use-request-headers/) so that the cookies are also correctly passed when this page is rendered server-side during the [universal-rendering process](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering). +:::warning Note: +For this to work, you will need to pass the cookie-header manually using [`useRequestHeaders`](https://nuxt.com/docs/api/composables/use-request-headers/) so that the cookies are also correctly passed when this page is rendered server-side during the [universal-rendering process](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering). +::: diff --git a/docs/guide/authjs/server-side/rest-api.md b/docs/guide/authjs/server-side/rest-api.md new file mode 100644 index 00000000..da2ea9a6 --- /dev/null +++ b/docs/guide/authjs/server-side/rest-api.md @@ -0,0 +1,21 @@ +--- +aside: false +--- + +# REST API + +All endpoints that NextAuth.js supports are also supported by `nuxt-auth`: + +| Endpoint | Request | Description | +|-----------------------------------|:-------------|:---------------------------------------------------------------| +| `${baseURL}/signin` | `GET` | Displays the built-in/unbranded sign-in | +| `${baseURL}/signin/:provider` | `POST` | Starts a provider-specific sign-in | +| `${baseURL}/callback/:provider` | `GET` `POST` | Handles returning requests from OAuth services during sign-in | +| `${baseURL}/signout` | `GET` `POST` | Displays the built-in/unbranded sign out | +| `${baseURL}/session` | `GET` | Returns client-safe session object | +| `${baseURL}/csrf` | `GET` | Returns object containing CSRF | +| `${baseURL}/providers` | `GET` | Returns a list of configured OAuth providers | + +The `baseURL` is `/api/auth` per default and [can be configured in the `nuxt.config.ts`](/guide/application-side/configuration#baseurl). + +You can directly interact with these API endpoints if you wish to, it's probably a better idea to use `useAuth` where possible though. [See the full rest API documentation of NextAuth.js here](https://next-auth.js.org/getting-started/rest-api). diff --git a/docs/content/4.server-side/2.session-access-and-route-protection.md b/docs/guide/authjs/server-side/session-access.md similarity index 53% rename from docs/content/4.server-side/2.session-access-and-route-protection.md rename to docs/guide/authjs/server-side/session-access.md index e08c39bf..ce1f9d30 100644 --- a/docs/content/4.server-side/2.session-access-and-route-protection.md +++ b/docs/guide/authjs/server-side/session-access.md @@ -1,39 +1,39 @@ -::alert{type="info"} -This page is only relevant to you, if you are using the `authjs`-provider. -:: - # Session Access and Route Protection On the server side you can get access to the current session like this: + ```ts import { getServerSession } from '#auth' export default eventHandler(async (event) => { - const session = await getServerSession(event) + const session = await getServerSession(event) }) ``` This is inspired by [the `getServerSession`](https://next-auth.js.org/tutorials/securing-pages-and-api-routes#securing-api-routes) of NextAuth.js. It also avoids an external HTTP `GET` request to the `/api/auth/sessions` endpoint, instead directly calling a pure JS-method. -Note: If you use [Nuxts' `useFetch`](https://nuxt.com/docs/api/composables/use-fetch) from your app-components to fetch data from an endpoint that uses `getServerSession` or [`getToken`](/nuxt-auth/v0.6/server-side/jwt-access) you will need to manually pass along cookies as [Nuxt 3 universal rendering](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering) will not do this per-default when it runs on the server-side. Not passing along cookies will result in `getServerSession` returning `null` when it is called from the server-side as no auth-cookies will exist. Here's an example that manually passes along cookies: +:::warning Note: +If you use [Nuxt's `useFetch`](https://nuxt.com/docs/api/composables/use-fetch) from your app-components to fetch data from an endpoint that uses `getServerSession` or `getToken` you will need to manually pass along cookies as [Nuxt 3 universal rendering](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering) will not do this per-default when it runs on the server-side. Not passing along cookies will result in `getServerSession` returning `null` when it is called from the server-side as no auth cookies will exist. Here's an example that manually passes along cookies: ```ts const headers = useRequestHeaders(['cookie']) as HeadersInit const { data: token } = await useFetch('/api/token', { headers }) ``` +::: ## Endpoint Protection To protect an endpoint, check the session after fetching it: + ```ts // file: ~/server/api/protected.get.ts import { getServerSession } from '#auth' export default eventHandler(async (event) => { - const session = await getServerSession(event) - if (!session) { - return { status: 'unauthenticated!' } - } - return { status: 'authenticated!' } + const session = await getServerSession(event) + if (!session) { + return { status: 'unauthenticated!' } + } + return { status: 'authenticated!' } }) ``` @@ -41,14 +41,18 @@ export default eventHandler(async (event) => { ## Server Middleware You can also use this in a [Nuxt server middleware](https://nuxt.com/docs/guide/directory-structure/server#server-middleware) to protect multiple pages at once and keep the authentication logic out of your endpoints: + ```ts // file: ~/server/middleware/auth.ts import { getServerSession } from '#auth' export default eventHandler(async (event) => { - const session = await getServerSession(event) - if (!session) { - throw createError({ statusMessage: 'Unauthenticated', statusCode: 403 }) - } + const session = await getServerSession(event) + if (!session) { + throw createError({ + statusMessage: 'Unauthenticated', + statusCode: 403 + }) + } }) ``` diff --git a/docs/guide/authjs/session-data.md b/docs/guide/authjs/session-data.md new file mode 100644 index 00000000..2ab67449 --- /dev/null +++ b/docs/guide/authjs/session-data.md @@ -0,0 +1,118 @@ +# Session data + +This guide explains how to add custom data to the user session. In many cases, you may wish to adapt which information is returned by the authentication flow. This can depend on your provider or any additional API calls you may make to enrich the session data. + +## Modify the JWT Token + +In order to persist data between session requests, we need to inject certain information into the JWT token, which we can then access during subsequent session requests. However, avoid injecting too much data into the JWT token, as it is limited in size. We recommend only injecting an access or a session token, that can then be used to request further user information inside the session callback. + +The JWT callback provides: +- `token`: The raw JWT token +- `account`, `profile`, `isNewUser`: The data returned by the OAuth provider. This data varies by provider, we recommend logging each value to inspect what data is included. These values are only avalible on creation of the JWT token, in subsequent requests only the `token` will be accessible. + + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // your authentication configuration here! + callbacks: { + jwt({ token, account, profile }) { + if (account) { + token.sessionToken = account.session_token + } + return token + }, + } +}) +``` + +:::info +Any data injected into the JWT token, cannot be directly accessed from the frontend, however can be accessed on the server side using the `getToken` function. +::: + +## Inject data into the Session + +After enriching the JWT token with additional data, you can now access this data inside the `session` callback. The `session` callback is invoked every time the session data is requested. This can happen when using `useAuth`, `getServerSideSession` or when the session is refreshed. + +```ts +import { NuxtAuthHandler } from '#auth' + +export default NuxtAuthHandler({ + // your authentication configuration here! + callbacks: { + async session({ session, token }) { + // Token we injected into the JWT callback above. + const token = token.sessionToken + + // Fetch data OR add previous data from the JWT callback. + const additionalUserData = await $fetch(`/api/session/${token}`) + + // Return the modified session + return { + ...session, + user: { + name: additionalUserData.name, + avatar: additionalUserData.avatar, + role: additionalUserData.role + } + } + }, + } +}) +``` + +:::info +Any errors thrown inside the `session` callback will result in the session being terminated and the user being logged out. +::: + +--- + +That's it! You can now access your new session data inside your application. + +```vue + + + +``` + +## Typescript + +When modifying the session or JWT objects, you may want to adjust the types accordingly, to ensure that you get proper intellisense and type support. [Module Augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) can be used to inject additional type declarations into installed modules to overwrite or add additional data. + +Begin by creating a new typescript file in the root of your project named: `next-auth.d.ts`. Typescript will automatically recognize that this file is augmenting the module types of `next-auth` (running under the hood). + +```ts +// file: ~/next-auth.d.ts +import type { DefaultSession } from 'next-auth' + +declare module 'next-auth' { + /* Returned by `useAuth`, `getSession` and `getServerSession` */ + interface Session extends DefaultSession { + user: { + name: string, + avatar: string, + role: 'admin' | 'manager' | 'user' + } + } +} +``` + +In addition to modifying the `session` data types, you can also extend the types of the JWT token. This allows you to receive proper type support when accessing the JWT token inside the NuxtAuthHandler or when calling `getToken` on the server side. + +```ts +// file: ~/next-auth.d.ts +declare module 'next-auth/jwt' { + /** Returned by the `jwt` callback and `getToken` */ + interface JWT { + sessionToken?: string + } +} +``` diff --git a/docs/guide/getting-started/choose-provider.md b/docs/guide/getting-started/choose-provider.md new file mode 100644 index 00000000..aeb89c4a --- /dev/null +++ b/docs/guide/getting-started/choose-provider.md @@ -0,0 +1,39 @@ +--- +aside: false +--- + +# Which Provider should I pick? + +To pick a provider you will first have to take into consideration the requirements of your use-case. In general one can say that picking: + +- `authjs` is best suited for plug-and-play OAuth for established oauth-providers or magic-url based sign-ins +- `local` is best when you already have a backend that accepts username + password as a login or want to build a static application +- `refresh` if you would need to extend the functionality of the `local` provider, with a refresh token + +If you are still unsure, below are some tables to help you pick: + +### Authentication Methods + +| | authjs | local | refresh +|----------------------------------------------------------- |-------------------------------------: |------: | ------: +| OAuth | ✅ (>50 providers) | ❌ | ❌ +| Magic URLs | ✅ | ❌ | ❌ +| Credentials / Username + Password flow | 🚧 (if possible: use `local` instead) | ✅ | ✅ +| Refresh tokens | ✅ | ❌ | ✅ + +### Features + +| | authjs | local | refresh +|----------------------------------------------------------- |---------------------------------: |------: | ---: +| [`useAuth`-composable](/guide/application-side/session-access) to sign in/out, refresh a session, etc. | ✅ | ✅ | ✅ +| Session-management: auto-refresh, refresh on refocus, ... | ✅ | ✅ | ✅ +| Static apps ("nuxi generate") | ❌ | ✅ | ✅ +| [Guest mode](/guide/application-side/protecting-pages#guest-mode) | ✅ | ✅ | ✅ +| [App-side middleware](/guide/application-side/protecting-pages) | ✅ | ✅ | ✅ +| [Server-side middleware](/guide/authjs/server-side/session-access#endpoint-protection) | ✅ | ✅ | ✅ +| Pre-made login-page | ✅ (impacts bundle-size) | ❌ | ❌ +| Database-adapters, server-side callback-hooks | ✅ | ❌ | ❌ + +::: tip Still unsure what is best for you? +Join our [Discord](https://discord.gg/VzABbVsqAc) and share your use case! +::: diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md new file mode 100644 index 00000000..20283adb --- /dev/null +++ b/docs/guide/getting-started/installation.md @@ -0,0 +1,39 @@ +# Installation + +You can install NuxtAuth using nuxi: + +```bash +npx nuxi@latest module add sidebase-auth +``` + +::: details Manual installation + +::: code-group + +```bash [npm] +npm i -D @sidebase/nuxt-auth +``` + +```bash [pnpm] +pnpm i -D @sidebase/nuxt-auth +``` + +```bash [yarn] +yarn add --dev @sidebase/nuxt-auth +``` + +::: + +Add NuxtAuth to your `nuxt.config`: + +::: code-group + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + modules: [ + '@sidebase/nuxt-auth', + ], +}) +``` + +::: diff --git a/docs/guide/getting-started/introduction.md b/docs/guide/getting-started/introduction.md new file mode 100644 index 00000000..a4442ff5 --- /dev/null +++ b/docs/guide/getting-started/introduction.md @@ -0,0 +1,56 @@ +# Introduction + +NuxtAuth is an open source Nuxt module that provides authentication for Nuxt 3 applications. It supports multiple authentication methods, allowing you to customize the ways users login to your application. + +Through a direct integration into Nuxt, you can access and utlize the user sessions within your pages, components and composables directly. + +## Features + +### Authentication providers + +- OAuth (eg. Github, Google, Twitter, Azure...) +- Custom OAuth (Add your own!) +- Credentials (username / email + password) +- Email Magic URLs + +### Application Side Session Managment + +- Session fetching with `status`, `data` and `lastRefreshedAt` +- Methods to `getSession`, `getCsrfToken`, `getProviders`, `signIn` and `signOut` +- Full TypeScript support for all methods and properties + +### Application protection + +- Application-side middleware protection for the full application or specific pages +- Server-side middleware and endpoint protection + +## Common questions + +### Why does NuxtAuth require NextAuth? + +The `authjs` provider is able to provide all of its features by wrapping [Auth.js / NextAuth.js](https://github.com/nextauthjs/next-auth) under the hood. This gives the reliability & convenience of a >22.000 github star library to the Nuxt 3 ecosystem with a native nuxt developer experience (DX). Wrapping Auth.js / NextAuth.js has the second advantage that many OAuth providers, database adapters, callbacks and more are supported out-of-the-box. This also means that you can use all NextAuth.js and Auth.js guides and documentation to achieve things with the authjs provider of nuxt-auth. + +NuxtAuth also provides Nuxt 3 specific features like a convenient application-side composable to login, logout, access user-authentication data or an authentication middleware and plugin that take care of managing the user authentication lifecycle by fetching authentication data on initial load, refreshing the user authentication on re-focusing the tab and more. + +### What is the difference between Auth.js and NextAuth? + +We use authjs everywhere to mean authjs and next-auth interchangably as next-auth is currently transitioning to become authjs (branded name: [Auth.js](https://authjs.dev/)). + +We are following this transition and are changing code related to this as it becomes stable enough to use it. You can follow our implementation of this transition in [this issue](https://github.com/sidebase/nuxt-auth/issues/673). If you are googling anything related to this provider, we recommend that you still use the term `next-auth` as this is still the mainly used library and the stable one we mostly use under the hood. New features that are Auth.js only are _not_ guaranteed to work at the moment, as we still mostly rely on next-auth as a stable foundation. + +### NextAuth `GHSA-v64w-49xw-qq89` vulnerability + +NuxtAuth wraps NextAuth `v4.21.1` to provide the reliability and convenience of countless preconfigured and tested OAuth providers.In `v4.22` and above, NextAuth has changed their package exports, blocking NuxtAuth users from using the newer versions. + +NextAuth versions under `4.22` are impacted by vulnerability [GHSA-v64w-49xw-qq89](https://github.com/advisories/GHSA-v64w-49xw-qq89), after doing an internal investigation into this vulnerability we could determine that NuxtAuth applications using this version are not affected. + +::: details Further details +--- +#### Description of the vulnerability +The vulnerability [GHSA-v64w-49xw-qq89](https://github.com/advisories/GHSA-v64w-49xw-qq89) only affects applications that rely on the default [Middleware authorization](https://next-auth.js.org/configuration/nextjs#middleware) provided by NextAuth. + +The vulnerability allows attackers to create/mock a user, by accessing the JWT from an interrupted OAuth sign-in flow. They can then manually override the session cookie and simulate a login. However, doing this does **not** give access to the users data or permissions, but can allow attackers to view the layouts of protected pages. + +#### Why does it not effect NuxtAuth? +As the affected middleware is written for Next.js, we wrote our own [custom middleware](https://github.com/sidebase/nuxt-auth/blob/main/src/runtime/middleware/auth.ts) for NuxtAuth that is not affected by the vulnerability. +::: diff --git a/docs/guide/local/quick-start.md b/docs/guide/local/quick-start.md new file mode 100644 index 00000000..6a1b9794 --- /dev/null +++ b/docs/guide/local/quick-start.md @@ -0,0 +1,283 @@ +# Local / Refresh provider + +This guide is for setting up `@sidebase/nuxt-auth` with the Local / Refresh Provider, which is best suited for when you already have a backend that accepts username + password as a login or want to build a static application. + +The `refresh` provider is based on the `local` provider and extends its functionality by adding support for refresh tokens. + +## Configuration + +The entire configuration for the `local` and `refresh` providers is contained inside the `nuxt.config.ts`. Inside the `auth` options, set your provider to either `local` or `refresh`. In this example, we will configure the `local` provider, however the same configuration applies to the `refresh` provider as well. + +```ts +export default defineNuxtConfig({ + modules: ['@sidebase/nuxt-auth'], + auth: { + baseURL: '/api/auth', + provider: { + type: 'local' // or 'refresh' + } + } +}) +``` + + +:::tip +Ensure that your `baseURL` is properly configured to match your backend API. Read more [here](/guide/application-side/configuration#local-and-refresh). +::: + +## API endpoints + +Afterwards, you can define the endpoints to which the authentication requests will be made: + +```ts +export default defineNuxtConfig({ + // ...Previous configuration + auth: { + baseURL: '/api/auth' + provider: { + type: 'local', + endpoints: { + signIn: { path: '/login', method: 'post' }, + signOut: { path: '/logout', method: 'post' }, + signUp: { path: '/register', method: 'post' }, + getSession: { path: '/session', method: 'get' }, + } + } + } +)} +``` + +Each endpoint, consists of an object, with a `path` and `method`. When a user triggers an action inside your application a request will be made to each endpoint. When a request is made to the `getSession` endpoint, a token will be sent as a header. You can configure the headers and token below. + +In the example above requests would be made to the following URLs: + +- **Sign in:** `/api/auth/login` (POST) +- **Sign out** `/api/auth/logout` (POST) +- **Sign up:** `/api/auth/register` (POST) +- **Get Session:** `/api/auth/session` (GET) + +:::info +Relative paths starting with a `/` (e.g. `/login`) will be treated as a part of your Nuxt application. If you want to use an external backend, please provide fully-specified URLs instead. +::: + +You can customize each endpoint to fit your needs or disable it by setting it to `false`. For example you may want to disable the `signUp` endpoint. + +```ts{7} +{ + auth: { + baseURL: '/api/auth', + provider: { + type: 'local', + endpoints: { + signUp: false + } + } + } +} +``` + +:::warning +You cannot disable the `getSession` endpoint, as NuxtAuth internally uses it to determine the authentication status. +::: + +### Refresh provider + +If you are using the refresh provider, you can additionally define a `refresh` endpoint, which will be used to refresh the access token upon expiry. + +```ts +endpoints: { + signIn: { path: '/login', method: 'post' }, + signOut: { path: '/logout', method: 'post' }, + signUp: { path: '/register', method: 'post' }, + getSession: { path: '/session', method: 'get' }, + refresh: { path: '/refresh', method: 'post' } // [!code ++] +} +``` + +## Token + +The `local` and `refresh` providers are both based on exchanging access tokens with your backend. NuxtAuth expects an access token to be provided by the `signIn` endpoint, which will then be saved into the session to authenticate further requests to e.g. `getSession`. + +The configuration of the `token` properties depend on how your backend accepts and returns data. The options are designed to be as adaptable as possible, to account for many different types of backends. + +```ts +export default defineNuxtConfig({ + // Previous configuration + auth: { + provider: { + type: 'local', + token: { + signInResponseTokenPointer: '/token', + type: 'Bearer', + cookieName: 'auth.token', + headerName: 'Authorization', + maxAgeInSeconds: 1800, + sameSiteAttribute: 'lax', + cookieDomain: 'sidebase.io' + } + } + } +}) +``` + +### `signInResponseTokenPointer` + +- **Type:** `string` +- **Default:** `'/token'` + +How to extract the authentication-token from the sign-in response. + +For example, if you have a response object like `{ token: { bearer: 'THE_AUTH_TOKEN' }, timestamp: '2023' }`, using `signInResponseTokenPointer: '/token/bearer'` will result in `nuxt-auth` extracting and storing `THE_AUTH_TOKEN`. + +This follows the JSON Pointer standard, see it's RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + +### `type` + +Header type to be used in requests. This in combination with `headerName` is used to construct the final authentication-header `nuxt-auth` uses, e.g. for requests via `getSession`. + +- **Type:** `string` +- **Default:** `'Bearer'` + +### `cookieName` + +Refers to the name of the property when it is stored in a cookie. + +- **Type:** `string` +- **Default:** `'auth.token'` + +### `headerName` + +Header name to be used in requests that need to be authenticated, e.g., to be used in the `getSession` request. + +- **Type:** `string` +- **Default:** `'Authorization'` + +### `maxAgeInSeconds` + +Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e. in the user's browser. + +Note: Your backend may reject / expire the token earlier / differently. + +- **Type:** `number` +- **Default:** `1800` + +### `sameSiteAttribute` + +The cookie sameSite policy. Can be used as a form of CSRF protection. If set to `strict`, the cookie will only be passed with requests to the same 'site'. Typically, this includes subdomains. So, a `sameSite: strict` cookie set by app.mysite.com will be passed to api.mysite.com, but not api.othersite.com. + +See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7 + +- **Type:** `boolean | 'lax' | 'strict' | 'none' | undefined` +- **Default:** `'lax'` + +### `cookieDomain` + +The cookie domain. See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.3 + +- **Type:** `string` +- **Default:** `''` + +## Refresh token + +:::tip +This section only applies to applications using the `refresh` provider. +::: + +If using the `refresh` provider, a seperate `refreshToken` configuration can also be passed to configure how the refresh token is handled. + +```ts +export default defineNuxtConfig({ + // Previous configuration + auth: { + provider: { + type: 'local', + token: { + signInResponseTokenPointer: '/token', + type: 'Bearer', + cookieName: 'auth.token', + headerName: 'Authorization', + maxAgeInSeconds: 1800, + sameSiteAttribute: 'lax', + cookieDomain: 'sidebase.io' + }, + refreshToken: { + signInResponseRefreshTokenPointer: '/refresh-token', + refreshRequestTokenPointer: 'Bearer', + cookieName: 'auth.token', + maxAgeInSeconds: 1800, + cookieDomain: 'sidebase.io' + } + } + } +}) +``` + +### `signInResponseRefreshTokenPointer` + +- **Type:** `string` +- **Default:** `'/refreshToken'` + +How to extract the authentication-refreshToken from the sign-in response. + +E.g., setting this to `/token/refreshToken` and returning an object like `{ token: { refreshToken: 'THE_REFRESH__TOKEN' }, timestamp: '2023' }` from the `signIn` endpoint will result in `nuxt-auth` extracting and storing `THE_REFRESH__TOKEN`. + +This follows the JSON Pointer standard, see its RFC6901 here: https://www.rfc-editor.org/rfc/rfc6901 + +### `refreshRequestTokenPointer` + +- **Type:** `string` +- **Default:** `'/refreshToken'` + +How to do a fetch for the refresh token. This is especially useful when you have an external backend signing tokens. Refer to this issue to get more information: https://github.com/sidebase/nuxt-auth/issues/635. + +### `cookieName` + +- **Type:** `string` +- **Default:** `'auth.refresh-token'` + +It refers to the name of the property when it is stored in a cookie. + +### `maxAgeInSeconds` + +- **Type:** `number` +- **Default:** `1800` + +Maximum age to store the authentication token for. After the expiry time the token is automatically deleted on the application side, i.e. in the user's browser. + +Note: Your backend may reject / expire the refreshToken earlier / differently. + +### `cookieDomain` + +- **Type:** `string` +- **Default:** `''` + +The cookie domain. See the specification here: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.3 + +## `refreshOnlyToken` + +:::tip +This section only applies to applications using the `refresh` provider. +::: + +- **Type:** `boolean` +- **Default:** `true` + +When refreshOnlyToken is set, only the token will be refreshed + +## Pages + +Configure the path of the login-page that the user should be redirected to, when they try to access a protected page without being logged in. This page will also not be blocked by the global middleware. + +```ts +export default defineNuxtConfig({ + // previous configuration + auth: { + provider: { + type: 'local', + pages: { + login: '/login' + } + } + } +}) +``` diff --git a/docs/guide/local/session-data.md b/docs/guide/local/session-data.md new file mode 100644 index 00000000..a7491ad7 --- /dev/null +++ b/docs/guide/local/session-data.md @@ -0,0 +1,61 @@ +# Session data + +To configure the types returned by your backend API, you can define an additional object inside the `nuxt.config.ts` that outlines the types of your session data object. + +```ts +export default defineNuxtConfig({ + auth: { + provider: { + type: 'local', + sessionDataType: { + id: 'string | number', + firstName: 'string', + lastName: 'string' + } + } + } +}) +``` + +In the example above, NuxtAuth will properly infer the types from the configuration and return: + +```ts +type SessionData = { + id: string | number, + firstName: string + lastName: string +} +``` + +## Complex types + +In addition to using simple type such as `string`, `number` or `boolean` you can also configure more complex types such as sub-objects or arrays. + +```ts +export default defineNuxtConfig({ + auth: { + provider: { + type: 'local', + sessionDataType: { + id: 'string | number', + firstName: 'string', + lastName: 'string', + subscriptions: "{ id: number, active: boolean}[]" + } + } + } +}) +``` + +Example above will generate the following type for your session: +```ts +type SessionConfig = { + id: string | number, + firstName: string + lastName: string + subscriptions: { id: number, status: boolean }[] +} +``` + + +This allows you to properly match the types to the return values of `getSession`. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..321dfd70 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,54 @@ +--- +layout: home + +title: Auth + +hero: + name: NuxtAuth + text: Authentication for Nuxt 3! + tagline: User authentication and sessions via authjs! + actions: + - theme: brand + text: Get started! + link: /guide/getting-started/introduction + - theme: alt + text: GitHub + link: https://github.com/sidebase/nuxt-auth + image: + src: /lock-and-key.png + alt: NuxtAuth + +features: + - icon: + src: /icons/Github.png + title: OAuth + details: Effortlessly connect your Nuxt 3 application with Google, Github, Azure and countless others. + - icon: + src: /icons/Database.png + title: Use your own backend + details: Use the local and refresh providers, to define your endpoints and watch the magic happen. + - icon: + src: /icons/Layers.png + title: Middleware + details: Easily define the authentication rules for all of your pages. + - icon: + src: /icons/Server.png + title: REST API + details: Everything authentication related automatically gets servered as new API routes. + - icon: + src: /icons/Mail.png + title: Magic URLs + details: Allow your users to login with magic links sent by mail. + - icon: + src: /icons/Refresh.png + title: Refresh Tokens + details: Refresh a users access token in the background. + - icon: + src: /icons/Speedometer.png + title: Made for Nuxt + details: Deep integrations with the Nuxt framework. + - icon: + src: /icons/Rocket.png + title: Easy deployment + details: Deploy to Vercel, Netlify or self-host. +--- diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts deleted file mode 100755 index 291237f5..00000000 --- a/docs/nuxt.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default defineNuxtConfig({ - extends: '@nuxt-themes/docus' -}) diff --git a/docs/package.json b/docs/package.json old mode 100755 new mode 100644 index c77c7978..d4ea1e27 --- a/docs/package.json +++ b/docs/package.json @@ -1,15 +1,12 @@ { - "name": "docus-starter", - "version": "0.1.0", + "name": "@sidebase/nuxt-auth-docs", "private": true, "scripts": { - "dev": "nuxi dev", - "build": "nuxi build", - "generate": "nuxi generate", - "preview": "nuxi preview" + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" }, "devDependencies": { - "@nuxt-themes/docus": "^1.15.0", - "nuxt": "^3.11.2" + "vitepress": "^1.1.4" } } diff --git a/docs/public/authjs/morpheme-auth-screenshot.png b/docs/public/authjs/morpheme-auth-screenshot.png new file mode 100644 index 00000000..3532c2b1 Binary files /dev/null and b/docs/public/authjs/morpheme-auth-screenshot.png differ diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 00000000..b3d1e39f Binary files /dev/null and b/docs/public/favicon.ico differ diff --git a/docs/public/icons/Database.png b/docs/public/icons/Database.png new file mode 100755 index 00000000..96c846e5 Binary files /dev/null and b/docs/public/icons/Database.png differ diff --git a/docs/public/icons/Github.png b/docs/public/icons/Github.png new file mode 100755 index 00000000..344a7cd3 Binary files /dev/null and b/docs/public/icons/Github.png differ diff --git a/docs/public/icons/Layers.png b/docs/public/icons/Layers.png new file mode 100755 index 00000000..19ca9d27 Binary files /dev/null and b/docs/public/icons/Layers.png differ diff --git a/docs/public/icons/Mail.png b/docs/public/icons/Mail.png new file mode 100755 index 00000000..6bda6838 Binary files /dev/null and b/docs/public/icons/Mail.png differ diff --git a/docs/public/icons/Refresh.png b/docs/public/icons/Refresh.png new file mode 100755 index 00000000..f468caca Binary files /dev/null and b/docs/public/icons/Refresh.png differ diff --git a/docs/public/icons/Rocket.png b/docs/public/icons/Rocket.png new file mode 100755 index 00000000..e8482bf1 Binary files /dev/null and b/docs/public/icons/Rocket.png differ diff --git a/docs/public/icons/Server.png b/docs/public/icons/Server.png new file mode 100755 index 00000000..53ffc7f8 Binary files /dev/null and b/docs/public/icons/Server.png differ diff --git a/docs/public/icons/Speedometer.png b/docs/public/icons/Speedometer.png new file mode 100755 index 00000000..923ea162 Binary files /dev/null and b/docs/public/icons/Speedometer.png differ diff --git a/docs/public/lock-and-key.png b/docs/public/lock-and-key.png new file mode 100644 index 00000000..f2236182 Binary files /dev/null and b/docs/public/lock-and-key.png differ diff --git a/docs/public/lock.png b/docs/public/lock.png new file mode 100644 index 00000000..1c0429d8 Binary files /dev/null and b/docs/public/lock.png differ diff --git a/docs/public/nuxt-auth/nuxt-auth-example-preview.png b/docs/public/nuxt-auth/nuxt-auth-example-preview.png deleted file mode 100644 index 9433e09c..00000000 Binary files a/docs/public/nuxt-auth/nuxt-auth-example-preview.png and /dev/null differ diff --git a/docs/public/nuxt-auth/vercel/create-project.png b/docs/public/nuxt-auth/vercel/create-project.png deleted file mode 100644 index 63b2d2e0..00000000 Binary files a/docs/public/nuxt-auth/vercel/create-project.png and /dev/null differ diff --git a/docs/public/nuxt-auth/vercel/env.png b/docs/public/nuxt-auth/vercel/env.png deleted file mode 100644 index 4eb4e725..00000000 Binary files a/docs/public/nuxt-auth/vercel/env.png and /dev/null differ diff --git a/docs/public/nuxt-auth/vercel/promote_production.png b/docs/public/nuxt-auth/vercel/promote_production.png deleted file mode 100644 index 003b93be..00000000 Binary files a/docs/public/nuxt-auth/vercel/promote_production.png and /dev/null differ diff --git a/docs/recipes/overview.md b/docs/recipes/overview.md new file mode 100644 index 00000000..a195a28a --- /dev/null +++ b/docs/recipes/overview.md @@ -0,0 +1,3 @@ +# Coming soon + +Oops, this page is not ready yet. You can find our current docs [here](https://sidebase.io). diff --git a/docs/renovate.json b/docs/renovate.json deleted file mode 100755 index 75ac0dd3..00000000 --- a/docs/renovate.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": [ - "@nuxtjs" - ], - "lockFileMaintenance": { - "enabled": true - } -} diff --git a/docs/content/6.resources/5.errors.md b/docs/resources/error-reference.md similarity index 57% rename from docs/content/6.resources/5.errors.md rename to docs/resources/error-reference.md index 5ed65729..0f4e4208 100644 --- a/docs/content/6.resources/5.errors.md +++ b/docs/resources/error-reference.md @@ -1,13 +1,11 @@ ---- -description: "A list of errors that `nuxt-auth` throws, what they mean and how you can resolve them." ---- -# Errors and Warnings +# Error and warnings -This is a list of errors & warnings that `nuxt-auth` throws, what each of them means and how you can resolve them. +This is a list of errors & warnings that NuxtAuth throws, what each of them means and how you can resolve them. ## AUTH_NO_SECRET -`AUTH_NO_SECRET` will appear as a warning message during development and be thrown as an error that stops the application during production. It is safe to ignore the development warning - it is only meant as a heads-up for your later production-deployment. `AUTH_NO_SECRET` occurs when no `secret` was set inside the `NuxtAuthHandler`: +`AUTH_NO_SECRET` will appear as a warning message during development and be thrown as an error that stops the application during production. It is safe to ignore the development warning - it is only meant as a heads-up for your later production-deployment. `AUTH_NO_SECRET` occurs when no secret was set inside the NuxtAuthHandler: + ```ts // file: ~/server/api/auth/[...].ts import { NuxtAuthHandler } from '#auth' @@ -19,15 +17,12 @@ export default NuxtAuthHandler({ }) ``` -The `secret` is important to encode the cookies / tokens of your app-users. It is mandatory for production and should be something random, long and secret that only you and priviledged personnal knows about. In development it can be omitted and will be replaced with a fake-value. To be clear: This fake value is insecure and should never be used in production. - -Read [the `NuxtAuthHandler` docs for more information and different options you can use to set the `secret` at runtime](/nuxt-auth/v0.6/configuration/nuxt-auth-handler). - ## AUTH_NO_ORIGIN -`AUTH_NO_ORIGIN` will appear as a warning message during development and be thrown as an error that stops the application during production. It is safe to ignore the development warning - it is only meant as a heads-up for your later production-deployment. `AUTH_NO_ORIGIN` occurs when the origin of your application was not set. `nuxt-auth` tries to find the origin of your application in the following order: +`AUTH_NO_ORIGIN` will appear as a warning message during development and be thrown as an error that stops the application during production. It is safe to ignore the development warning - it is only meant as a heads-up for your later production-deployment. `AUTH_NO_ORIGIN` occurs when the origin of your application was not set. NuxtAuth tries to find the origin of your application in the following order: + 1. Use the `AUTH_ORIGIN` environment variable if it is set, 2. Development only: Determine the origin automatically from the incoming HTTP request -The `origin` is important for callbacks that happen to a specific origin for `oauth` flows. Note that in order for (2) to work the `origin` already has to be set at build-time, i.e., when you run `npm run build` or `npm run generate` and it will lead to the `origin` being inside your app-bundle. +The `origin` is important for callbacks that happen to a specific origin for `oauth` flows. Note that in order for (2) to work the `origin` already has to be set at build-time, i.e., when you run `npm run build` or `npm run generate` and it will lead to the `origin` being inside your app-bundle. diff --git a/docs/resources/overview.md b/docs/resources/overview.md new file mode 100644 index 00000000..0a7658ed --- /dev/null +++ b/docs/resources/overview.md @@ -0,0 +1,35 @@ +# Resources + +The following resources should help with using NuxtAuth and tackle some topics that are frequently encountered during app-development. We will continue to expand this with more guides, recipes and references based on the questions, feedback and problems we see and receive from our community: https://discord.gg/VzABbVsqAc + +## Glossary + +There are some terms we use in this documentation that may not be known to every reader. Here is an explanation for some of them: + +- `application` / `application-side` / `universal-application`: This references all Nuxt code of your app that is [universally rendered](https://nuxt.com/docs/guide/concepts/rendering#universal-rendering). In short this means that that code is rendered on the server-side and on the client-side, so all JS in it is executed twice. This is an important distinction, as some things may behave different on the server-side than on the client-side. We use `application...` to denote something that will be universally rendered +- `server` / `server-side`: This references all Nuxt code of your app that will run only on your server. For example, all code inside the `~/server directory` should only ever run on the server +- `authentication`: Verifying that someone is who they claims to be, e.g., by asking them for a username and password or by asking Google to verify it (OAuth) and then trust their result +- `provider`: Could mean two things in the context of NuxAuth: + - The authentication provider you select on the NuxtAuth module level by setting the `provider.type` key to either `local`, `refresh` or `authjs` + - The OAuth authentication provider you choose to use in combination with the `authjs` module + +## Module Concept + +The idea of this library is to re-use all the open-source implementation that already exist in the JS ecosystem instead of rolling our own. The idea was born when researching through the ecosystem of framework-specific authentication libraries to figure out what the best implementation approach for a state-of-the-art Nuxt 3 authentication library would be. + +During research it became clear that implementing everything from scratch will be: +- a lot of work that has already been open-sourced by others, +- error prone as authentication has a lot of intricacies that need to be resolved in order to get it right, +- hard to maintain as authentication providers come and go, +- hard to build initial trust for as authentication is important and cannot go wrong, + +In order to avoid these problems without taking forever (leaving Nuxt without an authentication library in the meantime), we decided to investigate if we can wrap [NextAuth.js](https://github.com/nextauthjs/next-auth), the most popular authentication library in the Next.js ecosystem by far and a trusted, well maintained one at that! + +In our investigation we found prior attempts to make NextAuth.js framework agnostic. These have more or less come to fruition, so far mostly resulting in some PoCs and example apps. Looking at these was quite helpful to get started. In particular, big pushes in the right direction came from: + +- [NextAuth.js app examples](https://github.com/nextauthjs/next-auth/tree/main/apps) +- [Various comments, proposals, ...or this thread](https://github.com/nextauthjs/next-auth/discussions/3942), special thanks to [brillout](https://github.com/brillout) for starting the discussion, [balazsorban44](https://github.com/balazsorban44) for NextAuth.js and encouraging the discussion, [wobsoriano](https://github.com/wobsoriano) for adding PoCs for multiple languages + +The main part of the work was to piece everything together, resolve some outstanding issues with existing PoCs, add new things where nothing existed yet, e.g., for the `useAuth` composable by going through the NextAuth.js client code and translating it to a Nuxt 3 approach. + +The module had another big iteration in collaboration with [JoaoPedroAS51](https://github.com/JoaoPedroAS51) to make `useAuth` a sync operation and trigger the session lifecycle from a plugin rather than the `useAuth` composable itself. diff --git a/docs/content/6.resources/3.security.md b/docs/resources/security.md similarity index 85% rename from docs/content/6.resources/3.security.md rename to docs/resources/security.md index 5d59eb3c..1339f88d 100644 --- a/docs/content/6.resources/3.security.md +++ b/docs/resources/security.md @@ -1,17 +1,19 @@ # Security This section mostly contains a list of possible security problems. Note that the below flaws exist with many libraries and frameworks we use in our day-to-day when building and working with APIs. Even your vanilla Nuxt app already posesses some of these shortcoming. Missing in the below list are estimates of how likely it is that one of the list-items may occur and what impact it will have on your app. This is because that heavily depends on: + - your app: Are you building a fun project? A proof of concept? The next fort-nox money management app? - your environment: Building a freely available app for fun? Have authentication in front of your app and trust all users that successfully authenticated? Superb! Don't trust anyone? Then please be extra-careful when using this library and when building you backend in general Without further ado, here's some attack cases you can consider and take action against. Neither the attack vectors, the problems or the mitigations are exhaustive: + 1. sending arbitrary data: Denial-of-Service by server-resource exhaustion (bandwidth, cpu, memory), arbitrary code execution (if you parse the data), ... 2. creation arbitrarily many sessions: Denial-of-Service by server-resource exhaustion (bandwidth, cpu, memory) 3. guessing correct session ids: session data can leak 4. stealing session id(s) of client(s): session data can leak -Read up how to mitigate these and more issues if you see fit. Checkout the [`nuxt-security`](https://github.com/Baroshem/nuxt-security) module that may help with some of these. +Read up how to mitigate these and more issues if you see fit. Checkout the [nuxt-security module](https://nuxt-security.vercel.app/) that may help with some of these. ## Disclosure -A last reminder: This library was not written by crypto- or security-experts. Please proceed at your own risk, inspect the code if you want to and open issues / pull requests where you see room for improvement. If you want to file a security-concern privately, please send an email to `sidebase@sidestream.tech` with the subject saying "SECURITY nuxt-auth" and we'll look into your request ASAP. \ No newline at end of file +A last reminder: This library was not written by crypto- or security-experts. Please proceed at your own risk, inspect the code if you want to and open issues / pull requests where you see room for improvement. If you want to file a security-concern privately, please send an email to [sidebase@sidestream.tech](mailto:sidebase@sidestream.tech?subject=SECURITY-NuxtAuth) with the subject saying "SECURITY nuxt-auth" and we'll look into your request ASAP. diff --git a/docs/tsconfig.json b/docs/tsconfig.json deleted file mode 100755 index 4b34df15..00000000 --- a/docs/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./.nuxt/tsconfig.json" -} diff --git a/package.json b/package.json index 42ab899f..254f9504 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "clean": "rm -rf playground-authjs/.nuxt playground-local/.nuxt playground-refresh/.nuxt dist .nuxt", "typecheck": "nuxi prepare playground-local && tsc --noEmit", "typecheck:refresh": "nuxi prepare playground-refresh && tsc --noEmit", - "dev:prepare": "nuxt-module-build build --stub" + "dev:prepare": "nuxt-module-build build --stub", + "docs:dev": "cd ./docs && pnpm docs:dev" }, "dependencies": { "@nuxt/kit": "^3.11.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81c3b0b6..7b9ae46b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,12 +66,9 @@ importers: docs: devDependencies: - '@nuxt-themes/docus': - specifier: ^1.15.0 - version: 1.15.0(nuxt@3.11.2)(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23) - nuxt: - specifier: ^3.11.2 - version: 3.11.2(@types/node@18.19.31)(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(rollup@3.29.4)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) + vitepress: + specifier: ^1.1.4 + version: 1.2.3(@algolia/client-search@4.24.0)(@types/node@18.19.31)(postcss@8.4.38)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.14.0)(typescript@5.3.3) playground-authjs: devDependencies: @@ -139,7 +136,7 @@ importers: devDependencies: '@nuxt/test-utils': specifier: ^3.12.1 - version: 3.12.1(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23) + version: 3.12.1(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(rollup@3.29.4)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23) '@playwright/test': specifier: ^1.43.1 version: 1.43.1 @@ -154,13 +151,13 @@ importers: version: 8.57.0 nuxt: specifier: ^3.11.2 - version: 3.11.2(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) + version: 3.11.2(@types/node@18.19.31)(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(rollup@3.29.4)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) typescript: specifier: ^5.3.3 version: 5.3.3 vitest: specifier: ^1.5.0 - version: 1.5.0 + version: 1.5.0(@types/node@18.19.31) vue-tsc: specifier: ^1.8.27 version: 1.8.27(typescript@5.3.3) @@ -172,6 +169,156 @@ packages: engines: {node: '>=0.10.0'} dev: true + /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0): + resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + dev: true + + /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0): + resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==} + peerDependencies: + search-insights: '>= 1 < 3' + dependencies: + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + search-insights: 2.14.0 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + dev: true + + /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0): + resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + dependencies: + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@algolia/client-search': 4.24.0 + algoliasearch: 4.24.0 + dev: true + + /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0): + resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + dependencies: + '@algolia/client-search': 4.24.0 + algoliasearch: 4.24.0 + dev: true + + /@algolia/cache-browser-local-storage@4.24.0: + resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==} + dependencies: + '@algolia/cache-common': 4.24.0 + dev: true + + /@algolia/cache-common@4.24.0: + resolution: {integrity: sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==} + dev: true + + /@algolia/cache-in-memory@4.24.0: + resolution: {integrity: sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==} + dependencies: + '@algolia/cache-common': 4.24.0 + dev: true + + /@algolia/client-account@4.24.0: + resolution: {integrity: sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==} + dependencies: + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + + /@algolia/client-analytics@4.24.0: + resolution: {integrity: sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==} + dependencies: + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + + /@algolia/client-common@4.24.0: + resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==} + dependencies: + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + + /@algolia/client-personalization@4.24.0: + resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==} + dependencies: + '@algolia/client-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + + /@algolia/client-search@4.24.0: + resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==} + dependencies: + '@algolia/client-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + + /@algolia/logger-common@4.24.0: + resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==} + dev: true + + /@algolia/logger-console@4.24.0: + resolution: {integrity: sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==} + dependencies: + '@algolia/logger-common': 4.24.0 + dev: true + + /@algolia/recommend@4.24.0: + resolution: {integrity: sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==} + dependencies: + '@algolia/cache-browser-local-storage': 4.24.0 + '@algolia/cache-common': 4.24.0 + '@algolia/cache-in-memory': 4.24.0 + '@algolia/client-common': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/logger-console': 4.24.0 + '@algolia/requester-browser-xhr': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + + /@algolia/requester-browser-xhr@4.24.0: + resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==} + dependencies: + '@algolia/requester-common': 4.24.0 + dev: true + + /@algolia/requester-common@4.24.0: + resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==} + dev: true + + /@algolia/requester-node-http@4.24.0: + resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==} + dependencies: + '@algolia/requester-common': 4.24.0 + dev: true + + /@algolia/transporter@4.24.0: + resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} + dependencies: + '@algolia/cache-common': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/requester-common': 4.24.0 + dev: true + /@ampproject/remapping@2.3.0: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -420,6 +567,14 @@ packages: dependencies: '@babel/types': 7.24.0 + /@babel/parser@7.24.7: + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/plugin-proposal-decorators@7.24.1(@babel/core@7.24.4): resolution: {integrity: sha512-zPEvzFijn+hRvJuX2Vu3KbEBN39LN3f7tW3MQO2LsIs57B26KU+kUc82BdAktS1VCM6libzh45eKGI65lg0cpA==} engines: {node: '>=6.9.0'} @@ -569,29 +724,49 @@ packages: dependencies: mime: 3.0.0 - /@csstools/cascade-layer-name-parser@1.0.9(@csstools/css-parser-algorithms@2.6.1)(@csstools/css-tokenizer@2.2.4): - resolution: {integrity: sha512-RRqNjxTZDUhx7pxYOBG/AkCVmPS3zYzfE47GEhIGkFuWFTQGJBgWOUUkKNo5MfxIfjDz5/1L3F3rF1oIsYaIpw==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - '@csstools/css-parser-algorithms': ^2.6.1 - '@csstools/css-tokenizer': ^2.2.4 - dependencies: - '@csstools/css-parser-algorithms': 2.6.1(@csstools/css-tokenizer@2.2.4) - '@csstools/css-tokenizer': 2.2.4 + /@docsearch/css@3.6.0: + resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} dev: true - /@csstools/css-parser-algorithms@2.6.1(@csstools/css-tokenizer@2.2.4): - resolution: {integrity: sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - '@csstools/css-tokenizer': ^2.2.4 + /@docsearch/js@3.6.0(@algolia/client-search@4.24.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.14.0): + resolution: {integrity: sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==} dependencies: - '@csstools/css-tokenizer': 2.2.4 + '@docsearch/react': 3.6.0(@algolia/client-search@4.24.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.14.0) + preact: 10.19.4 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights dev: true - /@csstools/css-tokenizer@2.2.4: - resolution: {integrity: sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw==} - engines: {node: ^14 || ^16 || >=18} + /@docsearch/react@3.6.0(@algolia/client-search@4.24.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.14.0): + resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + dependencies: + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.14.0) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@docsearch/css': 3.6.0 + algoliasearch: 4.24.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + search-insights: 2.14.0 + transitivePeerDependencies: + - '@algolia/client-search' dev: true /@esbuild/aix-ppc64@0.19.12: @@ -611,11 +786,11 @@ packages: requiresBuild: true optional: true - /@esbuild/android-arm64@0.17.19: - resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} - cpu: [arm64] - os: [android] + cpu: [ppc64] + os: [aix] requiresBuild: true dev: true optional: true @@ -637,10 +812,10 @@ packages: requiresBuild: true optional: true - /@esbuild/android-arm@0.17.19: - resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [android] requiresBuild: true dev: true @@ -663,10 +838,10 @@ packages: requiresBuild: true optional: true - /@esbuild/android-x64@0.17.19: - resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm] os: [android] requiresBuild: true dev: true @@ -689,11 +864,11 @@ packages: requiresBuild: true optional: true - /@esbuild/darwin-arm64@0.17.19: - resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] requiresBuild: true dev: true optional: true @@ -715,10 +890,10 @@ packages: requiresBuild: true optional: true - /@esbuild/darwin-x64@0.17.19: - resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm64] os: [darwin] requiresBuild: true dev: true @@ -741,11 +916,11 @@ packages: requiresBuild: true optional: true - /@esbuild/freebsd-arm64@0.17.19: - resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] requiresBuild: true dev: true optional: true @@ -767,10 +942,10 @@ packages: requiresBuild: true optional: true - /@esbuild/freebsd-x64@0.17.19: - resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm64] os: [freebsd] requiresBuild: true dev: true @@ -793,11 +968,11 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-arm64@0.17.19: - resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] requiresBuild: true dev: true optional: true @@ -819,10 +994,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-arm@0.17.19: - resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [linux] requiresBuild: true dev: true @@ -845,10 +1020,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-ia32@0.17.19: - resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} - cpu: [ia32] + cpu: [arm] os: [linux] requiresBuild: true dev: true @@ -871,10 +1046,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-loong64@0.17.19: - resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} - cpu: [loong64] + cpu: [ia32] os: [linux] requiresBuild: true dev: true @@ -897,10 +1072,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-mips64el@0.17.19: - resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} - cpu: [mips64el] + cpu: [loong64] os: [linux] requiresBuild: true dev: true @@ -923,10 +1098,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-ppc64@0.17.19: - resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] requiresBuild: true dev: true @@ -949,10 +1124,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-riscv64@0.17.19: - resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] requiresBuild: true dev: true @@ -975,10 +1150,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-s390x@0.17.19: - resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} - cpu: [s390x] + cpu: [riscv64] os: [linux] requiresBuild: true dev: true @@ -1001,10 +1176,10 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-x64@0.17.19: - resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} - cpu: [x64] + cpu: [s390x] os: [linux] requiresBuild: true dev: true @@ -1027,11 +1202,11 @@ packages: requiresBuild: true optional: true - /@esbuild/netbsd-x64@0.17.19: - resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] - os: [netbsd] + os: [linux] requiresBuild: true dev: true optional: true @@ -1053,11 +1228,11 @@ packages: requiresBuild: true optional: true - /@esbuild/openbsd-x64@0.17.19: - resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] - os: [openbsd] + os: [netbsd] requiresBuild: true dev: true optional: true @@ -1079,11 +1254,11 @@ packages: requiresBuild: true optional: true - /@esbuild/sunos-x64@0.17.19: - resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] - os: [sunos] + os: [openbsd] requiresBuild: true dev: true optional: true @@ -1105,11 +1280,11 @@ packages: requiresBuild: true optional: true - /@esbuild/win32-arm64@0.17.19: - resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] requiresBuild: true dev: true optional: true @@ -1131,10 +1306,10 @@ packages: requiresBuild: true optional: true - /@esbuild/win32-ia32@0.17.19: - resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} - cpu: [ia32] + cpu: [arm64] os: [win32] requiresBuild: true dev: true @@ -1157,10 +1332,10 @@ packages: requiresBuild: true optional: true - /@esbuild/win32-x64@0.17.19: - resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} - cpu: [x64] + cpu: [ia32] os: [win32] requiresBuild: true dev: true @@ -1183,6 +1358,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1278,15 +1462,6 @@ packages: - supports-color dev: true - /@iconify/vue@4.1.1(vue@3.4.23): - resolution: {integrity: sha512-RL85Bm/DAe8y6rT6pux7D2FJSiUEM/TPfyK7GrbAOfTSwrhvwJW+S5yijdGcmtXouA8MtuH9C7l4hiSE4mLMjg==} - peerDependencies: - vue: '>=3' - dependencies: - '@iconify/types': 2.0.0 - vue: 3.4.23(typescript@5.3.3) - dev: true - /@ioredis/commands@1.2.0: resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} @@ -1579,221 +1754,59 @@ packages: - supports-color dev: true - /@nuxt-themes/docus@1.15.0(nuxt@3.11.2)(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-V2kJ5ecGUxXcEovXeQkJBPYfQwjmjaxB5fnl2XaQV+S2Epcn+vhPWShSlL6/WXzLPiAkQFdwbBj9xedTvXgjkw==} - dependencies: - '@nuxt-themes/elements': 0.9.5(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23) - '@nuxt-themes/tokens': 1.9.1(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23) - '@nuxt-themes/typography': 0.11.0(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23) - '@nuxt/content': 2.12.1(nuxt@3.11.2)(rollup@3.29.4)(vue@3.4.23) - '@nuxthq/studio': 1.0.13(rollup@3.29.4) - '@vueuse/integrations': 10.9.0(focus-trap@7.5.4)(fuse.js@6.6.2)(vue@3.4.23) - '@vueuse/nuxt': 10.9.0(nuxt@3.11.2)(rollup@3.29.4)(vue@3.4.23) - focus-trap: 7.5.4 - fuse.js: 6.6.2 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/kv' - - '@vue/composition-api' - - async-validator - - axios - - bufferutil - - change-case - - drauu - - idb-keyval - - ioredis - - jwt-decode - - nprogress - - nuxt - - postcss - - qrcode - - rollup - - sass - - sortablejs - - supports-color - - uWebSockets.js - - universal-cookie - - utf-8-validate - - vue - dev: true - - /@nuxt-themes/elements@0.9.5(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-uAA5AiIaT1SxCBjNIURJyCDPNR27+8J+t3AWuzWyhbNPr3L1inEcETZ3RVNzFdQE6mx7MGAMwFBqxPkOUhZQuA==} - dependencies: - '@nuxt-themes/tokens': 1.9.1(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23) - '@vueuse/core': 9.13.0(vue@3.4.23) - transitivePeerDependencies: - - '@vue/composition-api' - - postcss - - rollup - - sass - - supports-color - - vue + /@nuxt/devalue@2.0.2: + resolution: {integrity: sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==} dev: true - /@nuxt-themes/tokens@1.9.1(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-5C28kfRvKnTX8Tux+xwyaf+2pxKgQ53dC9l6C33sZwRRyfUJulGDZCFjKbuNq4iqVwdGvkFSQBYBYjFAv6t75g==} + /@nuxt/devtools-kit@1.1.5(nuxt@3.11.2)(rollup@3.29.4)(vite@5.2.9): + resolution: {integrity: sha512-Nb/NKFCRtxyqcPD6snB52rXtbRQMjGtn3ncpa8cLWsnoqnkd9emQ4uwV8IwCNxTnqUBtbGU79/TlJ79SKH9TAw==} + peerDependencies: + nuxt: ^3.9.0 + vite: '*' dependencies: - '@nuxtjs/color-mode': 3.4.0(rollup@3.29.4) - '@vueuse/core': 9.13.0(vue@3.4.23) - pinceau: 0.18.9(postcss@8.4.38) + '@nuxt/kit': 3.11.2(rollup@3.29.4) + '@nuxt/schema': 3.11.2(rollup@3.29.4) + execa: 7.2.0 + nuxt: 3.11.2(@types/node@18.19.31)(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(rollup@3.29.4)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) + vite: 5.2.9(@types/node@18.19.31) transitivePeerDependencies: - - '@vue/composition-api' - - postcss - rollup - - sass - supports-color - - vue dev: true - /@nuxt-themes/typography@0.11.0(postcss@8.4.38)(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-TqyvD7sDWnqGmL00VtuI7JdmNTPL5/g957HCAWNzcNp+S20uJjW/FXSdkM76d4JSVDHvBqw7Wer3RsqVhqvA4w==} + /@nuxt/devtools-wizard@1.1.5: + resolution: {integrity: sha512-bWLgLvYFbYCQYlLPttZaUo58cS1VJo1uEFguHaCwZ7Fzkm4Iv+lFTv5BzD+gOHwohaXLr3YecgZOO4YNJTgXyA==} + hasBin: true dependencies: - '@nuxtjs/color-mode': 3.4.0(rollup@3.29.4) - nuxt-config-schema: 0.4.6(rollup@3.29.4) - nuxt-icon: 0.3.3(rollup@3.29.4)(vue@3.4.23) - pinceau: 0.18.9(postcss@8.4.38) - ufo: 1.5.3 - transitivePeerDependencies: - - postcss - - rollup - - sass - - supports-color - - vue + consola: 3.2.3 + diff: 5.2.0 + execa: 7.2.0 + global-directory: 4.0.1 + magicast: 0.3.4 + pathe: 1.1.2 + pkg-types: 1.0.3 + prompts: 2.4.2 + rc9: 2.1.2 + semver: 7.6.0 dev: true - /@nuxt/content@2.12.1(nuxt@3.11.2)(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-xW4xjyYm6zqglb17Tu0J+rpKUV1PF9zp6SLu1lopylFnerdyImtce84206HT6Zd/DJgivKtoW4dyyJn0ZaSqCQ==} + /@nuxt/devtools@1.1.5(@unocss/reset@0.59.3)(floating-vue@5.2.2)(nuxt@3.11.2)(rollup@3.29.4)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23): + resolution: {integrity: sha512-aDEqz4L1GDj4DDnX7PL9ety3Wx0kLyKTb2JOSoJR8uX09fC3gonCvj/gYHLSSIKqhPasUjoOO5RPCtT+r9dtsA==} + hasBin: true + peerDependencies: + nuxt: ^3.9.0 + vite: '*' dependencies: + '@antfu/utils': 0.7.7 + '@nuxt/devtools-kit': 1.1.5(nuxt@3.11.2)(rollup@3.29.4)(vite@5.2.9) + '@nuxt/devtools-wizard': 1.1.5 '@nuxt/kit': 3.11.2(rollup@3.29.4) - '@nuxtjs/mdc': 0.6.1(rollup@3.29.4) - '@vueuse/core': 10.9.0(vue@3.4.23) - '@vueuse/head': 2.0.0(vue@3.4.23) - '@vueuse/nuxt': 10.9.0(nuxt@3.11.2)(rollup@3.29.4)(vue@3.4.23) + '@vue/devtools-applet': 7.0.27(@unocss/reset@0.59.3)(floating-vue@5.2.2)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23) + '@vue/devtools-core': 7.0.27(vite@5.2.9)(vue@3.4.23) + '@vue/devtools-kit': 7.0.27(vue@3.4.23) + birpc: 0.2.17 consola: 3.2.3 - defu: 6.1.4 - destr: 2.0.3 - json5: 2.2.3 - knitwork: 1.1.0 - listhen: 1.7.2 - mdast-util-to-string: 4.0.0 - mdurl: 2.0.0 - micromark: 4.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-types: 2.0.0 - minisearch: 6.3.0 - ohash: 1.1.3 - pathe: 1.1.2 - scule: 1.3.0 - shiki: 1.3.0 - slugify: 1.6.6 - socket.io-client: 4.7.5 - ufo: 1.5.3 - unist-util-stringify-position: 4.0.0 - unstorage: 1.10.2(ioredis@5.4.1) - ws: 8.16.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/kv' - - '@vue/composition-api' - - bufferutil - - idb-keyval - - ioredis - - nuxt - - rollup - - supports-color - - uWebSockets.js - - utf-8-validate - - vue - dev: true - - /@nuxt/devalue@2.0.2: - resolution: {integrity: sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==} - dev: true - - /@nuxt/devtools-kit@1.1.5(nuxt@3.11.2)(rollup@3.29.4)(vite@5.2.9): - resolution: {integrity: sha512-Nb/NKFCRtxyqcPD6snB52rXtbRQMjGtn3ncpa8cLWsnoqnkd9emQ4uwV8IwCNxTnqUBtbGU79/TlJ79SKH9TAw==} - peerDependencies: - nuxt: ^3.9.0 - vite: '*' - dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - '@nuxt/schema': 3.11.2(rollup@3.29.4) - execa: 7.2.0 - nuxt: 3.11.2(@types/node@18.19.31)(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(rollup@3.29.4)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) - vite: 5.2.9(@types/node@18.19.31) - transitivePeerDependencies: - - rollup - - supports-color - dev: true - - /@nuxt/devtools-kit@1.1.5(nuxt@3.11.2)(vite@5.2.9): - resolution: {integrity: sha512-Nb/NKFCRtxyqcPD6snB52rXtbRQMjGtn3ncpa8cLWsnoqnkd9emQ4uwV8IwCNxTnqUBtbGU79/TlJ79SKH9TAw==} - peerDependencies: - nuxt: ^3.9.0 - vite: '*' - dependencies: - '@nuxt/kit': 3.11.2 - '@nuxt/schema': 3.11.2 - execa: 7.2.0 - nuxt: 3.11.2(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) - vite: 5.2.9 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - - /@nuxt/devtools-wizard@1.1.5: - resolution: {integrity: sha512-bWLgLvYFbYCQYlLPttZaUo58cS1VJo1uEFguHaCwZ7Fzkm4Iv+lFTv5BzD+gOHwohaXLr3YecgZOO4YNJTgXyA==} - hasBin: true - dependencies: - consola: 3.2.3 - diff: 5.2.0 - execa: 7.2.0 - global-directory: 4.0.1 - magicast: 0.3.4 - pathe: 1.1.2 - pkg-types: 1.0.3 - prompts: 2.4.2 - rc9: 2.1.2 - semver: 7.6.0 - dev: true - - /@nuxt/devtools@1.1.5(@unocss/reset@0.59.3)(floating-vue@5.2.2)(nuxt@3.11.2)(rollup@3.29.4)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23): - resolution: {integrity: sha512-aDEqz4L1GDj4DDnX7PL9ety3Wx0kLyKTb2JOSoJR8uX09fC3gonCvj/gYHLSSIKqhPasUjoOO5RPCtT+r9dtsA==} - hasBin: true - peerDependencies: - nuxt: ^3.9.0 - vite: '*' - dependencies: - '@antfu/utils': 0.7.7 - '@nuxt/devtools-kit': 1.1.5(nuxt@3.11.2)(rollup@3.29.4)(vite@5.2.9) - '@nuxt/devtools-wizard': 1.1.5 - '@nuxt/kit': 3.11.2(rollup@3.29.4) - '@vue/devtools-applet': 7.0.27(@unocss/reset@0.59.3)(floating-vue@5.2.2)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23) - '@vue/devtools-core': 7.0.27(vite@5.2.9)(vue@3.4.23) - '@vue/devtools-kit': 7.0.27(vue@3.4.23) - birpc: 0.2.17 - consola: 3.2.3 - cronstrue: 2.49.0 + cronstrue: 2.49.0 destr: 2.0.3 error-stack-parser-es: 0.1.1 execa: 7.2.0 @@ -1848,104 +1861,6 @@ packages: - vue dev: true - /@nuxt/devtools@1.1.5(@unocss/reset@0.59.3)(floating-vue@5.2.2)(nuxt@3.11.2)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23): - resolution: {integrity: sha512-aDEqz4L1GDj4DDnX7PL9ety3Wx0kLyKTb2JOSoJR8uX09fC3gonCvj/gYHLSSIKqhPasUjoOO5RPCtT+r9dtsA==} - hasBin: true - peerDependencies: - nuxt: ^3.9.0 - vite: '*' - dependencies: - '@antfu/utils': 0.7.7 - '@nuxt/devtools-kit': 1.1.5(nuxt@3.11.2)(vite@5.2.9) - '@nuxt/devtools-wizard': 1.1.5 - '@nuxt/kit': 3.11.2 - '@vue/devtools-applet': 7.0.27(@unocss/reset@0.59.3)(floating-vue@5.2.2)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23) - '@vue/devtools-core': 7.0.27(vite@5.2.9)(vue@3.4.23) - '@vue/devtools-kit': 7.0.27(vue@3.4.23) - birpc: 0.2.17 - consola: 3.2.3 - cronstrue: 2.49.0 - destr: 2.0.3 - error-stack-parser-es: 0.1.1 - execa: 7.2.0 - fast-glob: 3.3.2 - flatted: 3.3.1 - get-port-please: 3.1.2 - hookable: 5.5.3 - image-meta: 0.2.0 - is-installed-globally: 1.0.0 - launch-editor: 2.6.1 - local-pkg: 0.5.0 - magicast: 0.3.4 - nuxt: 3.11.2(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) - nypm: 0.3.8 - ohash: 1.1.3 - pacote: 17.0.7 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - pkg-types: 1.0.3 - rc9: 2.1.2 - scule: 1.3.0 - semver: 7.6.0 - simple-git: 3.24.0 - sirv: 2.0.4 - unimport: 3.7.1(rollup@4.14.3) - vite: 5.2.9 - vite-plugin-inspect: 0.8.3(@nuxt/kit@3.11.2)(vite@5.2.9) - vite-plugin-vue-inspector: 4.0.2(vite@5.2.9) - which: 3.0.1 - ws: 8.16.0 - transitivePeerDependencies: - - '@unocss/reset' - - '@vue/composition-api' - - async-validator - - axios - - bluebird - - bufferutil - - change-case - - drauu - - floating-vue - - fuse.js - - idb-keyval - - jwt-decode - - nprogress - - qrcode - - rollup - - sortablejs - - supports-color - - universal-cookie - - unocss - - utf-8-validate - - vue - dev: true - - /@nuxt/kit@3.11.2: - resolution: {integrity: sha512-yiYKP0ZWMW7T3TCmsv4H8+jEsB/nFriRAR8bKoSqSV9bkVYWPE36sf7JDux30dQ91jSlQG6LQkB3vCHYTS2cIg==} - engines: {node: ^14.18.0 || >=16.10.0} - dependencies: - '@nuxt/schema': 3.11.2 - c12: 1.10.0 - consola: 3.2.3 - defu: 6.1.4 - globby: 14.0.1 - hash-sum: 2.0.0 - ignore: 5.3.1 - jiti: 1.21.0 - knitwork: 1.1.0 - mlly: 1.6.1 - pathe: 1.1.2 - pkg-types: 1.0.3 - scule: 1.3.0 - semver: 7.6.0 - ufo: 1.5.3 - unctx: 2.3.1 - unimport: 3.7.1(rollup@4.14.3) - untyped: 1.4.2 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /@nuxt/kit@3.11.2(rollup@3.29.4): resolution: {integrity: sha512-yiYKP0ZWMW7T3TCmsv4H8+jEsB/nFriRAR8bKoSqSV9bkVYWPE36sf7JDux30dQ91jSlQG6LQkB3vCHYTS2cIg==} engines: {node: ^14.18.0 || >=16.10.0} @@ -1992,26 +1907,6 @@ packages: - typescript dev: true - /@nuxt/schema@3.11.2: - resolution: {integrity: sha512-Z0bx7N08itD5edtpkstImLctWMNvxTArsKXzS35ZuqyAyKBPcRjO1CU01slH0ahO30Gg9kbck3/RKNZPwfOjJg==} - engines: {node: ^14.18.0 || >=16.10.0} - dependencies: - '@nuxt/ui-templates': 1.3.3 - consola: 3.2.3 - defu: 6.1.4 - hookable: 5.5.3 - pathe: 1.1.2 - pkg-types: 1.0.3 - scule: 1.3.0 - std-env: 3.7.0 - ufo: 1.5.3 - unimport: 3.7.1(rollup@4.14.3) - untyped: 1.4.2 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /@nuxt/schema@3.11.2(rollup@3.29.4): resolution: {integrity: sha512-Z0bx7N08itD5edtpkstImLctWMNvxTArsKXzS35ZuqyAyKBPcRjO1CU01slH0ahO30Gg9kbck3/RKNZPwfOjJg==} engines: {node: ^14.18.0 || >=16.10.0} @@ -2031,32 +1926,6 @@ packages: - rollup - supports-color - /@nuxt/telemetry@2.5.3: - resolution: {integrity: sha512-Ghv2MgWbJcUM9G5Dy3oQP0cJkUwEgaiuQxEF61FXJdn0a69Q4StZEP/hLF0MWPM9m6EvAwI7orxkJHM7MrmtVg==} - hasBin: true - dependencies: - '@nuxt/kit': 3.11.2 - ci-info: 4.0.0 - consola: 3.2.3 - create-require: 1.1.1 - defu: 6.1.4 - destr: 2.0.3 - dotenv: 16.4.5 - git-url-parse: 13.1.1 - is-docker: 3.0.0 - jiti: 1.21.0 - mri: 1.2.0 - nanoid: 4.0.2 - ofetch: 1.3.4 - parse-git-config: 3.0.0 - pathe: 1.1.2 - rc9: 2.1.2 - std-env: 3.7.0 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /@nuxt/telemetry@2.5.3(rollup@3.29.4): resolution: {integrity: sha512-Ghv2MgWbJcUM9G5Dy3oQP0cJkUwEgaiuQxEF61FXJdn0a69Q4StZEP/hLF0MWPM9m6EvAwI7orxkJHM7MrmtVg==} hasBin: true @@ -2158,81 +2027,6 @@ packages: - supports-color dev: true - /@nuxt/test-utils@3.12.1(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23): - resolution: {integrity: sha512-VRLNcDz9Ad/4pjHdNRVLPs5DVIO5IJ0ij81PLmsE/lt+5oeeIQld+AgHgcqM4BM1YKsXTBuavbk1mEBqj7h/+A==} - engines: {node: ^14.18.0 || >=16.10.0} - peerDependencies: - '@cucumber/cucumber': ^10.3.1 - '@jest/globals': ^29.5.0 - '@playwright/test': ^1.42.1 - '@testing-library/vue': ^7.0.0 || ^8.0.1 - '@vitest/ui': ^0.34.6 || ^1.0.0 - '@vue/test-utils': ^2.4.2 - h3: '*' - happy-dom: ^9.10.9 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 - jsdom: ^22.0.0 || ^23.0.0 || ^24.0.0 - playwright-core: ^1.34.3 - vite: '*' - vitest: ^0.34.6 || ^1.0.0 - vue: ^3.3.4 - vue-router: ^4.0.0 - peerDependenciesMeta: - '@cucumber/cucumber': - optional: true - '@jest/globals': - optional: true - '@playwright/test': - optional: true - '@testing-library/vue': - optional: true - '@vitest/ui': - optional: true - '@vue/test-utils': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright-core: - optional: true - vitest: - optional: true - dependencies: - '@nuxt/kit': 3.11.2 - '@nuxt/schema': 3.11.2 - '@playwright/test': 1.43.1 - '@vue/test-utils': 2.4.5 - c12: 1.10.0 - consola: 3.2.3 - defu: 6.1.4 - destr: 2.0.3 - estree-walker: 3.0.3 - execa: 8.0.1 - fake-indexeddb: 5.0.2 - get-port-please: 3.1.2 - h3: 1.11.1 - local-pkg: 0.5.0 - magic-string: 0.30.10 - node-fetch-native: 1.6.2 - ofetch: 1.3.4 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - radix3: 1.1.2 - scule: 1.3.0 - std-env: 3.7.0 - ufo: 1.5.3 - unenv: 1.9.0 - unplugin: 1.10.1 - vite: 5.2.9 - vitest: 1.5.0 - vitest-environment-nuxt: 1.0.0(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23) - vue: 3.4.23(typescript@5.3.3) - vue-router: 4.3.1(vue@3.4.23) - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /@nuxt/ui-templates@1.3.3: resolution: {integrity: sha512-3BG5doAREcD50dbKyXgmjD4b1GzY8CUy3T41jMhHZXNDdaNwOd31IBq+D6dV00OSrDVhzrTVj0IxsUsnMyHvIQ==} @@ -2298,99 +2092,6 @@ packages: - vue-tsc dev: true - /@nuxt/vite-builder@3.11.2(eslint@8.57.0)(typescript@5.3.3)(vue-tsc@1.8.27)(vue@3.4.23): - resolution: {integrity: sha512-eXTZsAAN4dPz4eA2UD5YU2kD/DqgfyQp1UYsIdCe6+PAVe1ifkUboBjbc0piR5+3qI/S/eqk3nzxRGbiYF7Ccg==} - engines: {node: ^14.18.0 || >=16.10.0} - peerDependencies: - vue: ^3.3.4 - dependencies: - '@nuxt/kit': 3.11.2 - '@rollup/plugin-replace': 5.0.5(rollup@4.14.3) - '@vitejs/plugin-vue': 5.0.4(vite@5.2.9)(vue@3.4.23) - '@vitejs/plugin-vue-jsx': 3.1.0(vite@5.2.9)(vue@3.4.23) - autoprefixer: 10.4.19(postcss@8.4.38) - clear: 0.1.0 - consola: 3.2.3 - cssnano: 6.1.2(postcss@8.4.38) - defu: 6.1.4 - esbuild: 0.20.2 - escape-string-regexp: 5.0.0 - estree-walker: 3.0.3 - externality: 1.0.2 - fs-extra: 11.2.0 - get-port-please: 3.1.2 - h3: 1.11.1 - knitwork: 1.1.0 - magic-string: 0.30.10 - mlly: 1.6.1 - ohash: 1.1.3 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - pkg-types: 1.0.3 - postcss: 8.4.38 - rollup-plugin-visualizer: 5.12.0(rollup@4.14.3) - std-env: 3.7.0 - strip-literal: 2.1.0 - ufo: 1.5.3 - unenv: 1.9.0 - unplugin: 1.10.1 - vite: 5.2.9 - vite-node: 1.5.0 - vite-plugin-checker: 0.6.4(eslint@8.57.0)(typescript@5.3.3)(vite@5.2.9)(vue-tsc@1.8.27) - vue: 3.4.23(typescript@5.3.3) - vue-bundle-renderer: 2.0.0 - transitivePeerDependencies: - - '@types/node' - - eslint - - less - - lightningcss - - meow - - optionator - - rollup - - sass - - stylelint - - stylus - - sugarss - - supports-color - - terser - - typescript - - uWebSockets.js - - vls - - vti - - vue-tsc - dev: true - - /@nuxthq/studio@1.0.13(rollup@3.29.4): - resolution: {integrity: sha512-C4j+K53om5Kj5xLrehWEbnDJlid5tDsHP7QdOIsK550dHOxPmvaXMH4YxsSNU8cDppMheMbN59zdYhQaQfko6g==} - dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - defu: 6.1.4 - git-url-parse: 14.0.0 - nuxt-component-meta: 0.6.3(rollup@3.29.4) - parse-git-config: 3.0.0 - pkg-types: 1.0.3 - socket.io-client: 4.7.5 - ufo: 1.5.3 - untyped: 1.4.2 - transitivePeerDependencies: - - bufferutil - - rollup - - supports-color - - utf-8-validate - dev: true - - /@nuxtjs/color-mode@3.4.0(rollup@3.29.4): - resolution: {integrity: sha512-rS51nG3zW8ksOwNubIP3BPQ+vBL0r2M6td1kkB/VaR1e1uVtNfWlBPfZjb604bgNCE2AeRNqaI0CrxkfnQ6h6Q==} - dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - pathe: 1.1.2 - pkg-types: 1.0.3 - semver: 7.6.0 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /@nuxtjs/eslint-config-typescript@12.1.0(eslint@8.57.0)(typescript@5.3.3): resolution: {integrity: sha512-l2fLouDYwdAvCZEEw7wGxOBj+i8TQcHFu3zMPTLqKuv1qu6WcZIr0uztkbaa8ND1uKZ9YPqKx6UlSOjM4Le69Q==} peerDependencies: @@ -2431,49 +2132,6 @@ packages: - supports-color dev: true - /@nuxtjs/mdc@0.6.1(rollup@3.29.4): - resolution: {integrity: sha512-zS5QK7DZ/SBrjqQX1DOy7GnxKy+wbj2+LvooefOWmQqHfLTAqJLVIjuv/BmKnQWiRCq19+uysys3iY42EoY5/A==} - dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - '@shikijs/transformers': 1.3.0 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 - '@vue/compiler-core': 3.4.23 - consola: 3.2.3 - debug: 4.3.4 - defu: 6.1.4 - destr: 2.0.3 - detab: 3.0.2 - github-slugger: 2.0.0 - hast-util-to-string: 3.0.0 - mdast-util-to-hast: 13.1.0 - micromark-util-sanitize-uri: 2.0.0 - ohash: 1.1.3 - parse5: 7.1.2 - pathe: 1.1.2 - property-information: 6.5.0 - rehype-external-links: 3.0.0 - rehype-raw: 7.0.0 - rehype-slug: 6.0.0 - rehype-sort-attribute-values: 5.0.0 - rehype-sort-attributes: 5.0.0 - remark-emoji: 4.0.1 - remark-gfm: 4.0.0 - remark-mdc: 3.2.0 - remark-parse: 11.0.0 - remark-rehype: 11.1.0 - scule: 1.3.0 - shiki: 1.3.0 - ufo: 1.5.3 - unified: 11.0.4 - unist-builder: 4.0.0 - unist-util-visit: 5.0.0 - unwasm: 0.3.9 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /@one-ini/wasm@0.1.1: resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} dev: true @@ -2653,24 +2311,6 @@ packages: rollup: 4.14.3 slash: 4.0.0 - /@rollup/plugin-commonjs@24.1.0(rollup@3.29.4): - resolution: {integrity: sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.68.0||^3.0.0 - peerDependenciesMeta: - rollup: - optional: true - dependencies: - '@rollup/pluginutils': 5.1.0(rollup@3.29.4) - commondir: 1.0.1 - estree-walker: 2.0.2 - glob: 8.1.0 - is-reference: 1.2.1 - magic-string: 0.27.0 - rollup: 3.29.4 - dev: true - /@rollup/plugin-commonjs@25.0.7(rollup@3.29.4): resolution: {integrity: sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==} engines: {node: '>=14.0.0'} @@ -2968,14 +2608,14 @@ packages: requiresBuild: true optional: true - /@shikijs/core@1.3.0: - resolution: {integrity: sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA==} + /@shikijs/core@1.10.1: + resolution: {integrity: sha512-qdiJS5a/QGCff7VUFIqd0hDdWly9rDp8lhVmXVrS11aazX8LOTRLHAXkkEeONNsS43EcCd7gax9LLoOz4vlFQA==} dev: true - /@shikijs/transformers@1.3.0: - resolution: {integrity: sha512-3mlpg2I9CjhjE96dEWQOGeCWoPcyTov3s4aAsHmgvnTHa8MBknEnCQy8/xivJPSpD+olqOqIEoHnLfbNJK29AA==} + /@shikijs/transformers@1.10.1: + resolution: {integrity: sha512-0gLtcFyi6R6zcUkFajUEp1Qiv7lHBSFgOz4tQvS8nFsYCQSLI1/9pM+Me8jEIPXv7XLKAoUjw6InL+Sv+BHw/A==} dependencies: - shiki: 1.3.0 + shiki: 1.10.1 dev: true /@sigstore/bundle@2.3.1: @@ -3030,19 +2670,10 @@ packages: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true - /@sindresorhus/is@4.6.0: - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - dev: true - /@sindresorhus/merge-streams@2.3.0: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} - /@socket.io/component-emitter@3.1.1: - resolution: {integrity: sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==} - dev: true - /@swc/helpers@0.5.2: resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} dependencies: @@ -3067,21 +2698,9 @@ packages: minimatch: 9.0.4 dev: true - /@types/debug@4.1.12: - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - dependencies: - '@types/ms': 0.7.34 - dev: true - /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - /@types/hast@3.0.4: - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - dependencies: - '@types/unist': 3.0.2 - dev: true - /@types/http-proxy@1.17.14: resolution: {integrity: sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==} dependencies: @@ -3101,14 +2720,19 @@ packages: '@types/node': 20.12.7 dev: true - /@types/mdast@4.0.3: - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + /@types/linkify-it@5.0.0: + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + dev: true + + /@types/markdown-it@14.1.1: + resolution: {integrity: sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==} dependencies: - '@types/unist': 3.0.2 + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 dev: true - /@types/ms@0.7.34: - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + /@types/mdurl@2.0.0: + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} dev: true /@types/node@18.19.31: @@ -3133,18 +2757,6 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true - /@types/unist@2.0.10: - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} - dev: true - - /@types/unist@3.0.2: - resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} - dev: true - - /@types/web-bluetooth@0.0.16: - resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} - dev: true - /@types/web-bluetooth@0.0.20: resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} dev: true @@ -3324,7 +2936,7 @@ packages: vue: 3.4.23(typescript@5.3.3) dev: true - /@unocss/astro@0.59.3(vite@5.2.9): + /@unocss/astro@0.59.3(rollup@3.29.4)(vite@5.2.9): resolution: {integrity: sha512-Q0eL9LLWTORWQYZYz4aoiT0SQhXqrCYJKK6+Z++d5ugsnVsRP2O/ovxf+0CueMHe6volW0O2EhgUt0yT20FdAA==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 @@ -3334,19 +2946,19 @@ packages: dependencies: '@unocss/core': 0.59.3 '@unocss/reset': 0.59.3 - '@unocss/vite': 0.59.3(vite@5.2.9) - vite: 5.2.9 + '@unocss/vite': 0.59.3(rollup@3.29.4)(vite@5.2.9) + vite: 5.2.9(@types/node@18.19.31) transitivePeerDependencies: - rollup dev: true - /@unocss/cli@0.59.3: + /@unocss/cli@0.59.3(rollup@3.29.4): resolution: {integrity: sha512-BkDkNZYVJrTRWxtTUPxq3TvbaBJ5r5zy82csCv+RJbNmQLJaqBy7gt0qkLJ9H4C83HNgqOuYEupVM65Ts3g0MA==} engines: {node: '>=14'} hasBin: true dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + '@rollup/pluginutils': 5.1.0(rollup@3.29.4) '@unocss/config': 0.59.3 '@unocss/core': 0.59.3 '@unocss/preset-uno': 0.59.3 @@ -3465,10 +3077,6 @@ packages: '@unocss/rule-utils': 0.59.3 dev: true - /@unocss/reset@0.50.8: - resolution: {integrity: sha512-2WoM6O9VyuHDPAnvCXr7LBJQ8ZRHDnuQAFsL1dWXp561Iq2l9whdNtPuMcozLGJGUUrFfVBXIrHY4sfxxScgWg==} - dev: true - /@unocss/reset@0.59.3: resolution: {integrity: sha512-4m2p/TcOamf17w4b8w6YIx9p1VP3BPiMQ4WUx2FvsgQz7G3/w+FJEEQ0xoc2FIJ0UBggr9UJmrs2Y7SQ9Gmukg==} dev: true @@ -3522,13 +3130,13 @@ packages: '@unocss/core': 0.59.3 dev: true - /@unocss/vite@0.59.3(vite@5.2.9): + /@unocss/vite@0.59.3(rollup@3.29.4)(vite@5.2.9): resolution: {integrity: sha512-+K4kSEt3BvJfYlc8Tg3nmF53i14+OUTIasnzwUQF4JCF+B47jd47IVbNBm2izhTA5OrmZ+1xXBjHR7cXgDjDhg==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) + '@rollup/pluginutils': 5.1.0(rollup@3.29.4) '@unocss/config': 0.59.3 '@unocss/core': 0.59.3 '@unocss/inspector': 0.59.3 @@ -3537,7 +3145,7 @@ packages: chokidar: 3.6.0 fast-glob: 3.3.2 magic-string: 0.30.10 - vite: 5.2.9 + vite: 5.2.9(@types/node@18.19.31) transitivePeerDependencies: - rollup dev: true @@ -3573,7 +3181,7 @@ packages: '@babel/core': 7.24.4 '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.4) '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.24.4) - vite: 5.2.9 + vite: 5.2.9(@types/node@18.19.31) vue: 3.4.23(typescript@5.3.3) transitivePeerDependencies: - supports-color @@ -3586,10 +3194,21 @@ packages: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.2.9 + vite: 5.2.9(@types/node@18.19.31) vue: 3.4.23(typescript@5.3.3) dev: true + /@vitejs/plugin-vue@5.0.5(vite@5.3.3)(vue@3.4.31): + resolution: {integrity: sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + dependencies: + vite: 5.3.3(@types/node@18.19.31) + vue: 3.4.31(typescript@5.3.3) + dev: true + /@vitest/expect@1.5.0: resolution: {integrity: sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==} dependencies: @@ -3635,24 +3254,12 @@ packages: '@volar/source-map': 1.11.1 dev: true - /@volar/language-core@1.4.1: - resolution: {integrity: sha512-EIY+Swv+TjsWpxOxujjMf1ZXqOjg9MT2VMXZ+1dKva0wD8W0L6EtptFFcCJdBbcKmGMFkr57Qzz9VNMWhs3jXQ==} - dependencies: - '@volar/source-map': 1.4.1 - dev: true - /@volar/source-map@1.11.1: resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} dependencies: muggle-string: 0.3.1 dev: true - /@volar/source-map@1.4.1: - resolution: {integrity: sha512-bZ46ad72dsbzuOWPUtJjBXkzSQzzSejuR3CT81+GvTEI2E994D8JPXzM3tl98zyCNnjgs4OkRyliImL1dvJ5BA==} - dependencies: - muggle-string: 0.2.2 - dev: true - /@volar/typescript@1.11.1: resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} dependencies: @@ -3660,20 +3267,6 @@ packages: path-browserify: 1.0.1 dev: true - /@volar/vue-language-core@1.6.5: - resolution: {integrity: sha512-IF2b6hW4QAxfsLd5mePmLgtkXzNi+YnH6ltCd80gb7+cbdpFMjM1I+w+nSg2kfBTyfu+W8useCZvW89kPTBpzg==} - dependencies: - '@volar/language-core': 1.4.1 - '@volar/source-map': 1.4.1 - '@vue/compiler-dom': 3.4.23 - '@vue/compiler-sfc': 3.4.23 - '@vue/reactivity': 3.4.23 - '@vue/shared': 3.4.23 - minimatch: 9.0.4 - muggle-string: 0.2.2 - vue-template-compiler: 2.7.16 - dev: true - /@vue-macros/common@1.10.1(rollup@3.29.4)(vue@3.4.23): resolution: {integrity: sha512-uftSpfwdwitcQT2lM8aVxcfe5rKQBzC9jMrtJM5sG4hEuFyfIvnJihpPpnaWxY+X4p64k+YYXtBFv+1O5Bq3dg==} engines: {node: '>=16.14.0'} @@ -3694,26 +3287,6 @@ packages: - rollup dev: true - /@vue-macros/common@1.10.1(vue@3.4.23): - resolution: {integrity: sha512-uftSpfwdwitcQT2lM8aVxcfe5rKQBzC9jMrtJM5sG4hEuFyfIvnJihpPpnaWxY+X4p64k+YYXtBFv+1O5Bq3dg==} - engines: {node: '>=16.14.0'} - peerDependencies: - vue: ^2.7.0 || ^3.2.25 - peerDependenciesMeta: - vue: - optional: true - dependencies: - '@babel/types': 7.24.0 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) - '@vue/compiler-sfc': 3.4.23 - ast-kit: 0.11.3 - local-pkg: 0.5.0 - magic-string-ast: 0.3.0 - vue: 3.4.23(typescript@5.3.3) - transitivePeerDependencies: - - rollup - dev: true - /@vue/babel-helper-vue-transform-on@1.2.2: resolution: {integrity: sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw==} dev: true @@ -3765,6 +3338,16 @@ packages: source-map-js: 1.2.0 dev: true + /@vue/compiler-core@3.4.31: + resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==} + dependencies: + '@babel/parser': 7.24.7 + '@vue/shared': 3.4.31 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + dev: true + /@vue/compiler-dom@3.4.23: resolution: {integrity: sha512-t0b9WSTnCRrzsBGrDd1LNR5HGzYTr7LX3z6nNBG+KGvZLqrT0mY6NsMzOqlVMBKKXKVuusbbB5aOOFgTY+senw==} dependencies: @@ -3772,6 +3355,13 @@ packages: '@vue/shared': 3.4.23 dev: true + /@vue/compiler-dom@3.4.31: + resolution: {integrity: sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==} + dependencies: + '@vue/compiler-core': 3.4.31 + '@vue/shared': 3.4.31 + dev: true + /@vue/compiler-sfc@3.4.23: resolution: {integrity: sha512-fSDTKTfzaRX1kNAUiaj8JB4AokikzStWgHooMhaxyjZerw624L+IAP/fvI4ZwMpwIh8f08PVzEnu4rg8/Npssw==} dependencies: @@ -3786,6 +3376,20 @@ packages: source-map-js: 1.2.0 dev: true + /@vue/compiler-sfc@3.4.31: + resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==} + dependencies: + '@babel/parser': 7.24.7 + '@vue/compiler-core': 3.4.31 + '@vue/compiler-dom': 3.4.31 + '@vue/compiler-ssr': 3.4.31 + '@vue/shared': 3.4.31 + estree-walker: 2.0.2 + magic-string: 0.30.10 + postcss: 8.4.38 + source-map-js: 1.2.0 + dev: true + /@vue/compiler-ssr@3.4.23: resolution: {integrity: sha512-hb6Uj2cYs+tfqz71Wj6h3E5t6OKvb4MVcM2Nl5i/z1nv1gjEhw+zYaNOV+Xwn+SSN/VZM0DgANw5TuJfxfezPg==} dependencies: @@ -3793,10 +3397,23 @@ packages: '@vue/shared': 3.4.23 dev: true + /@vue/compiler-ssr@3.4.31: + resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==} + dependencies: + '@vue/compiler-dom': 3.4.31 + '@vue/shared': 3.4.31 + dev: true + /@vue/devtools-api@6.6.1: resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==} dev: true + /@vue/devtools-api@7.3.5: + resolution: {integrity: sha512-BSdBBu5hOIv+gBJC9jzYMh5bC27FQwjWLSb8fVAniqlL9gvsqvK27xTgczMf+hgctlszMYQnRm3bpY/j8vhPqw==} + dependencies: + '@vue/devtools-kit': 7.3.5 + dev: true + /@vue/devtools-applet@7.0.27(@unocss/reset@0.59.3)(floating-vue@5.2.2)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23): resolution: {integrity: sha512-ubNn/qIn5n3x7YCVSabfQfKL49GoJPJdYu4LfdNz/gZkgb1+djdATpKl/+xzQoOqtGzqnR9nMoCHApAJAgeMyg==} peerDependencies: @@ -3856,12 +3473,30 @@ packages: vue: 3.4.23(typescript@5.3.3) dev: true + /@vue/devtools-kit@7.3.5: + resolution: {integrity: sha512-wwfi10gJ1HMtjzcd8aIOnzBHlIRqsYDgcDyrKvkeyc0Gbcoe7UrkXRVHZUOtcxxoplHA0PwpT6wFg0uUCmi8Ww==} + dependencies: + '@vue/devtools-shared': 7.3.5 + birpc: 0.2.17 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.1 + dev: true + /@vue/devtools-shared@7.0.27: resolution: {integrity: sha512-4VxtmZ6yjhiSloqZZq2UYU0TBGxOJ8GxWvp5OlAH70zYqi0FIAyWGPkOhvfoZ7DKQyv2UU0mmKzFHjsEkelGyQ==} dependencies: rfdc: 1.3.1 dev: true + /@vue/devtools-shared@7.3.5: + resolution: {integrity: sha512-Rqii3VazmWTi67a86rYopi61n5Ved05EybJCwyrfoO9Ok3MaS/4yRFl706ouoISMlyrASJFEzM0/AiDA6w4f9A==} + dependencies: + rfdc: 1.4.1 + dev: true + /@vue/devtools-ui@7.0.27(@unocss/reset@0.59.3)(floating-vue@5.2.2)(unocss@0.59.3)(vue@3.4.23): resolution: {integrity: sha512-MVcQwqqGNW2poW29OkzOcpNLHb0R/VQECWYiDYvKqjWp3G8M/FS2E5mUnjXxZGpfqHjSEmJs+fFGY8exnYpNng==} peerDependencies: @@ -3875,9 +3510,9 @@ packages: '@vueuse/core': 10.9.0(vue@3.4.23) '@vueuse/integrations': 10.9.0(focus-trap@7.5.4)(vue@3.4.23) colord: 2.9.3 - floating-vue: 5.2.2(vue@3.4.23) + floating-vue: 5.2.2(@nuxt/kit@3.11.2)(vue@3.4.23) focus-trap: 7.5.4 - unocss: 0.59.3(postcss@8.4.38)(vite@5.2.9) + unocss: 0.59.3(postcss@8.4.38)(rollup@3.29.4)(vite@5.2.9) vue: 3.4.23(typescript@5.3.3) transitivePeerDependencies: - '@vue/composition-api' @@ -3920,6 +3555,12 @@ packages: '@vue/shared': 3.4.23 dev: true + /@vue/reactivity@3.4.31: + resolution: {integrity: sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==} + dependencies: + '@vue/shared': 3.4.31 + dev: true + /@vue/runtime-core@3.4.23: resolution: {integrity: sha512-FeQ9MZEXoFzFkFiw9MQQ/FWs3srvrP+SjDKSeRIiQHIhtkzoj0X4rWQlRNHbGuSwLra6pMyjAttwixNMjc/xLw==} dependencies: @@ -3927,6 +3568,13 @@ packages: '@vue/shared': 3.4.23 dev: true + /@vue/runtime-core@3.4.31: + resolution: {integrity: sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==} + dependencies: + '@vue/reactivity': 3.4.31 + '@vue/shared': 3.4.31 + dev: true + /@vue/runtime-dom@3.4.23: resolution: {integrity: sha512-RXJFwwykZWBkMiTPSLEWU3kgVLNAfActBfWFlZd0y79FTUxexogd0PLG4HH2LfOktjRxV47Nulygh0JFXe5f9A==} dependencies: @@ -3935,6 +3583,15 @@ packages: csstype: 3.1.3 dev: true + /@vue/runtime-dom@3.4.31: + resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==} + dependencies: + '@vue/reactivity': 3.4.31 + '@vue/runtime-core': 3.4.31 + '@vue/shared': 3.4.31 + csstype: 3.1.3 + dev: true + /@vue/server-renderer@3.4.23(vue@3.4.23): resolution: {integrity: sha512-LDwGHtnIzvKFNS8dPJ1SSU5Gvm36p2ck8wCZc52fc3k/IfjKcwCyrWEf0Yag/2wTFUBXrqizfhK9c/mC367dXQ==} peerDependencies: @@ -3945,10 +3602,24 @@ packages: vue: 3.4.23(typescript@5.3.3) dev: true + /@vue/server-renderer@3.4.31(vue@3.4.31): + resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==} + peerDependencies: + vue: 3.4.31 + dependencies: + '@vue/compiler-ssr': 3.4.31 + '@vue/shared': 3.4.31 + vue: 3.4.31(typescript@5.3.3) + dev: true + /@vue/shared@3.4.23: resolution: {integrity: sha512-wBQ0gvf+SMwsCQOyusNw/GoXPV47WGd1xB5A1Pgzy0sQ3Bi5r5xm3n+92y3gCnB3MWqnRDdvfkRGxhKtbBRNgg==} dev: true + /@vue/shared@3.4.31: + resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==} + dev: true + /@vue/test-utils@2.4.5: resolution: {integrity: sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==} dependencies: @@ -3967,57 +3638,45 @@ packages: - vue dev: true - /@vueuse/core@10.9.0(vue@3.4.23): - resolution: {integrity: sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==} + /@vueuse/core@10.11.0(vue@3.4.31): + resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==} dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.9.0 - '@vueuse/shared': 10.9.0(vue@3.4.23) - vue-demi: 0.14.7(vue@3.4.23) + '@vueuse/metadata': 10.11.0 + '@vueuse/shared': 10.11.0(vue@3.4.31) + vue-demi: 0.14.8(vue@3.4.31) transitivePeerDependencies: - '@vue/composition-api' - vue dev: true - /@vueuse/core@9.13.0(vue@3.4.23): - resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + /@vueuse/core@10.9.0(vue@3.4.23): + resolution: {integrity: sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==} dependencies: - '@types/web-bluetooth': 0.0.16 - '@vueuse/metadata': 9.13.0 - '@vueuse/shared': 9.13.0(vue@3.4.23) + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 10.9.0 + '@vueuse/shared': 10.9.0(vue@3.4.23) vue-demi: 0.14.7(vue@3.4.23) transitivePeerDependencies: - '@vue/composition-api' - vue dev: true - /@vueuse/head@2.0.0(vue@3.4.23): - resolution: {integrity: sha512-ykdOxTGs95xjD4WXE4na/umxZea2Itl0GWBILas+O4oqS7eXIods38INvk3XkJKjqMdWPcpCyLX/DioLQxU1KA==} - peerDependencies: - vue: '>=2.7 || >=3' - dependencies: - '@unhead/dom': 1.9.5 - '@unhead/schema': 1.9.5 - '@unhead/ssr': 1.9.5 - '@unhead/vue': 1.9.5(vue@3.4.23) - vue: 3.4.23(typescript@5.3.3) - dev: true - - /@vueuse/integrations@10.9.0(focus-trap@7.5.4)(fuse.js@6.6.2)(vue@3.4.23): - resolution: {integrity: sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q==} - peerDependencies: - async-validator: '*' - axios: '*' - change-case: '*' - drauu: '*' - focus-trap: '*' - fuse.js: '*' - idb-keyval: '*' - jwt-decode: '*' - nprogress: '*' - qrcode: '*' - sortablejs: '*' - universal-cookie: '*' + /@vueuse/integrations@10.11.0(focus-trap@7.5.4)(vue@3.4.31): + resolution: {integrity: sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^4 + drauu: ^0.3 + focus-trap: ^7 + fuse.js: ^6 + idb-keyval: ^6 + jwt-decode: ^3 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^6 peerDependenciesMeta: async-validator: optional: true @@ -4044,11 +3703,10 @@ packages: universal-cookie: optional: true dependencies: - '@vueuse/core': 10.9.0(vue@3.4.23) - '@vueuse/shared': 10.9.0(vue@3.4.23) + '@vueuse/core': 10.11.0(vue@3.4.31) + '@vueuse/shared': 10.11.0(vue@3.4.31) focus-trap: 7.5.4 - fuse.js: 6.6.2 - vue-demi: 0.14.7(vue@3.4.23) + vue-demi: 0.14.8(vue@3.4.31) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -4104,29 +3762,20 @@ packages: - vue dev: true - /@vueuse/metadata@10.9.0: - resolution: {integrity: sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==} + /@vueuse/metadata@10.11.0: + resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==} dev: true - /@vueuse/metadata@9.13.0: - resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + /@vueuse/metadata@10.9.0: + resolution: {integrity: sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==} dev: true - /@vueuse/nuxt@10.9.0(nuxt@3.11.2)(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-nC4Efg28Q6E41fUD5R+zM9uT5c+NfaDzaJCpqaEV/qHj+/BNJmkDBK8POLIUsiVOY35d0oD/YxZ+eVizqWBZow==} - peerDependencies: - nuxt: ^3.0.0 + /@vueuse/shared@10.11.0(vue@3.4.31): + resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==} dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - '@vueuse/core': 10.9.0(vue@3.4.23) - '@vueuse/metadata': 10.9.0 - local-pkg: 0.5.0 - nuxt: 3.11.2(@types/node@18.19.31)(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(rollup@3.29.4)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27) - vue-demi: 0.14.7(vue@3.4.23) + vue-demi: 0.14.8(vue@3.4.31) transitivePeerDependencies: - '@vue/composition-api' - - rollup - - supports-color - vue dev: true @@ -4139,15 +3788,6 @@ packages: - vue dev: true - /@vueuse/shared@9.13.0(vue@3.4.23): - resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} - dependencies: - vue-demi: 0.14.7(vue@3.4.23) - transitivePeerDependencies: - - '@vue/composition-api' - - vue - dev: true - /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -4221,6 +3861,26 @@ packages: uri-js: 4.4.1 dev: true + /algoliasearch@4.24.0: + resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==} + dependencies: + '@algolia/cache-browser-local-storage': 4.24.0 + '@algolia/cache-common': 4.24.0 + '@algolia/cache-in-memory': 4.24.0 + '@algolia/client-account': 4.24.0 + '@algolia/client-analytics': 4.24.0 + '@algolia/client-common': 4.24.0 + '@algolia/client-personalization': 4.24.0 + '@algolia/client-search': 4.24.0 + '@algolia/logger-common': 4.24.0 + '@algolia/logger-console': 4.24.0 + '@algolia/recommend': 4.24.0 + '@algolia/requester-browser-xhr': 4.24.0 + '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http': 4.24.0 + '@algolia/transporter': 4.24.0 + dev: true + /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -4385,31 +4045,10 @@ packages: is-shared-array-buffer: 1.0.2 dev: true - /assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - dependencies: - call-bind: 1.0.7 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.5 - util: 0.12.5 - dev: true - /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /ast-kit@0.11.3: - resolution: {integrity: sha512-qdwwKEhckRk0XE22/xDdmU3v/60E8Edu4qFhgTLIhGGDs/PAJwLw9pQn8Rj99PitlbBZbYpx0k/lbir4kg0SuA==} - engines: {node: '>=16.14.0'} - dependencies: - '@babel/parser': 7.24.4 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) - pathe: 1.1.2 - transitivePeerDependencies: - - rollup - dev: true - /ast-kit@0.11.3(rollup@3.29.4): resolution: {integrity: sha512-qdwwKEhckRk0XE22/xDdmU3v/60E8Edu4qFhgTLIhGGDs/PAJwLw9pQn8Rj99PitlbBZbYpx0k/lbir4kg0SuA==} engines: {node: '>=16.14.0'} @@ -4421,17 +4060,6 @@ packages: - rollup dev: true - /ast-kit@0.9.5: - resolution: {integrity: sha512-kbL7ERlqjXubdDd+szuwdlQ1xUxEz9mCz1+m07ftNVStgwRb2RWw+U6oKo08PAvOishMxiqz1mlJyLl8yQx2Qg==} - engines: {node: '>=16.14.0'} - dependencies: - '@babel/parser': 7.24.4 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) - pathe: 1.1.2 - transitivePeerDependencies: - - rollup - dev: true - /ast-kit@0.9.5(rollup@3.29.4): resolution: {integrity: sha512-kbL7ERlqjXubdDd+szuwdlQ1xUxEz9mCz1+m07ftNVStgwRb2RWw+U6oKo08PAvOishMxiqz1mlJyLl8yQx2Qg==} engines: {node: '>=16.14.0'} @@ -4443,23 +4071,6 @@ packages: - rollup dev: true - /ast-types@0.15.2: - resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} - engines: {node: '>=4'} - dependencies: - tslib: 2.6.2 - dev: true - - /ast-walker-scope@0.5.0: - resolution: {integrity: sha512-NsyHMxBh4dmdEHjBo1/TBZvCKxffmZxRYhmclfu0PP6Aftre47jOHYaYaNqJcV0bxihxFXhDkzLHUwHc0ocd0Q==} - engines: {node: '>=16.14.0'} - dependencies: - '@babel/parser': 7.24.4 - ast-kit: 0.9.5 - transitivePeerDependencies: - - rollup - dev: true - /ast-walker-scope@0.5.0(rollup@3.29.4): resolution: {integrity: sha512-NsyHMxBh4dmdEHjBo1/TBZvCKxffmZxRYhmclfu0PP6Aftre47jOHYaYaNqJcV0bxihxFXhDkzLHUwHc0ocd0Q==} engines: {node: '>=16.14.0'} @@ -4507,10 +4118,6 @@ packages: /b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} - /bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - dev: true - /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -4678,13 +4285,6 @@ packages: engines: {node: '>=6'} dev: true - /camel-case@4.1.2: - resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - dependencies: - pascal-case: 3.1.2 - tslib: 2.6.2 - dev: true - /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} @@ -4702,18 +4302,6 @@ packages: /caniuse-lite@1.0.30001611: resolution: {integrity: sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q==} - /capital-case@1.0.4: - resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} - dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - upper-case-first: 2.0.2 - dev: true - - /ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - dev: true - /chai@4.4.1: resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} engines: {node: '>=4'} @@ -4747,44 +4335,6 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - /change-case@4.1.2: - resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} - dependencies: - camel-case: 4.1.2 - capital-case: 1.0.4 - constant-case: 3.0.4 - dot-case: 3.0.4 - header-case: 2.0.4 - no-case: 3.0.4 - param-case: 3.0.4 - pascal-case: 3.1.2 - path-case: 3.0.4 - sentence-case: 3.0.4 - snake-case: 3.0.4 - tslib: 2.6.2 - dev: true - - /char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - dev: true - - /character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - dev: true - - /character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - dev: true - - /character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - dev: true - - /character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - dev: true - /check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} dependencies: @@ -4809,10 +4359,6 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - /chroma-js@2.4.2: - resolution: {integrity: sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==} - dev: true - /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -4897,20 +4443,11 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true - /comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - dev: true - /commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} dev: true - /commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} - dev: true - /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -4961,14 +4498,6 @@ packages: /console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - /constant-case@3.0.4: - resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} - dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - upper-case: 2.0.2 - dev: true - /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -4980,6 +4509,13 @@ packages: engines: {node: '>= 0.6'} dev: false + /copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + dependencies: + is-what: 4.1.16 + dev: true + /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -5190,12 +4726,6 @@ packages: dependencies: ms: 2.1.2 - /decode-named-character-reference@1.0.2: - resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} - dependencies: - character-entities: 2.0.2 - dev: true - /deep-eql@4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} @@ -5275,11 +4805,6 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - /dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - dev: true - /destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} @@ -5287,10 +4812,6 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - /detab@3.0.2: - resolution: {integrity: sha512-7Bp16Bk8sk0Y6gdXiCtnpGbghn8atnTJdd/82aWvS5ESnlcNvgUc10U2NYS0PAiDSGjWiI8qs/Cv1b2uSGdQ8w==} - dev: true - /detect-libc@1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} @@ -5304,12 +4825,6 @@ packages: resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} dev: true - /devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dependencies: - dequal: 2.0.3 - dev: true - /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5368,13 +4883,6 @@ packages: domhandler: 5.0.3 dev: true - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - dev: true - /dot-prop@8.0.2: resolution: {integrity: sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ==} engines: {node: '>=16'} @@ -5420,14 +4928,6 @@ packages: /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - /emojilib@2.4.0: - resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} - dev: true - - /emoticon@4.0.1: - resolution: {integrity: sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==} - dev: true - /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -5440,25 +4940,6 @@ packages: dev: true optional: true - /engine.io-client@6.5.3: - resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==} - dependencies: - '@socket.io/component-emitter': 3.1.1 - debug: 4.3.4 - engine.io-parser: 5.2.2 - ws: 8.11.0 - xmlhttprequest-ssl: 2.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /engine.io-parser@5.2.2: - resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==} - engines: {node: '>=10.0.0'} - dev: true - /enhanced-resolve@5.15.0: resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} engines: {node: '>=10.13.0'} @@ -5584,36 +5065,6 @@ packages: is-symbol: 1.0.4 dev: true - /esbuild@0.17.19: - resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.17.19 - '@esbuild/android-arm64': 0.17.19 - '@esbuild/android-x64': 0.17.19 - '@esbuild/darwin-arm64': 0.17.19 - '@esbuild/darwin-x64': 0.17.19 - '@esbuild/freebsd-arm64': 0.17.19 - '@esbuild/freebsd-x64': 0.17.19 - '@esbuild/linux-arm': 0.17.19 - '@esbuild/linux-arm64': 0.17.19 - '@esbuild/linux-ia32': 0.17.19 - '@esbuild/linux-loong64': 0.17.19 - '@esbuild/linux-mips64el': 0.17.19 - '@esbuild/linux-ppc64': 0.17.19 - '@esbuild/linux-riscv64': 0.17.19 - '@esbuild/linux-s390x': 0.17.19 - '@esbuild/linux-x64': 0.17.19 - '@esbuild/netbsd-x64': 0.17.19 - '@esbuild/openbsd-x64': 0.17.19 - '@esbuild/sunos-x64': 0.17.19 - '@esbuild/win32-arm64': 0.17.19 - '@esbuild/win32-ia32': 0.17.19 - '@esbuild/win32-x64': 0.17.19 - dev: true - /esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -5675,6 +5126,37 @@ packages: '@esbuild/win32-ia32': 0.20.2 '@esbuild/win32-x64': 0.20.2 + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -6007,12 +5489,6 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true - /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} @@ -6105,10 +5581,6 @@ packages: resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} dev: true - /extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - dev: true - /externality@1.0.2: resolution: {integrity: sha512-LyExtJWKxtgVzmgtEHyQtLFpw1KFhQphF9nTG8TpAIVkiI/xQ3FJh75tRFLYl4hkn7BNIIdLJInuDAavX35pMw==} dependencies: @@ -6193,12 +5665,6 @@ packages: rimraf: 3.0.2 dev: true - /flat@6.0.1: - resolution: {integrity: sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw==} - engines: {node: '>=18'} - hasBin: true - dev: true - /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true @@ -6207,7 +5673,7 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /floating-vue@5.2.2(vue@3.4.23): + /floating-vue@5.2.2(@nuxt/kit@3.11.2)(vue@3.4.23): resolution: {integrity: sha512-afW+h2CFafo+7Y9Lvw/xsqjaQlKLdJV7h1fCHfcYQ1C4SVMlu7OAekqWgu5d4SgvkBVU0pVpLlVsrSTBURFRkg==} peerDependencies: '@nuxt/kit': ^3.2.0 @@ -6217,6 +5683,7 @@ packages: optional: true dependencies: '@floating-ui/dom': 1.1.1 + '@nuxt/kit': 3.11.2(rollup@3.29.4) vue: 3.4.23(typescript@5.3.3) vue-resize: 2.0.0-alpha.1(vue@3.4.23) dev: true @@ -6308,11 +5775,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /fuse.js@6.6.2: - resolution: {integrity: sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==} - engines: {node: '>=10'} - dev: true - /gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} @@ -6408,16 +5870,6 @@ packages: git-up: 7.0.0 dev: true - /git-url-parse@14.0.0: - resolution: {integrity: sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==} - dependencies: - git-up: 7.0.0 - dev: true - - /github-slugger@2.0.0: - resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - dev: true - /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -6627,120 +6079,36 @@ packages: dev: true /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: true - - /hash-sum@2.0.0: - resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} - - /hasown@2.0.0: - resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} - engines: {node: '>= 0.4'} - dependencies: - function-bind: 1.1.2 - dev: true - - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - dependencies: - function-bind: 1.1.2 - - /hast-util-from-parse5@8.0.1: - resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.2 - devlop: 1.1.0 - hastscript: 8.0.0 - property-information: 6.5.0 - vfile: 6.0.1 - vfile-location: 5.0.2 - web-namespaces: 2.0.1 - dev: true - - /hast-util-heading-rank@3.0.0: - resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} - dependencies: - '@types/hast': 3.0.4 - dev: true - - /hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - dependencies: - '@types/hast': 3.0.4 - dev: true - - /hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - dependencies: - '@types/hast': 3.0.4 - dev: true - - /hast-util-raw@9.0.2: - resolution: {integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==} - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.2 - '@ungap/structured-clone': 1.2.0 - hast-util-from-parse5: 8.0.1 - hast-util-to-parse5: 8.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.1.0 - parse5: 7.1.2 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - dev: true + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - /hast-util-to-parse5@8.0.0: - resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - property-information: 6.5.0 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 + function-bind: 1.1.1 dev: true - /hast-util-to-string@3.0.0: - resolution: {integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==} + /hash-sum@2.0.0: + resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} dependencies: - '@types/hast': 3.0.4 + function-bind: 1.1.2 dev: true - /hastscript@8.0.0: - resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 6.5.0 - space-separated-tokens: 2.0.2 - dev: true + function-bind: 1.1.2 /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true dev: true - /header-case@2.0.4: - resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} - dependencies: - capital-case: 1.0.4 - tslib: 2.6.2 - dev: true - /hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -6760,10 +6128,6 @@ packages: engines: {node: '>=8'} dev: true - /html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - dev: true - /http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} dev: true @@ -6932,30 +6296,6 @@ packages: /iron-webcrypto@1.1.0: resolution: {integrity: sha512-5vgYsCakNlaQub1orZK5QmNYhwYtcllTkZBp5sfIaCqY93Cf6l+v2rtE+E4TMbcfjxDMCdrO8wmp7+ZvhDECLA==} - /is-absolute-url@4.0.1: - resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - - /is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - dev: true - - /is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - dev: true - - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - dev: true - /is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -7017,10 +6357,6 @@ packages: has-tostringtag: 1.0.2 dev: true - /is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - dev: true - /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -7039,23 +6375,12 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.2 - dev: true - /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - /is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - dev: true - /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -7078,14 +6403,6 @@ packages: /is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - /is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - dev: true - /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -7112,11 +6429,6 @@ packages: engines: {node: '>=12'} dev: true - /is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - dev: true - /is-primitive@3.0.1: resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} engines: {node: '>=0.10.0'} @@ -7181,6 +6493,11 @@ packages: call-bind: 1.0.6 dev: true + /is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + dev: true + /is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -7445,10 +6762,6 @@ packages: p-locate: 5.0.0 dev: true - /lodash._reinterpolate@3.0.0: - resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==} - dev: true - /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} @@ -7480,540 +6793,121 @@ packages: dev: false /lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - dev: true - - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - - /lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - dev: false - - /lodash.template@4.5.0: - resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==} - dependencies: - lodash._reinterpolate: 3.0.0 - lodash.templatesettings: 4.2.0 - dev: true - - /lodash.templatesettings@4.2.0: - resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==} - dependencies: - lodash._reinterpolate: 3.0.0 - dev: true - - /lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - dev: true - - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - /longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - dev: true - - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - dev: false - - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - dependencies: - get-func-name: 2.0.2 - dev: true - - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - dependencies: - tslib: 2.6.2 - dev: true - - /lru-cache@10.2.0: - resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} - engines: {node: 14 || >=16.14} - - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - - /magic-string-ast@0.3.0: - resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==} - engines: {node: '>=16.14.0'} - dependencies: - magic-string: 0.30.10 - dev: true - - /magic-string@0.27.0: - resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - - /magic-string@0.30.7: - resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /magicast@0.3.4: - resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} - dependencies: - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 - source-map-js: 1.2.0 - dev: true - - /make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.1 - - /make-fetch-happen@13.0.0: - resolution: {integrity: sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - '@npmcli/agent': 2.2.2 - cacache: 18.0.2 - http-cache-semantics: 4.1.1 - is-lambda: 1.0.1 - minipass: 7.0.4 - minipass-fetch: 3.0.4 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - ssri: 10.0.5 - transitivePeerDependencies: - - supports-color - dev: true - - /markdown-table@3.0.3: - resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} - dev: true - - /mdast-util-find-and-replace@3.0.1: - resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} - dependencies: - '@types/mdast': 4.0.3 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - dev: true - - /mdast-util-from-markdown@2.0.0: - resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} - dependencies: - '@types/mdast': 4.0.3 - '@types/unist': 3.0.2 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-decode-string: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} - dependencies: - '@types/mdast': 4.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.1.0 - dev: true - - /mdast-util-gfm-footnote@2.0.0: - resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} - dependencies: - '@types/mdast': 4.0.3 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - dependencies: - '@types/mdast': 4.0.3 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - dependencies: - '@types/mdast': 4.0.3 - devlop: 1.1.0 - markdown-table: 3.0.3 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - dependencies: - '@types/mdast': 4.0.3 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-gfm@3.0.0: - resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} - dependencies: - mdast-util-from-markdown: 2.0.0 - mdast-util-gfm-autolink-literal: 2.0.0 - mdast-util-gfm-footnote: 2.0.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - dependencies: - '@types/mdast': 4.0.3 - unist-util-is: 6.0.0 - dev: true - - /mdast-util-to-hast@13.1.0: - resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==} - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 - '@ungap/structured-clone': 1.2.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.0 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.1 - dev: true - - /mdast-util-to-markdown@2.1.0: - resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} - dependencies: - '@types/mdast': 4.0.3 - '@types/unist': 3.0.2 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-decode-string: 2.0.0 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - dev: true - - /mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - dependencies: - '@types/mdast': 4.0.3 - dev: true - - /mdn-data@2.0.28: - resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} - dev: true - - /mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - dev: true - - /mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - dev: true - - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - /micromark-core-commonmark@2.0.0: - resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==} - dependencies: - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - micromark-factory-destination: 2.0.0 - micromark-factory-label: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-title: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-html-tag-name: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-autolink-literal@2.0.0: - resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} - dependencies: - micromark-util-character: 2.1.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-footnote@2.0.0: - resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-table@2.0.0: - resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true - - /micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - dependencies: - micromark-util-types: 2.0.0 + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true - /micromark-extension-gfm-task-list-item@2.0.1: - resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - dependencies: - micromark-extension-gfm-autolink-literal: 2.0.0 - micromark-extension-gfm-footnote: 2.0.0 - micromark-extension-gfm-strikethrough: 2.0.0 - micromark-extension-gfm-table: 2.0.0 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.0.1 - micromark-util-combine-extensions: 2.0.0 - micromark-util-types: 2.0.0 - dev: true + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false - /micromark-factory-destination@2.0.0: - resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} - dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + /lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} dev: true - /micromark-factory-label@2.0.0: - resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - /micromark-factory-space@2.0.0: - resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true dependencies: - micromark-util-character: 2.1.0 - micromark-util-types: 2.0.0 - dev: true + js-tokens: 4.0.0 - /micromark-factory-title@2.0.0: - resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + get-func-name: 2.0.2 dev: true - /micromark-factory-whitespace@2.0.0: - resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} - dependencies: - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} - /micromark-util-character@2.1.0: - resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - dev: true + yallist: 3.1.1 - /micromark-util-chunked@2.0.0: - resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} dependencies: - micromark-util-symbol: 2.0.0 - dev: true + yallist: 4.0.0 - /micromark-util-classify-character@2.0.0: - resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + /magic-string-ast@0.3.0: + resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==} + engines: {node: '>=16.14.0'} dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + magic-string: 0.30.10 dev: true - /micromark-util-combine-extensions@2.0.0: - resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} dependencies: - micromark-util-chunked: 2.0.0 - micromark-util-types: 2.0.0 - dev: true + '@jridgewell/sourcemap-codec': 1.4.15 - /micromark-util-decode-numeric-character-reference@2.0.1: - resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + /magic-string@0.30.7: + resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} + engines: {node: '>=12'} dependencies: - micromark-util-symbol: 2.0.0 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /micromark-util-decode-string@2.0.0: - resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + /magicast@0.3.4: + resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 2.1.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-symbol: 2.0.0 - dev: true - - /micromark-util-encode@2.0.0: - resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} - dev: true - - /micromark-util-html-tag-name@2.0.0: - resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + '@babel/parser': 7.24.4 + '@babel/types': 7.24.0 + source-map-js: 1.2.0 dev: true - /micromark-util-normalize-identifier@2.0.0: - resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + /make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} dependencies: - micromark-util-symbol: 2.0.0 - dev: true + semver: 6.3.1 - /micromark-util-resolve-all@2.0.0: - resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + /make-fetch-happen@13.0.0: + resolution: {integrity: sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==} + engines: {node: ^16.14.0 || >=18.0.0} dependencies: - micromark-util-types: 2.0.0 + '@npmcli/agent': 2.2.2 + cacache: 18.0.2 + http-cache-semantics: 4.1.1 + is-lambda: 1.0.1 + minipass: 7.0.4 + minipass-fetch: 3.0.4 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.3 + promise-retry: 2.0.1 + ssri: 10.0.5 + transitivePeerDependencies: + - supports-color dev: true - /micromark-util-sanitize-uri@2.0.0: - resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} - dependencies: - micromark-util-character: 2.1.0 - micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 + /mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} dev: true - /micromark-util-subtokenize@2.0.1: - resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 + /mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} dev: true - /micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + /mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} dev: true - /micromark-util-types@2.0.0: - resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} - dev: true + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - /micromark@4.0.0: - resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} - dependencies: - '@types/debug': 4.1.12 - debug: 4.3.4 - decode-named-character-reference: 1.0.2 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-combine-extensions: 2.0.0 - micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-encode: 2.0.0 - micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.0 - micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: true + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -8233,10 +7127,6 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - /muggle-string@0.2.2: - resolution: {integrity: sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==} - dev: true - /muggle-string@0.3.1: resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} dev: true @@ -8425,27 +7315,10 @@ packages: - supports-color - uWebSockets.js - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - dependencies: - lower-case: 2.0.2 - tslib: 2.6.2 - dev: true - /node-addon-api@7.1.0: resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} engines: {node: ^16 || ^18 || >= 20} - /node-emoji@2.1.3: - resolution: {integrity: sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==} - engines: {node: '>=18'} - dependencies: - '@sindresorhus/is': 4.6.0 - char-regex: 1.0.2 - emojilib: 2.4.0 - skin-tone: 2.0.0 - dev: true - /node-fetch-native@1.6.2: resolution: {integrity: sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==} dev: true @@ -8634,45 +7507,6 @@ packages: fsevents: 2.3.3 dev: true - /nuxt-component-meta@0.6.3(rollup@3.29.4): - resolution: {integrity: sha512-GdqnSMC1vqabry7WSj3GWA2LZ1gBiWeS2lj943c9TjkL9SN/rABEFXVZA6RO4sOTKF1qV947UGi27PdRd7u+tA==} - hasBin: true - dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - citty: 0.1.6 - scule: 1.3.0 - typescript: 5.3.3 - vue-component-meta: 1.8.27(typescript@5.3.3) - transitivePeerDependencies: - - rollup - - supports-color - dev: true - - /nuxt-config-schema@0.4.6(rollup@3.29.4): - resolution: {integrity: sha512-kHLWJFynj5QrxVZ1MjY2xmDaTSN1BCMLGExA+hMMLoCb3wn9TJlDVqnE/nSdUJPMRkNn/NQ5WP9NLA9vlAXRUw==} - dependencies: - '@nuxt/kit': 3.11.2(rollup@3.29.4) - defu: 6.1.4 - jiti: 1.21.0 - pathe: 1.1.2 - untyped: 1.4.2 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - - /nuxt-icon@0.3.3(rollup@3.29.4)(vue@3.4.23): - resolution: {integrity: sha512-KdhJAigBGTP8/YIFZ3orwetk40AgLq6VQ5HRYuDLmv5hiDptor9Ro+WIdZggHw7nciRxZvDdQkEwi9B5G/jrkQ==} - dependencies: - '@iconify/vue': 4.1.1(vue@3.4.23) - '@nuxt/kit': 3.11.2(rollup@3.29.4) - nuxt-config-schema: 0.4.6(rollup@3.29.4) - transitivePeerDependencies: - - rollup - - supports-color - - vue - dev: true - /nuxt@3.11.2(@types/node@18.19.31)(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(rollup@3.29.4)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27): resolution: {integrity: sha512-Be1d4oyFo60pdF+diBolYDcfNemoMYM3R8PDjhnGrs/w3xJoDH1YMUVWHXXY8WhSmYZI7dyBehx/6kTfGFliVA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -8734,134 +7568,9 @@ packages: uncrypto: 0.1.3 unctx: 2.3.1 unenv: 1.9.0 - unimport: 3.7.1(rollup@3.29.4) - unplugin: 1.10.1 - unplugin-vue-router: 0.7.0(rollup@3.29.4)(vue-router@4.3.1)(vue@3.4.23) - unstorage: 1.10.2(ioredis@5.4.1) - untyped: 1.4.2 - vue: 3.4.23(typescript@5.3.3) - vue-bundle-renderer: 2.0.0 - vue-devtools-stub: 0.1.0 - vue-router: 4.3.1(vue@3.4.23) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@unocss/reset' - - '@upstash/redis' - - '@vercel/kv' - - '@vue/composition-api' - - async-validator - - axios - - better-sqlite3 - - bluebird - - bufferutil - - change-case - - drauu - - drizzle-orm - - encoding - - eslint - - floating-vue - - fuse.js - - idb-keyval - - ioredis - - jwt-decode - - less - - lightningcss - - meow - - nprogress - - optionator - - qrcode - - rollup - - sass - - sortablejs - - stylelint - - stylus - - sugarss - - supports-color - - terser - - typescript - - uWebSockets.js - - universal-cookie - - unocss - - utf-8-validate - - vite - - vls - - vti - - vue-tsc - - xml2js - dev: true - - /nuxt@3.11.2(@unocss/reset@0.59.3)(eslint@8.57.0)(floating-vue@5.2.2)(typescript@5.3.3)(unocss@0.59.3)(vite@5.2.9)(vue-tsc@1.8.27): - resolution: {integrity: sha512-Be1d4oyFo60pdF+diBolYDcfNemoMYM3R8PDjhnGrs/w3xJoDH1YMUVWHXXY8WhSmYZI7dyBehx/6kTfGFliVA==} - engines: {node: ^14.18.0 || >=16.10.0} - hasBin: true - peerDependencies: - '@parcel/watcher': ^2.1.0 - '@types/node': ^14.18.0 || >=16.10.0 - peerDependenciesMeta: - '@parcel/watcher': - optional: true - '@types/node': - optional: true - dependencies: - '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.1.5(@unocss/reset@0.59.3)(floating-vue@5.2.2)(nuxt@3.11.2)(unocss@0.59.3)(vite@5.2.9)(vue@3.4.23) - '@nuxt/kit': 3.11.2 - '@nuxt/schema': 3.11.2 - '@nuxt/telemetry': 2.5.3 - '@nuxt/ui-templates': 1.3.3 - '@nuxt/vite-builder': 3.11.2(eslint@8.57.0)(typescript@5.3.3)(vue-tsc@1.8.27)(vue@3.4.23) - '@unhead/dom': 1.9.5 - '@unhead/ssr': 1.9.5 - '@unhead/vue': 1.9.5(vue@3.4.23) - '@vue/shared': 3.4.23 - acorn: 8.11.3 - c12: 1.10.0 - chokidar: 3.6.0 - cookie-es: 1.1.0 - defu: 6.1.4 - destr: 2.0.3 - devalue: 4.3.2 - esbuild: 0.20.2 - escape-string-regexp: 5.0.0 - estree-walker: 3.0.3 - fs-extra: 11.2.0 - globby: 14.0.1 - h3: 1.11.1 - hookable: 5.5.3 - jiti: 1.21.0 - klona: 2.0.6 - knitwork: 1.1.0 - magic-string: 0.30.10 - mlly: 1.6.1 - nitropack: 2.9.6 - nuxi: 3.11.1 - nypm: 0.3.8 - ofetch: 1.3.4 - ohash: 1.1.3 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - pkg-types: 1.0.3 - radix3: 1.1.2 - scule: 1.3.0 - std-env: 3.7.0 - strip-literal: 2.1.0 - ufo: 1.5.3 - ultrahtml: 1.5.3 - uncrypto: 0.1.3 - unctx: 2.3.1 - unenv: 1.9.0 - unimport: 3.7.1(rollup@4.14.3) + unimport: 3.7.1(rollup@3.29.4) unplugin: 1.10.1 - unplugin-vue-router: 0.7.0(vue-router@4.3.1)(vue@3.4.23) + unplugin-vue-router: 0.7.0(rollup@3.29.4)(vue-router@4.3.1)(vue@3.4.23) unstorage: 1.10.2(ioredis@5.4.1) untyped: 1.4.2 vue: 3.4.23(typescript@5.3.3) @@ -8952,14 +7661,6 @@ packages: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: true - /object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - dev: true - /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -9167,22 +7868,6 @@ packages: - supports-color dev: true - /paneer@0.1.0: - resolution: {integrity: sha512-SZfJe/y9fbpeXZU+Kf7cSG2G7rnGP50hUYzCvcWyhp7hYzA3YXGthpkGfv6NSt0oo6QbcRyKwycg/6dpG5p8aw==} - deprecated: Please migrate to https://github.com/unjs/magicast - dependencies: - '@babel/parser': 7.24.4 - '@types/estree': 1.0.5 - recast: 0.22.0 - dev: true - - /param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} - dependencies: - dot-case: 3.0.4 - tslib: 2.6.2 - dev: true - /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -9190,19 +7875,6 @@ packages: callsites: 3.1.0 dev: true - /parse-entities@4.0.1: - resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} - dependencies: - '@types/unist': 2.0.10 - character-entities: 2.0.2 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.0.2 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - dev: true - /parse-git-config@3.0.0: resolution: {integrity: sha512-wXoQGL1D+2COYWCD35/xbiKma1Z15xvZL8cI25wvxzled58V51SJM04Urt/uznS900iQor7QO04SgdfT/XlbuA==} engines: {node: '>=8'} @@ -9233,34 +7905,14 @@ packages: parse-path: 7.0.0 dev: true - /parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - dependencies: - entities: 4.5.0 - dev: true - /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - /pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - dev: true - /path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} dev: true - /path-case@3.0.4: - resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} - dependencies: - dot-case: 3.0.4 - tslib: 2.6.2 - dev: true - /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -9318,39 +7970,14 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - /pinceau@0.18.9(postcss@8.4.38): - resolution: {integrity: sha512-GJ+l8a5Y+7PP/diwuajJhd2QONTIFkk2YXjrVTh7QKC3sMQEphpLH6ZJfXSeeSonQ0/BnhrrMi9a5e14mmqXug==} - dependencies: - '@unocss/reset': 0.50.8 - '@volar/vue-language-core': 1.6.5 - acorn: 8.11.3 - chroma-js: 2.4.2 - consola: 3.2.3 - csstype: 3.1.3 - defu: 6.1.4 - magic-string: 0.30.10 - nanoid: 4.0.2 - ohash: 1.1.3 - paneer: 0.1.0 - pathe: 1.1.2 - postcss-custom-properties: 13.1.4(postcss@8.4.38) - postcss-dark-theme-class: 0.7.3(postcss@8.4.38) - postcss-nested: 6.0.1(postcss@8.4.38) - recast: 0.22.0 - scule: 1.3.0 - style-dictionary-esm: 1.9.2 - unbuild: 1.2.1 - unplugin: 1.10.1 - transitivePeerDependencies: - - postcss - - sass - - supports-color - dev: true - /pkg-types@1.0.3: resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} dependencies: @@ -9419,28 +8046,6 @@ packages: postcss-value-parser: 4.2.0 dev: true - /postcss-custom-properties@13.1.4(postcss@8.4.38): - resolution: {integrity: sha512-iSAdaZrM3KMec8cOSzeTUNXPYDlhqsMJHpt62yrjwG6nAnMtRHPk5JdMzGosBJtqEahDolvD5LNbcq+EZ78o5g==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - postcss: ^8.4 - dependencies: - '@csstools/cascade-layer-name-parser': 1.0.9(@csstools/css-parser-algorithms@2.6.1)(@csstools/css-tokenizer@2.2.4) - '@csstools/css-parser-algorithms': 2.6.1(@csstools/css-tokenizer@2.2.4) - '@csstools/css-tokenizer': 2.2.4 - postcss: 8.4.38 - postcss-value-parser: 4.2.0 - dev: true - - /postcss-dark-theme-class@0.7.3(postcss@8.4.38): - resolution: {integrity: sha512-M9vtfh8ORzQsVdT9BWb+xpEDAzC7nHBn7wVc988/JkEVLPupKcUnV0jw7RZ8sSj0ovpqN1POf6PLdt19JCHfhQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - dependencies: - postcss: 8.4.38 - dev: true - /postcss-discard-comments@6.0.2(postcss@8.4.38): resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} engines: {node: ^14 || ^16 || >=18.0} @@ -9736,6 +8341,15 @@ packages: source-map-js: 1.2.0 dev: true + /postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + dev: true + /preact-render-to-string@5.2.6(preact@10.19.4): resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} peerDependencies: @@ -9747,7 +8361,6 @@ packages: /preact@10.19.4: resolution: {integrity: sha512-dwaX5jAh0Ga8uENBX1hSOujmKWgx9RtL80KaKUFLc6jb4vCEAc3EeZ0rnQO/FO4VgjfPMfoLFWnNG8bHuZ9VLw==} - dev: false /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -9813,10 +8426,6 @@ packages: sisteransi: 1.0.5 dev: true - /property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - dev: true - /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} dev: true @@ -9862,7 +8471,6 @@ packages: loose-envify: 1.4.0 react: 18.2.0 scheduler: 0.23.0 - dev: false /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} @@ -9873,7 +8481,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 - dev: false /read-package-json-fast@3.0.2: resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} @@ -9952,17 +8559,6 @@ packages: dependencies: picomatch: 2.3.1 - /recast@0.22.0: - resolution: {integrity: sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==} - engines: {node: '>= 4'} - dependencies: - assert: 2.1.0 - ast-types: 0.15.2 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.6.2 - dev: true - /redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -9996,128 +8592,6 @@ packages: engines: {node: '>=8'} dev: true - /rehype-external-links@3.0.0: - resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==} - dependencies: - '@types/hast': 3.0.4 - '@ungap/structured-clone': 1.2.0 - hast-util-is-element: 3.0.0 - is-absolute-url: 4.0.1 - space-separated-tokens: 2.0.2 - unist-util-visit: 5.0.0 - dev: true - - /rehype-raw@7.0.0: - resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} - dependencies: - '@types/hast': 3.0.4 - hast-util-raw: 9.0.2 - vfile: 6.0.1 - dev: true - - /rehype-slug@6.0.0: - resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} - dependencies: - '@types/hast': 3.0.4 - github-slugger: 2.0.0 - hast-util-heading-rank: 3.0.0 - hast-util-to-string: 3.0.0 - unist-util-visit: 5.0.0 - dev: true - - /rehype-sort-attribute-values@5.0.0: - resolution: {integrity: sha512-dQdHdCIRnpiU+BkrLSqH+aM4lWJyLqGzv49KvH4gHj+JxYwNqvGhoTXckS3AJu4V9ZutwsTcawP0pC7PhwX0tQ==} - dependencies: - '@types/hast': 3.0.4 - hast-util-is-element: 3.0.0 - unist-util-visit: 5.0.0 - dev: true - - /rehype-sort-attributes@5.0.0: - resolution: {integrity: sha512-6tJUH4xHFcdO85CZRwAcEtHNCzjZ9V9S0VZLgo1pzbN04qy8jiVCZ3oAxDmBVG3Rth5b1xFTDet5WG/UYZeJLQ==} - dependencies: - '@types/hast': 3.0.4 - unist-util-visit: 5.0.0 - dev: true - - /remark-emoji@4.0.1: - resolution: {integrity: sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - '@types/mdast': 4.0.3 - emoticon: 4.0.1 - mdast-util-find-and-replace: 3.0.1 - node-emoji: 2.1.3 - unified: 11.0.4 - dev: true - - /remark-gfm@4.0.0: - resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} - dependencies: - '@types/mdast': 4.0.3 - mdast-util-gfm: 3.0.0 - micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 - remark-stringify: 11.0.0 - unified: 11.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /remark-mdc@3.2.0: - resolution: {integrity: sha512-zRi5frIC3O/bcxXgUPHfQ3lyRBKPtokrGnsOPvNrt9bqp4EfjPtduzcWgO4R1WeHYUnvl0zeBStvGsFkJIZf+Q==} - dependencies: - '@types/mdast': 4.0.3 - '@types/unist': 3.0.2 - flat: 6.0.1 - js-yaml: 4.1.0 - mdast-util-from-markdown: 2.0.0 - mdast-util-to-markdown: 2.1.0 - micromark: 4.0.0 - micromark-core-commonmark: 2.0.0 - micromark-factory-space: 2.0.0 - micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-types: 2.0.0 - parse-entities: 4.0.1 - scule: 1.3.0 - stringify-entities: 4.0.4 - unified: 11.0.4 - unist-util-visit: 5.0.0 - unist-util-visit-parents: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - dependencies: - '@types/mdast': 4.0.3 - mdast-util-from-markdown: 2.0.0 - micromark-util-types: 2.0.0 - unified: 11.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /remark-rehype@11.1.0: - resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 - mdast-util-to-hast: 13.1.0 - unified: 11.0.4 - vfile: 6.0.1 - dev: true - - /remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - dependencies: - '@types/mdast': 4.0.3 - mdast-util-to-markdown: 2.1.0 - unified: 11.0.4 - dev: true - /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -10169,26 +8643,16 @@ packages: resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} dev: true + /rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + dev: true + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 - /rollup-plugin-dts@5.3.1(rollup@3.29.4)(typescript@5.3.3): - resolution: {integrity: sha512-gusMi+Z4gY/JaEQeXnB0RUdU82h1kF0WYzCWgVmV4p3hWXqelaKuCvcJawfeg+EKn2T1Ie+YWF2OiN1/L8bTVg==} - engines: {node: '>=v14.21.3'} - peerDependencies: - rollup: ^3.0 - typescript: ^4.1 || ^5.0 - dependencies: - magic-string: 0.30.10 - rollup: 3.29.4 - typescript: 5.3.3 - optionalDependencies: - '@babel/code-frame': 7.24.2 - dev: true - /rollup-plugin-dts@6.1.0(rollup@3.29.4)(typescript@5.3.3): resolution: {integrity: sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==} engines: {node: '>=16'} @@ -10319,11 +8783,14 @@ packages: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 - dev: false /scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + /search-insights@2.14.0: + resolution: {integrity: sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==} + dev: true + /semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -10360,14 +8827,6 @@ packages: transitivePeerDependencies: - supports-color - /sentence-case@3.0.4: - resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} - dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - upper-case-first: 2.0.2 - dev: true - /serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} dependencies: @@ -10442,10 +8901,10 @@ packages: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} dev: true - /shiki@1.3.0: - resolution: {integrity: sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww==} + /shiki@1.10.1: + resolution: {integrity: sha512-uafV7WCgN4YYrccH6yxpnps6k38sSTlFRrwc4jycWmhWxJIm9dPrk+XkY1hZ2t0I7jmacMNb15Lf2fspa/Y3lg==} dependencies: - '@shikijs/core': 1.3.0 + '@shikijs/core': 1.10.1 dev: true /side-channel@1.0.5: @@ -10506,13 +8965,6 @@ packages: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true - /skin-tone@2.0.0: - resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} - engines: {node: '>=8'} - dependencies: - unicode-emoji-modifier-base: 1.0.0 - dev: true - /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -10526,11 +8978,6 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} - /slugify@1.6.6: - resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} - engines: {node: '>=8.0.0'} - dev: true - /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -10539,37 +8986,6 @@ packages: /smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} - /snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - dependencies: - dot-case: 3.0.4 - tslib: 2.6.2 - dev: true - - /socket.io-client@4.7.5: - resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==} - engines: {node: '>=10.0.0'} - dependencies: - '@socket.io/component-emitter': 3.1.1 - debug: 4.3.4 - engine.io-client: 6.5.3 - socket.io-parser: 4.2.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /socket.io-parser@4.2.4: - resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} - engines: {node: '>=10.0.0'} - dependencies: - '@socket.io/component-emitter': 3.1.1 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - /socks-proxy-agent@8.0.3: resolution: {integrity: sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==} engines: {node: '>= 14'} @@ -10607,10 +9023,6 @@ packages: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} - /space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - dev: true - /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -10731,13 +9143,6 @@ packages: dependencies: safe-buffer: 5.2.1 - /stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - dev: true - /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -10793,23 +9198,6 @@ packages: js-tokens: 9.0.0 dev: true - /style-dictionary-esm@1.9.2: - resolution: {integrity: sha512-MR+ppTqzkJJtXH6UyDJ0h4h4ekBCePA8A8xlYNuL0tLj2K+ngyuxoe0AvCHQ7sJVX8O5WK2z32ANSgIcF4mGxw==} - hasBin: true - dependencies: - chalk: 5.3.0 - change-case: 4.1.2 - commander: 11.1.0 - consola: 3.2.3 - fast-glob: 3.3.2 - glob: 10.3.12 - jiti: 1.21.0 - json5: 2.2.3 - jsonc-parser: 3.2.1 - lodash.template: 4.5.0 - tinycolor2: 1.6.0 - dev: true - /styled-jsx@5.1.1(@babel/core@7.24.4)(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -10839,6 +9227,13 @@ packages: postcss-selector-parser: 6.0.16 dev: true + /superjson@2.2.1: + resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} + engines: {node: '>=16'} + dependencies: + copy-anything: 3.0.5 + dev: true + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -10931,10 +9326,6 @@ packages: resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} dev: true - /tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - dev: true - /tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} engines: {node: '>=14.0.0'} @@ -10967,14 +9358,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - /trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - dev: true - - /trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - dev: true - /ts-api-utils@1.2.1(typescript@5.3.3): resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==} engines: {node: '>=16'} @@ -11006,6 +9389,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false /tuf-js@2.2.0: resolution: {integrity: sha512-ZSDngmP1z6zw+FIkIBjvOp/II/mIub/O7Pp12j1WNsiCpg5R5wAc//i555bBQsE44O94btLt0xM/Zr2LQjwdCg==} @@ -11114,40 +9498,6 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /unbuild@1.2.1: - resolution: {integrity: sha512-J4efk69Aye43tWcBPCsLK7TIRppGrEN4pAlDzRKo3HSE6MgTSTBxSEuE3ccx7ixc62JvGQ/CoFXYqqF2AHozow==} - hasBin: true - dependencies: - '@rollup/plugin-alias': 5.1.0(rollup@3.29.4) - '@rollup/plugin-commonjs': 24.1.0(rollup@3.29.4) - '@rollup/plugin-json': 6.1.0(rollup@3.29.4) - '@rollup/plugin-node-resolve': 15.2.3(rollup@3.29.4) - '@rollup/plugin-replace': 5.0.5(rollup@3.29.4) - '@rollup/pluginutils': 5.1.0(rollup@3.29.4) - chalk: 5.3.0 - consola: 3.2.3 - defu: 6.1.4 - esbuild: 0.17.19 - globby: 13.2.2 - hookable: 5.5.3 - jiti: 1.21.0 - magic-string: 0.30.10 - mkdist: 1.4.0(typescript@5.3.3) - mlly: 1.6.1 - mri: 1.2.0 - pathe: 1.1.2 - pkg-types: 1.0.3 - pretty-bytes: 6.1.1 - rollup: 3.29.4 - rollup-plugin-dts: 5.3.1(rollup@3.29.4)(typescript@5.3.3) - scule: 1.3.0 - typescript: 5.3.3 - untyped: 1.4.2 - transitivePeerDependencies: - - sass - - supports-color - dev: true - /unbuild@2.0.0(typescript@5.3.3): resolution: {integrity: sha512-JWCUYx3Oxdzvw2J9kTAp+DKE8df/BnH/JTSj6JyA4SH40ECdFu7FoJJcrm8G92B7TjofQ6GZGjJs50TRxoH6Wg==} hasBin: true @@ -11233,27 +9583,10 @@ packages: hookable: 5.5.3 dev: true - /unicode-emoji-modifier-base@1.0.0: - resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} - engines: {node: '>=4'} - dev: true - /unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - /unified@11.0.4: - resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==} - dependencies: - '@types/unist': 3.0.2 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.1 - dev: true - /unimport@3.7.1(rollup@3.29.4): resolution: {integrity: sha512-V9HpXYfsZye5bPPYUgs0Otn3ODS1mDUciaBlXljI4C2fTwfFpvFZRywmlOu943puN9sncxROMZhsZCjNXEpzEQ==} dependencies: @@ -11299,57 +9632,18 @@ packages: unique-slug: 4.0.0 dev: true - /unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - imurmurhash: 0.1.4 - dev: true - - /unist-builder@4.0.0: - resolution: {integrity: sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg==} - dependencies: - '@types/unist': 3.0.2 - dev: true - - /unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - dependencies: - '@types/unist': 3.0.2 - dev: true - - /unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - dependencies: - '@types/unist': 3.0.2 - dev: true - - /unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - dependencies: - '@types/unist': 3.0.2 - dev: true - - /unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - dependencies: - '@types/unist': 3.0.2 - unist-util-is: 6.0.0 - dev: true - - /unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + /unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - '@types/unist': 3.0.2 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 + imurmurhash: 0.1.4 dev: true /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - /unocss@0.59.3(postcss@8.4.38)(vite@5.2.9): + /unocss@0.59.3(postcss@8.4.38)(rollup@3.29.4)(vite@5.2.9): resolution: {integrity: sha512-4Sos0FjDX5Ck/cV1wrTase0r2V/LI/bIncguisIGq9v7/akghsGEqU8LlxZNqoCug/vpcQICmzt/zclJVUT+GQ==} engines: {node: '>=14'} peerDependencies: @@ -11361,8 +9655,8 @@ packages: vite: optional: true dependencies: - '@unocss/astro': 0.59.3(vite@5.2.9) - '@unocss/cli': 0.59.3 + '@unocss/astro': 0.59.3(rollup@3.29.4)(vite@5.2.9) + '@unocss/cli': 0.59.3(rollup@3.29.4) '@unocss/core': 0.59.3 '@unocss/extractor-arbitrary-variants': 0.59.3 '@unocss/postcss': 0.59.3(postcss@8.4.38) @@ -11380,8 +9674,8 @@ packages: '@unocss/transformer-compile-class': 0.59.3 '@unocss/transformer-directives': 0.59.3 '@unocss/transformer-variant-group': 0.59.3 - '@unocss/vite': 0.59.3(vite@5.2.9) - vite: 5.2.9 + '@unocss/vite': 0.59.3(rollup@3.29.4)(vite@5.2.9) + vite: 5.2.9(@types/node@18.19.31) transitivePeerDependencies: - postcss - rollup @@ -11415,33 +9709,6 @@ packages: - vue dev: true - /unplugin-vue-router@0.7.0(vue-router@4.3.1)(vue@3.4.23): - resolution: {integrity: sha512-ddRreGq0t5vlSB7OMy4e4cfU1w2AwBQCwmvW3oP/0IHQiokzbx4hd3TpwBu3eIAFVuhX2cwNQwp1U32UybTVCw==} - peerDependencies: - vue-router: ^4.1.0 - peerDependenciesMeta: - vue-router: - optional: true - dependencies: - '@babel/types': 7.24.0 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) - '@vue-macros/common': 1.10.1(vue@3.4.23) - ast-walker-scope: 0.5.0 - chokidar: 3.6.0 - fast-glob: 3.3.2 - json5: 2.2.3 - local-pkg: 0.4.3 - mlly: 1.6.1 - pathe: 1.1.2 - scule: 1.3.0 - unplugin: 1.10.1 - vue-router: 4.3.1(vue@3.4.23) - yaml: 2.3.4 - transitivePeerDependencies: - - rollup - - vue - dev: true - /unplugin@1.10.1: resolution: {integrity: sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==} engines: {node: '>=14.0.0'} @@ -11551,18 +9818,6 @@ packages: escalade: 3.1.2 picocolors: 1.0.0 - /upper-case-first@2.0.2: - resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} - dependencies: - tslib: 2.6.2 - dev: true - - /upper-case@2.0.2: - resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} - dependencies: - tslib: 2.6.2 - dev: true - /uqr@0.1.2: resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} @@ -11578,16 +9833,6 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - /util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - dependencies: - inherits: 2.0.4 - is-arguments: 1.1.1 - is-generator-function: 1.0.10 - is-typed-array: 1.1.13 - which-typed-array: 1.1.15 - dev: true - /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -11607,55 +9852,12 @@ packages: builtins: 5.1.0 dev: true - /vfile-location@5.0.2: - resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} - dependencies: - '@types/unist': 3.0.2 - vfile: 6.0.1 - dev: true - - /vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - dependencies: - '@types/unist': 3.0.2 - unist-util-stringify-position: 4.0.0 - dev: true - - /vfile@6.0.1: - resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} - dependencies: - '@types/unist': 3.0.2 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.2 - dev: true - /vite-hot-client@0.2.3(vite@5.2.9): resolution: {integrity: sha512-rOGAV7rUlUHX89fP2p2v0A2WWvV3QMX2UYq0fRqsWSvFvev4atHWqjwGoKaZT1VTKyLGk533ecu3eyd0o59CAg==} peerDependencies: vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 dependencies: - vite: 5.2.9 - dev: true - - /vite-node@1.5.0: - resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.4 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.9 - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser + vite: 5.2.9(@types/node@18.19.31) dev: true /vite-node@1.5.0(@types/node@18.19.31): @@ -11723,7 +9925,7 @@ packages: strip-ansi: 6.0.1 tiny-invariant: 1.3.1 typescript: 5.3.3 - vite: 5.2.9 + vite: 5.2.9(@types/node@18.19.31) vscode-languageclient: 7.0.0 vscode-languageserver: 7.0.0 vscode-languageserver-textdocument: 1.0.8 @@ -11757,32 +9959,6 @@ packages: - supports-color dev: true - /vite-plugin-inspect@0.8.3(@nuxt/kit@3.11.2)(vite@5.2.9): - resolution: {integrity: sha512-SBVzOIdP/kwe6hjkt7LSW4D0+REqqe58AumcnCfRNw4Kt3mbS9pEBkch+nupu2PBxv2tQi69EQHQ1ZA1vgB/Og==} - engines: {node: '>=14'} - peerDependencies: - '@nuxt/kit': '*' - vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 - peerDependenciesMeta: - '@nuxt/kit': - optional: true - dependencies: - '@antfu/utils': 0.7.7 - '@nuxt/kit': 3.11.2 - '@rollup/pluginutils': 5.1.0(rollup@4.14.3) - debug: 4.3.4 - error-stack-parser-es: 0.1.1 - fs-extra: 11.2.0 - open: 10.1.0 - perfect-debounce: 1.0.0 - picocolors: 1.0.0 - sirv: 2.0.4 - vite: 5.2.9 - transitivePeerDependencies: - - rollup - - supports-color - dev: true - /vite-plugin-vue-inspector@4.0.2(vite@5.2.9): resolution: {integrity: sha512-KPvLEuafPG13T7JJuQbSm5PwSxKFnVS965+MP1we2xGw9BPkkc/+LPix5MMWenpKWqtjr0ws8THrR+KuoDC8hg==} peerDependencies: @@ -11797,12 +9973,12 @@ packages: '@vue/compiler-dom': 3.4.23 kolorist: 1.8.0 magic-string: 0.30.10 - vite: 5.2.9 + vite: 5.2.9(@types/node@18.19.31) transitivePeerDependencies: - supports-color dev: true - /vite@5.2.9: + /vite@5.2.9(@types/node@18.19.31): resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -11830,6 +10006,7 @@ packages: terser: optional: true dependencies: + '@types/node': 18.19.31 esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.14.3 @@ -11837,8 +10014,8 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.2.9(@types/node@18.19.31): - resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} + /vite@5.3.3(@types/node@18.19.31): + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -11866,40 +10043,74 @@ packages: optional: true dependencies: '@types/node': 18.19.31 - esbuild: 0.20.2 - postcss: 8.4.38 + esbuild: 0.21.5 + postcss: 8.4.39 rollup: 4.14.3 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest-environment-nuxt@1.0.0(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(rollup@3.29.4)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23): - resolution: {integrity: sha512-AWMO9h4HdbaFdPWZw34gALFI8gbBiOpvfbyeZwHIPfh4kWg/TwElYHvYMQ61WPUlCGaS5LebfHkaI0WPyb//Iw==} + /vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/node@18.19.31)(postcss@8.4.38)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.14.0)(typescript@5.3.3): + resolution: {integrity: sha512-GvEsrEeNLiDE1+fuwDAYJCYLNZDAna+EtnXlPajhv/MYeTjbNK6Bvyg6NoTdO1sbwuQJ0vuJR99bOlH53bo6lg==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true dependencies: - '@nuxt/test-utils': 3.12.1(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(rollup@3.29.4)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23) + '@docsearch/css': 3.6.0 + '@docsearch/js': 3.6.0(@algolia/client-search@4.24.0)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.14.0) + '@shikijs/core': 1.10.1 + '@shikijs/transformers': 1.10.1 + '@types/markdown-it': 14.1.1 + '@vitejs/plugin-vue': 5.0.5(vite@5.3.3)(vue@3.4.31) + '@vue/devtools-api': 7.3.5 + '@vue/shared': 3.4.31 + '@vueuse/core': 10.11.0(vue@3.4.31) + '@vueuse/integrations': 10.11.0(focus-trap@7.5.4)(vue@3.4.31) + focus-trap: 7.5.4 + mark.js: 8.11.1 + minisearch: 6.3.0 + postcss: 8.4.38 + shiki: 1.10.1 + vite: 5.3.3(@types/node@18.19.31) + vue: 3.4.31(typescript@5.3.3) transitivePeerDependencies: - - '@cucumber/cucumber' - - '@jest/globals' - - '@playwright/test' - - '@testing-library/vue' - - '@vitest/ui' - - '@vue/test-utils' - - h3 - - happy-dom - - jsdom - - playwright-core - - rollup - - supports-color - - vite - - vitest - - vue - - vue-router + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie dev: true - /vitest-environment-nuxt@1.0.0(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23): + /vitest-environment-nuxt@1.0.0(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(rollup@3.29.4)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23): resolution: {integrity: sha512-AWMO9h4HdbaFdPWZw34gALFI8gbBiOpvfbyeZwHIPfh4kWg/TwElYHvYMQ61WPUlCGaS5LebfHkaI0WPyb//Iw==} dependencies: - '@nuxt/test-utils': 3.12.1(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23) + '@nuxt/test-utils': 3.12.1(@playwright/test@1.43.1)(@vue/test-utils@2.4.5)(h3@1.11.1)(rollup@3.29.4)(vite@5.2.9)(vitest@1.5.0)(vue-router@4.3.1)(vue@3.4.23) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -11919,61 +10130,6 @@ packages: - vue-router dev: true - /vitest@1.5.0: - resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.5.0 - '@vitest/ui': 1.5.0 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@vitest/expect': 1.5.0 - '@vitest/runner': 1.5.0 - '@vitest/snapshot': 1.5.0 - '@vitest/spy': 1.5.0 - '@vitest/utils': 1.5.0 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.7 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 2.0.0 - tinybench: 2.6.0 - tinypool: 0.8.4 - vite: 5.2.9 - vite-node: 1.5.0 - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - /vitest@1.5.0(@types/node@18.19.31): resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -12076,25 +10232,6 @@ packages: ufo: 1.5.3 dev: true - /vue-component-meta@1.8.27(typescript@5.3.3): - resolution: {integrity: sha512-j3WJsyQHP4TDlvnjHc/eseo0/eVkf0FaCpkqGwez5zD+Tj31onBzWZEXTnWKs8xRj0n3dMNYdy3SpiS6NubSvg==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@volar/typescript': 1.11.1 - '@vue/language-core': 1.8.27(typescript@5.3.3) - path-browserify: 1.0.1 - typescript: 5.3.3 - vue-component-type-helpers: 1.8.27 - dev: true - - /vue-component-type-helpers@1.8.27: - resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==} - dev: true - /vue-component-type-helpers@2.0.13: resolution: {integrity: sha512-xNO5B7DstNWETnoYflLkVgh8dK8h2ZDgxY1M2O0zrqGeBNq5yAZ8a10yCS9+HnixouNGYNX+ggU9MQQq86HTpg==} dev: true @@ -12114,6 +10251,21 @@ packages: vue: 3.4.23(typescript@5.3.3) dev: true + /vue-demi@0.14.8(vue@3.4.31): + resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.4.31(typescript@5.3.3) + dev: true + /vue-devtools-stub@0.1.0: resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==} dev: true @@ -12207,6 +10359,22 @@ packages: typescript: 5.3.3 dev: true + /vue@3.4.31(typescript@5.3.3): + resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@vue/compiler-dom': 3.4.31 + '@vue/compiler-sfc': 3.4.31 + '@vue/runtime-dom': 3.4.31 + '@vue/server-renderer': 3.4.31(vue@3.4.31) + '@vue/shared': 3.4.31 + typescript: 5.3.3 + dev: true + /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -12215,10 +10383,6 @@ packages: graceful-fs: 4.2.11 dev: false - /web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - dev: true - /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -12323,19 +10487,6 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - /ws@8.11.0: - resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true - /ws@8.16.0: resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} @@ -12354,11 +10505,6 @@ packages: engines: {node: '>=12'} dev: true - /xmlhttprequest-ssl@2.0.0: - resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} - engines: {node: '>=0.4.0'} - dev: true - /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -12415,7 +10561,3 @@ packages: /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - - /zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - dev: true