├── packages
├── playground
│ ├── README.md
│ ├── src
│ │ ├── postcss.config.js
│ │ ├── vite-env.d.ts
│ │ ├── App.vue
│ │ ├── pages
│ │ │ ├── register.vue
│ │ │ ├── about.vue
│ │ │ ├── contact.vue
│ │ │ ├── index.vue
│ │ │ ├── dashboard
│ │ │ │ ├── profile.vue
│ │ │ │ ├── posts.vue
│ │ │ │ ├── users.vue
│ │ │ │ └── index.vue
│ │ │ └── login.vue
│ │ ├── layouts
│ │ │ ├── auth.vue
│ │ │ ├── dashboard.vue
│ │ │ └── default.vue
│ │ ├── middleware
│ │ │ ├── logger.ts
│ │ │ ├── index.ts
│ │ │ └── dashboard.ts
│ │ ├── router
│ │ │ ├── index.ts
│ │ │ └── routes.ts
│ │ ├── style.css
│ │ ├── composables
│ │ │ └── index.ts
│ │ ├── components
│ │ │ └── Navbar.vue
│ │ └── main.ts
│ ├── vercel.json
│ ├── postcss.config.js
│ ├── tailwind.config.js
│ ├── tsconfig.node.json
│ ├── vite.config.ts
│ ├── index.html
│ ├── package.json
│ └── tsconfig.json
└── vue-middleware
│ ├── src
│ ├── drivers
│ │ ├── index.ts
│ │ ├── driver.ts
│ │ └── laravel.ts
│ ├── globalDeclarations.ts
│ ├── composables
│ │ └── index.ts
│ ├── index.ts
│ └── handler.ts
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── package.json
│ ├── LICENSE
│ └── README.md
├── pnpm-workspace.yaml
├── .vscode
└── extensions.json
├── docs
├── installation.md
├── package.json
├── public
│ └── vue-logo.svg
├── introduction.md
├── api-examples.md
├── index.md
├── page-title.md
├── can-and-is.md
├── .vitepress
│ └── config.mts
├── custom-drivers.md
├── quick-usage.md
├── roles-and-permissions.md
├── basics.md
└── pnpm-lock.yaml
├── .gitignore
├── tsconfig.json
├── package.json
├── LICENSE
├── README.md
└── scripts
└── build.js
/packages/playground/README.md:
--------------------------------------------------------------------------------
1 | # Vue middleware playground
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - docs
3 | - packages/*
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/playground/src/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | //
3 | }
--------------------------------------------------------------------------------
/packages/playground/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/playground/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/register.vue:
--------------------------------------------------------------------------------
1 |
2 | Register
3 |
4 |
--------------------------------------------------------------------------------
/packages/playground/src/layouts/auth.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/about.vue:
--------------------------------------------------------------------------------
1 |
2 | About
3 |
4 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/contact.vue:
--------------------------------------------------------------------------------
1 |
2 | Contact
3 |
4 |
--------------------------------------------------------------------------------
/packages/vue-middleware/src/drivers/index.ts:
--------------------------------------------------------------------------------
1 | export { LaravelPermissionsDriver } from './laravel'
2 |
--------------------------------------------------------------------------------
/packages/playground/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/playground/src/middleware/logger.ts:
--------------------------------------------------------------------------------
1 | export default () => {
2 | console.log('> 🔥 The `logger` middleware..')
3 | }
4 |
--------------------------------------------------------------------------------
/packages/playground/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | Quickly install `vue-middleware` into your project, in this example we're using npm.
4 |
5 | ```
6 | npm i vue-middleware
7 | ```
8 |
--------------------------------------------------------------------------------
/packages/playground/src/middleware/index.ts:
--------------------------------------------------------------------------------
1 | import dashboard from './dashboard'
2 | import logger from './logger'
3 |
4 | export default {
5 | dashboard,
6 | logger,
7 | }
8 |
--------------------------------------------------------------------------------
/packages/playground/src/layouts/dashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/packages/playground/src/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/packages/playground/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router'
2 | import routes from './routes'
3 |
4 | export default createRouter({
5 | history: createWebHistory(),
6 | routes,
7 | })
8 |
--------------------------------------------------------------------------------
/packages/vue-middleware/src/globalDeclarations.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
3 | declare global {
4 | interface Window {
5 | Laravel: undefined | {
6 | permissions: string[]
7 | roles: string[]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "private": true,
4 | "scripts": {
5 | "docs:dev": "vitepress dev",
6 | "docs:build": "vitepress build",
7 | "docs:preview": "vitepress preview"
8 | },
9 | "dependencies": {
10 | "vitepress": "^1.1.3"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/playground/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | './index.html',
5 | './src/**/*.{js,ts,jsx,tsx,vue}',
6 | ],
7 | theme: {
8 | fontFamily: {
9 | sans: ['Inter'],
10 | },
11 | },
12 | plugins: [],
13 | }
14 |
--------------------------------------------------------------------------------
/packages/playground/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "esnext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/vue-middleware/src/composables/index.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentInstance } from 'vue'
2 |
3 | export function usePermissions() {
4 | const globalprops = getCurrentInstance()?.appContext.config.globalProperties
5 |
6 | return {
7 | is: globalprops?.is,
8 | can: globalprops?.can,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Welcome
4 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore voluptate cumque modi debitis dolor eum neque illum eaque quod sapiente?
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/playground/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url'
2 | import { defineConfig } from 'vite'
3 | import vue from '@vitejs/plugin-vue'
4 |
5 | export default defineConfig({
6 | plugins: [vue()],
7 | resolve: {
8 | alias: {
9 | '@': fileURLToPath(new URL('./src', import.meta.url)),
10 | },
11 | },
12 | })
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | docs/.vitepress/cache
--------------------------------------------------------------------------------
/packages/vue-middleware/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": ["src", "src/globalDeclarations.d.ts"],
4 | "exclude": ["node_modules", "dist"],
5 | "compilerOptions": {
6 | "noEmit": false,
7 | "declaration": true,
8 | "emitDeclarationOnly": true,
9 | "outDir": "dist/types",
10 | "baseUrl": "./",
11 | "paths": {
12 | "@/*": ["./src/*"]
13 | },
14 | "sourceMap": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vue middleware dplayground
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/public/vue-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "useDefineForClassFields": true,
5 | "module": "esnext",
6 | "lib": ["es2020", "dom", "dom.iterable"],
7 | "skipLibCheck": true,
8 | "moduleResolution": "bundler",
9 | "allowImportingTsExtensions": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": true,
13 | "strict": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "types": ["node"],
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/packages/playground/src/middleware/dashboard.ts:
--------------------------------------------------------------------------------
1 | import { type MiddlewareContext } from 'vue-middleware'
2 | import { useAuth } from '@/composables'
3 |
4 | export default ({ app, router, from, to, redirect, abort, guard }: MiddlewareContext) => {
5 | const { loggedIn } = useAuth()
6 |
7 | console.log('> 🔥 The `dashboard` middleware..', guard)
8 |
9 | if (loggedIn.value && guard === 'guest') {
10 | return redirect({ name: 'dashboard' })
11 | }
12 |
13 | if (!loggedIn.value && !guard) {
14 | return redirect({ path: '/auth/login' })
15 | }
16 |
17 | // return abort()
18 | }
19 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/dashboard/profile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Profile
4 |
5 |
Profile
6 |
Name: {{ user.name }}
7 |
ADMIN
8 |
9 |
10 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/packages/vue-middleware/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'path'
2 | import { defineConfig } from 'vite'
3 |
4 | export default defineConfig({
5 | build: {
6 | emptyOutDir: false,
7 | lib: {
8 | entry: resolve(__dirname, 'src/index.ts'),
9 | name: 'vue-middleware',
10 | fileName: 'vue-middleware',
11 | formats: ['umd', 'es', 'cjs'],
12 | },
13 | rollupOptions: {
14 | external: ['vue', 'vue-router'],
15 | output: {
16 | exports: 'named',
17 | globals: {
18 | vue: 'Vue',
19 | 'vue-router': 'vue-router',
20 | },
21 | },
22 | },
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/packages/playground/src/style.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /*
6 | * {
7 | padding: 0;
8 | margin: 0;
9 | box-sizing: border-box;
10 | }
11 | body {
12 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
13 | }
14 | nav {
15 | padding: 1rem 5rem;
16 | display: flex;
17 | justify-content: space-between;
18 | }
19 | ul {
20 | list-style: none;
21 | }
22 | ul li {
23 | display: inline-block;
24 | margin: 0 10px;
25 | }
26 | a {
27 | text-decoration: none;
28 | } */
--------------------------------------------------------------------------------
/packages/playground/src/pages/dashboard/posts.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Posts
4 |
5 |
6 |
Post title {{ n }}
7 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus aut maxime natus impedit libero quo veritatis.
8 |
View
9 |
Delete
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/playground/src/composables/index.ts:
--------------------------------------------------------------------------------
1 | import { ref, computed, Ref } from 'vue'
2 |
3 | interface User {
4 | id: number
5 | name: string
6 | }
7 |
8 | export function useAuth() {
9 | const user: Ref = ref(null)
10 | const permissions: Ref<{ permissions: []; roles: [] } | undefined> = ref(undefined)
11 | const loggedIn = computed(() => {
12 | return !!user.value
13 | })
14 |
15 | if (localStorage.getItem('user')) {
16 | const data = JSON.parse(localStorage.getItem('user') as string)
17 |
18 | user.value = data.user
19 | permissions.value = data.permissions
20 | }
21 |
22 | const reset = () => {
23 | user.value = null
24 | permissions.value = undefined
25 | }
26 |
27 | return { loggedIn, user, permissions, reset }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playground",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "plugin:dev": "pnpm run --filter=vue-middleware watch",
7 | "playground:dev": "vite",
8 | "dev": "npm-run-all -p 'plugin:dev' 'playground:dev'",
9 | "build": "vite build",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "vue": "^3.4.21",
14 | "vue-router": "^4.3.2"
15 | },
16 | "devDependencies": {
17 | "@vitejs/plugin-vue": "^5.0.4",
18 | "autoprefixer": "^10.4.19",
19 | "npm-run-all": "^4.1.5",
20 | "postcss": "^8.4.38",
21 | "tailwindcss": "^3.4.3",
22 | "typescript": "^5.2.2",
23 | "vite": "^5.2.0",
24 | "vue-tsc": "^2.0.6"
25 | },
26 | "peerDependencies": {
27 | "vue-middleware": "*"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "type": "module",
4 | "scripts": {
5 | "plugin:build": "pnpm run --filter=vue-middleware build",
6 | "plugin:build-dts": "pnpm run --filter=vue-middleware build:dts",
7 | "docs:dev": "pnpm run --filter=docs docs:dev",
8 | "playground:dev": "pnpm run --filter=playground dev"
9 | },
10 | "devDependencies": {
11 | "@rollup/plugin-typescript": "^11.1.6",
12 | "@types/node": "^20.14.2",
13 | "chalk": "^5.3.0",
14 | "fs-extra": "^11.2.0",
15 | "rimraf": "^5.0.7",
16 | "rollup": "^4.18.0",
17 | "rollup-plugin-dts": "^6.1.1",
18 | "tslib": "^2.6.3",
19 | "typescript": "^5.4.5",
20 | "vite": "^5.3.1",
21 | "vue-tsc": "^2.0.21"
22 | },
23 | "dependencies": {
24 | "vue": "^3.4.29",
25 | "vue-router": "^4.3.3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Vue middleware is a powerful Vuejs plugin for creating custom middleware, similar to what you find in Nuxt apps but with extra features for roles and permissions.
4 |
5 | It helps you manage who can access which parts of your app especially when it comes to different route-based roles or permissions using a nice driver-approach making it great with integrating other backend frameworks.
6 |
7 | For live demo please visit [https://vue-middleware-demo.vercel.app](https://vue-middleware-demo.vercel.app).
8 |
9 | For demo source code please visit [our playground](https://github.com/themustafaomar/vue-middleware/tree/main/packages/playground).
10 |
11 | Out of the box it makes it super easy to handle Laravel roles and permissions without needing to set up a bunch of stuff using zero-config driver.
12 |
--------------------------------------------------------------------------------
/packages/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "preserve",
16 |
17 | /* Linting */
18 | "strict": false,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true,
22 |
23 | "baseUrl": ".",
24 | "paths": {
25 | "@/*": ["./src/*"]
26 | }
27 | },
28 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
29 | "references": [{ "path": "./tsconfig.node.json" }]
30 | }
--------------------------------------------------------------------------------
/packages/playground/src/pages/dashboard/users.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Users
4 |
5 |
6 |
User {{ n }}
7 |
Lorem ipsum dolor sit amet consectetur adipisicing elit.libero quo veritatis.
8 |
View
9 |
Delete
10 |
11 |
12 |
13 |
14 |
15 |
22 |
--------------------------------------------------------------------------------
/packages/vue-middleware/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-middleware",
3 | "version": "1.0.0-alpha.5",
4 | "description": "Vue middleware is a powerful Vuejs plugin for creating custom middleware and manage roles and permissions easily.",
5 | "main": "dist/vue-middleware.js",
6 | "type": "module",
7 | "license": "MIT",
8 | "files": [
9 | "src",
10 | "dist",
11 | "README.md"
12 | ],
13 | "scripts": {
14 | "watch": "vue-tsc && vite build --watch --minify false"
15 | },
16 | "dependencies": {
17 | "vue": "^3.4.26",
18 | "vue-router": "^4.3.2"
19 | },
20 | "module": "./dist/vue-middleware.js",
21 | "types": "./dist/vue-middleware.d.ts",
22 | "exports": {
23 | "default": "./dist/vue-middleware.esm.js",
24 | "import": "./dist/vue-middleware.esm.js",
25 | "require": "./dist/vue-middleware.cjs",
26 | "types": {
27 | "import": "./dist/vue-middleware.d.ts"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/vue-middleware/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { App } from 'vue'
2 | import type { Router } from 'vue-router'
3 | import { type Options, handler } from './handler'
4 | import './globalDeclarations'
5 |
6 | export type { Options, MiddlewareContext } from './handler'
7 |
8 | export { Driver } from './drivers/driver'
9 | export * from './drivers'
10 | export * from './composables'
11 |
12 | declare module 'vue' {
13 | interface ComponentCustomProperties {
14 | is: (value: string) => boolean
15 | can: (value: string) => boolean
16 | }
17 | }
18 |
19 | const plugin = {
20 | install(app: App, options: Partial = {}) {
21 | const router: Router = app.config.globalProperties.$router
22 |
23 | if (!app.config.globalProperties.$router) {
24 | throw new Error(
25 | 'The vue-router is required in order to work with vue-middleware.'
26 | )
27 | }
28 |
29 | handler(app, router, options)
30 | },
31 | }
32 |
33 | export default plugin
34 |
--------------------------------------------------------------------------------
/docs/api-examples.md:
--------------------------------------------------------------------------------
1 | ---
2 | outline: deep
3 | ---
4 |
5 | # Runtime API Examples
6 |
7 | This page demonstrates usage of some of the runtime APIs provided by VitePress.
8 |
9 | The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files:
10 |
11 | ```md
12 |
17 |
18 | ## Results
19 |
20 | ### Theme Data
21 | {{ theme }}
22 |
23 | ### Page Data
24 | {{ page }}
25 |
26 | ### Page Frontmatter
27 | {{ frontmatter }}
28 | ```
29 |
30 |
35 |
36 | ## Results
37 |
38 | ### Theme Data
39 | {{ theme }}
40 |
41 | ### Page Data
42 | {{ page }}
43 |
44 | ### Page Frontmatter
45 | {{ frontmatter }}
46 |
47 | ## More
48 |
49 | Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Mustafa Omar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/vue-middleware/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Mustafa Omar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # https://vitepress.dev/reference/default-theme-home-page
3 | layout: home
4 |
5 | hero:
6 | name: "Vue Middleware"
7 | text: "Middleware has never been easier"
8 | tagline: Vue middleware is a powerful Vue.js plugin for creating middleware, similar to what you find in Nuxt apps, with extra features for roles and permissions.
9 | image:
10 | src: /vue-logo.svg
11 | alt: Vue Middleware Logo
12 | actions:
13 | - theme: brand
14 | text: Documenation
15 | link: /introduction
16 | - theme: alt
17 | text: Demo
18 | link: https://vue-middleware-demo.vercel.app
19 |
20 | features:
21 | - icon: 🛠️
22 | title: Middleware functions
23 | details: Vue middleware provides a way to register middleware functions and can be attached to routes using its name.
24 | - icon: ✈️
25 | title: Route-based roles & permissions
26 | details: It also provides route-based roles and permissions functionality for protecting your application routes.
27 | - icon: 🚀
28 | title: Interoperability
29 | details: While it supports Laravel roles and permissions out of the box, it also provides a way to support other backend frameworks using custom driver.
30 | ---
31 |
32 |
--------------------------------------------------------------------------------
/packages/vue-middleware/src/drivers/driver.ts:
--------------------------------------------------------------------------------
1 | import { type App } from 'vue'
2 | import { type RouteMeta } from 'vue-router'
3 |
4 | export abstract class Driver {
5 | _app: App
6 |
7 | constructor(app: App) {
8 | this._app = app
9 | }
10 |
11 | abstract can(): (value: string) => boolean
12 |
13 | abstract is(): (value: string) => boolean
14 |
15 | _hasntRole({ roles }: RouteMeta) {
16 | roles = this._normalize(roles as string | [])
17 |
18 | return !roles || this.is()(roles as string) ? false : true
19 | }
20 |
21 | _hasntPermissions({ permissions }: RouteMeta) {
22 | if (!permissions) {
23 | return false
24 | }
25 |
26 | permissions = this._normalize(permissions as string | [])
27 |
28 | return !permissions || this.can()(permissions as string) ? false : true
29 | }
30 |
31 | _normalize(value: string[] | string) {
32 | if (Array.isArray(value)) {
33 | return value.join('&')
34 | }
35 |
36 | if (typeof value === 'string') {
37 | return value
38 | }
39 |
40 | return ''
41 | }
42 |
43 | _lookup() {
44 | const globalProperties = this._app.config.globalProperties
45 |
46 | globalProperties.can = this.can()
47 | globalProperties.is = this.is()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/docs/page-title.md:
--------------------------------------------------------------------------------
1 | # Page Title
2 |
3 | While navigating each route vue-middleware generates a page title depending on the route name if presented, with the ability to customize the title format perfectly.
4 |
5 | ## Enabling Page Title
6 |
7 | Actually, the page title is enabled by default and you can disable it with the following:
8 |
9 | ```ts
10 | app.use(vueMiddleware, {
11 | pageTitle: false,
12 | }
13 | ```
14 |
15 | ## Customization
16 |
17 | By default the page title is enabled and replacing each `-` or `_` with a space, that means if you have a route named `dashboard_users` it becomes Dashboard - users, to customize this behavior take a look at the following snippet.
18 |
19 | ```ts
20 | import { type RouteMeta } from 'vue-router'
21 |
22 | app.use(vueMiddleware, {
23 | pageTitle: {
24 | template: (name: string, meta: RouteMeta) => {
25 | // Transform you name, you're also have access to
26 | // meta property if you need to define your own title from route meta.
27 |
28 | let title: string[] | string = name.replace(/_|-/g, ' ').split(' ')
29 |
30 | title = title[title.length - 1]
31 |
32 | // dashboard_users -> Users
33 | // dashboard-posts -> Posts etc..
34 |
35 | return `${title.charAt(0).toUpperCase()}${title.slice(1)}`
36 | }
37 | },
38 | })
39 | ```
40 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hi admin!
6 |
7 |
8 |
9 |
10 |
11 |
Anyone can see this
12 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus aut maxime natus impedit libero quo veritatis.
13 |
14 |
15 |
16 |
Anyone can see this
17 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus aut maxime natus impedit libero quo veritatis.
18 |
19 |
20 |
21 |
Admin only can see this.
22 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus aut maxime natus impedit libero quo veritatis.
23 |
24 |
25 |
26 |
Admin only can see this.
27 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellendus aut maxime natus impedit libero quo veritatis.
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/packages/vue-middleware/src/drivers/laravel.ts:
--------------------------------------------------------------------------------
1 | import { type App } from 'vue'
2 | import { Driver } from './driver'
3 |
4 | export class LaravelPermissionsDriver extends Driver {
5 | constructor(app: App) {
6 | super(app)
7 | }
8 |
9 | can(): (value: string) => boolean {
10 | return (value: string) => {
11 | const permissions = window.Laravel?.permissions
12 |
13 | if (!permissions || !Array.isArray(permissions)) {
14 | return false
15 | }
16 |
17 | if (value.includes('|')) {
18 | return value
19 | .split('|')
20 | .some((permission) => permissions.includes(permission.trim()))
21 | } else if (value.includes('&')) {
22 | return value
23 | .split('&')
24 | .every((permission) => !permissions.includes(permission.trim()))
25 | } else {
26 | return permissions.includes(value.trim())
27 | }
28 | }
29 | }
30 |
31 | is(): (value: string) => boolean {
32 | return (value: string) => {
33 | const roles = window.Laravel?.roles
34 |
35 | if (!roles || !Array.isArray(roles)) {
36 | return false
37 | }
38 |
39 | if (value.includes('|')) {
40 | return value.split('|').some((item) => roles.includes(item.trim()))
41 | } else if (value.includes('&')) {
42 | return value.split('&').every((role) => !roles.includes(role.trim()))
43 | } else {
44 | return roles.includes(value.trim())
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/can-and-is.md:
--------------------------------------------------------------------------------
1 | # can() And is()
2 |
3 | In this section we're gonna be discussing how to use can and is utilities in your application.
4 |
5 | ## is()
6 |
7 | The is() utility used to check for the roles of the current authenticated user.
8 |
9 | ```vue
10 |
11 |
21 | ```
22 |
23 | ## can()
24 |
25 | The can() utility used to check for the current authenticated user's permissions.
26 |
27 | ```vue
28 |
29 |
39 | ```
40 |
41 | ## Using In Script Setup
42 |
43 | The is() and can() utilities are available in script setup as well using usePermissions() composable.
44 |
45 | ```vue
46 |
59 | ```
60 |
--------------------------------------------------------------------------------
/packages/playground/src/pages/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Login
5 |
6 |
7 | Login with Admin (full permissions)
8 |
9 |
10 |
11 |
12 |
13 | Login with moderator (less permissions)
14 |
15 |
16 |
17 |
18 |
19 |
46 |
--------------------------------------------------------------------------------
/packages/playground/src/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dashboard
7 |
8 |
9 | Profile
10 |
11 |
12 | Users
13 |
14 |
15 | Posts
16 |
17 |
18 |
19 |
20 | Login to view dashboard links...
21 |
22 |
23 |
24 | Login |
25 | Register
26 |
27 |
28 |
29 | Hi, {{ user?.name }} |
30 | ↓ Logout
31 |
32 |
33 |
34 |
35 |
36 |
50 |
--------------------------------------------------------------------------------
/packages/playground/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h, toRaw, unref } from 'vue'
2 | import vueMiddleware, { LaravelPermissionsDriver } from 'vue-middleware'
3 | import { useAuth } from './composables'
4 | import middleware from './middleware'
5 | import './style.css'
6 | import router from './router'
7 | import App from './App.vue'
8 | import { RouteLocationNormalized, RouteMeta } from 'vue-router'
9 |
10 | const app = createApp({
11 | render: () => h(App),
12 | beforeCreate: () => {
13 | window.Laravel = toRaw(unref(useAuth().permissions))
14 | },
15 | })
16 |
17 | app.use(router)
18 | app.use(vueMiddleware, {
19 | middleware,
20 | permissions: {
21 | driver: LaravelPermissionsDriver,
22 | },
23 | hooks: {
24 | onBeforeEach: (
25 | to: RouteLocationNormalized,
26 | from: RouteLocationNormalized
27 | ): void => {
28 | //
29 | },
30 | onAfterEach: (
31 | to: RouteLocationNormalized,
32 | from: RouteLocationNormalized
33 | ): void => {
34 | //
35 | },
36 | },
37 | pageTitle: {
38 | template: (name: string, meta: RouteMeta) => {
39 | // Transform you name, you're also have access to
40 | // meta property if you need to define your own title from route meta.
41 | // console.log(name, meta)
42 |
43 | let title: string[] | string = name.replace(/_|-/g, ' ').split(' ')
44 |
45 | title = title[title.length - 1]
46 |
47 | // dashboard_users -> Users, dashboard-posts -> Posts etc..
48 |
49 | return `${title.charAt(0).toUpperCase()}${title.slice(1)}`
50 | }
51 | },
52 | })
53 | app.mount('#app')
54 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.mts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitepress'
2 |
3 | // https://vitepress.dev/reference/site-config
4 | export default defineConfig({
5 | title: "Vue Middleware",
6 | description: "Vue middleware is a powerful Vuejs plugin for creating custom middleware, similar to what you find in Nuxt apps but with extra features for roles and permissions.",
7 | themeConfig: {
8 | siteTitle: 'Vue Middleware',
9 | // https://vitepress.dev/reference/default-theme-config
10 | nav: [
11 | { text: 'Home', link: '/' },
12 | { text: 'Demo', link: 'https://vue-middleware-demo.vercel.app' }
13 | ],
14 |
15 | sidebar: [
16 | {
17 | text: 'Getting Started',
18 | items: [
19 | { text: 'Introduction', link: '/introduction' },
20 | { text: 'Quick usage', link: '/quick-usage' },
21 | { text: 'Installation', link: '/installation' },
22 | ]
23 | },
24 | {
25 | text: 'Middleware',
26 | items: [
27 | { text: 'Basic middleware', link: '/basics' },
28 | { text: 'Page title', link: '/page-title' },
29 | ]
30 | },
31 | {
32 | text: 'Roles & Permissions',
33 | items: [
34 | { text: 'Basics', link: '/roles-and-permissions' },
35 | { text: 'can() and is() Utilities', link: '/can-and-is' },
36 | { text: 'Creating Drivers', link: '/custom-drivers' },
37 | ]
38 | },
39 | {
40 | text: 'Links',
41 | items: [
42 | { text: 'Troubleshooting', link: 'https://github.com/themustafaomar/vue-middleware/issues/new', target: '_blank' },
43 | ]
44 | }
45 | ],
46 |
47 | socialLinks: [
48 | { icon: 'github', link: 'https://github.com/themustafaomar/vue-middleware' }
49 | ]
50 | }
51 | })
52 |
--------------------------------------------------------------------------------
/docs/custom-drivers.md:
--------------------------------------------------------------------------------
1 | # Creating Custom Drivers
2 |
3 | For some reasons you may want to write your own is and can, vue middleware makes it easy to create your custom driver.
4 |
5 | ## Getting Started
6 |
7 | Let's create our custom driver that handles roles and permissions for us, feel free to add your can and is logic inside the can and is methods.
8 |
9 | In the following example that's the default Laravel driver.
10 |
11 | The can and is become our global utilties you're using everywhere.
12 |
13 | ```ts
14 | import type { App } from 'vue'
15 | import { Driver } from 'vue-middleware'
16 |
17 | export class MyPermissionsDriver extends Driver {
18 | constructor(app: App) {
19 | super(app)
20 | }
21 |
22 | can(): (value: string) => boolean {
23 | return (value: string) => {
24 | const permissions = window.Laravel?.permissions
25 |
26 | if (!permissions || !Array.isArray(permissions)) {
27 | return false
28 | }
29 |
30 | if (value.includes('|')) {
31 | return value
32 | .split('|')
33 | .some((permission) => permissions.includes(permission.trim()))
34 | } else if (value.includes('&')) {
35 | return value
36 | .split('&')
37 | .every((permission) => !permissions.includes(permission.trim()))
38 | } else {
39 | return permissions.includes(value.trim())
40 | }
41 | }
42 | }
43 |
44 | is(): (value: string) => boolean {
45 | return (value: string) => {
46 | const roles = window.Laravel?.roles
47 |
48 | if (!roles || !Array.isArray(roles)) {
49 | return false
50 | }
51 |
52 | if (value.includes('|')) {
53 | return value.split('|').some((item) => roles.includes(item.trim()))
54 | } else if (value.includes('&')) {
55 | return value.split('&').every((role) => !roles.includes(role.trim()))
56 | } else {
57 | return roles.includes(value.trim())
58 | }
59 | }
60 | }
61 | }
62 |
63 | ```
--------------------------------------------------------------------------------
/packages/playground/src/router/routes.ts:
--------------------------------------------------------------------------------
1 | export default [
2 | // Public routes
3 | {
4 | name: 'home',
5 | path: '/',
6 | component: () => import('@/layouts/default.vue'),
7 | children: [
8 | {
9 | name: 'home',
10 | path: '',
11 | component: () => import('@/pages/index.vue'),
12 | },
13 | {
14 | name: 'about',
15 | path: 'about',
16 | component: () => import('@/pages/about.vue'),
17 | },
18 | {
19 | name: 'contact',
20 | path: 'contact',
21 | component: () => import('@/pages/contact.vue'),
22 | },
23 | ]
24 | },
25 | // Authentication routes
26 | {
27 | name: 'auth',
28 | path: '/auth',
29 | component: () => import('@/layouts/auth.vue'),
30 | redirect: '/auth/login',
31 | meta: {
32 | middleware: 'dashboard:guest',
33 | },
34 | children: [
35 | {
36 | name: 'login',
37 | path: 'login',
38 | component: () => import('@/pages/login.vue'),
39 | },
40 | {
41 | name: 'register',
42 | path: 'register',
43 | component: () => import('@/pages/register.vue'),
44 | },
45 | ],
46 | },
47 | // Dashboard routes
48 | {
49 | name: 'dashboard',
50 | path: '/dashboard',
51 | component: () => import('@/layouts/dashboard.vue'),
52 | meta: {
53 | middleware: ['dashboard'],
54 | fallbackTo: '/auth/login',
55 | },
56 | children: [
57 | {
58 | name: 'dashboard_home',
59 | path: '',
60 | component: () => import('@/pages/dashboard/index.vue'),
61 | },
62 | {
63 | name: 'dashboard_users',
64 | path: 'users',
65 | component: () => import('@/pages/dashboard/users.vue'),
66 | meta: {
67 | middleware: 'logger',
68 | },
69 | },
70 | {
71 | name: 'dashboard_posts',
72 | path: 'posts',
73 | component: () => import('@/pages/dashboard/posts.vue'),
74 | },
75 | {
76 | name: 'dashboard_profile',
77 | path: 'profile',
78 | component: () => import('@/pages/dashboard/profile.vue'),
79 | },
80 | ],
81 | },
82 | ]
83 |
--------------------------------------------------------------------------------
/packages/vue-middleware/README.md:
--------------------------------------------------------------------------------
1 | # vue-middleware
2 |
3 | vue-middleware is a powerful Vuejs plugin for creating custom middleware, similar to what you find in Nuxt apps but with extra features for roles and permissions.
4 |
5 | It helps you manage who can access which parts of your app especially when it comes to different route-based roles or permissions using a nice driver-approach making it great with integrating other backend frameworks, for more information please visit the offical [documentation](https://vue-middleware-docs.vercel.app).
6 |
7 | Out of the box it makes it super easy to handle Laravel roles and permissions without needing to set up a bunch of stuff using zero-config driver.
8 |
9 | ## Installation
10 |
11 | Follow these steps to quickly install `vue-middleware` into your project, in this example we're using npm.
12 |
13 | ```
14 | npm i vue-middleware
15 | ```
16 |
17 | ## Quick Usage
18 |
19 | Let's create a simple middleware for protecting our dashboard.
20 |
21 | In `main.ts` we're goning to register our first `dashboard` middleware the function receives all the parameters you might think of in the authentication process.
22 |
23 | ```ts
24 | import { createApp, App } from 'vue'
25 | import { vueMiddleware, MiddlewareContext } from 'vue-middleware'
26 | import App from './App.vue'
27 |
28 | const app: App = createApp(App)
29 |
30 | app.use(vueMiddleware, {
31 | middleware: {
32 | dashboard: ({ app, router, from, to, redirect, abort, guard }: MiddlewareContext) => {
33 | //
34 | },
35 | },
36 | })
37 |
38 | app.mount('#app')
39 | ```
40 |
41 | In vue-router routes we need to attach this middleware name in a route.
42 |
43 | ```ts
44 | export const routes = [
45 | {
46 | name: 'dashboard',
47 | path: '/dashboard',
48 | component: () => import('@/layouts/dashboard.vue'),
49 | meta: {
50 | middleware: 'dashboard', // This dashboard and its children are now guarded using the dashboard middleware
51 | },
52 | children: [
53 | {
54 | name: 'dashboard_home',
55 | path: '',
56 | component: () => import('@/pages/dashboard/index.vue'),
57 | },
58 | {
59 | name: 'dashboard_users',
60 | path: 'users',
61 | component: () => import('@/pages/dashboard/users.vue'),
62 | },
63 | ]
64 | }
65 | ]
66 | ```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-middleware
2 |
3 | vue-middleware is a powerful Vuejs plugin for creating custom middleware, similar to what you find in Nuxt apps but with extra features for roles and permissions..
4 |
5 | It helps you manage who can access which parts of your app especially when it comes to different route-based roles or permissions using a nice driver-approach making it great with integrating other backend frameworks, for more information please visit the offical [documentation](https://vue-middleware-docs.vercel.app).
6 |
7 | Out of the box it makes it super easy to handle Laravel roles and permissions without needing to set up a bunch of stuff using zero-config driver.
8 |
9 | ## Demo
10 |
11 | For live demo please visit [https://vue-middleware-demo.vercel.app](https://vue-middleware-demo.vercel.app).
12 |
13 | For demo source code please visit the [playground](https://github.com/themustafaomar/vue-middleware/tree/main/packages/playground).
14 |
15 | ## Installation
16 |
17 | Follow these steps to quickly install `vue-middleware` into your project, in this example we're using npm.
18 |
19 | ```
20 | npm i vue-middleware
21 | ```
22 |
23 | ## Quick Usage
24 |
25 | Let's create a simple middleware for protecting our dashboard.
26 |
27 | In `main.ts` we're goning to register our first `dashboard` middleware the function receives all the parameters you might think of in the authentication process.
28 |
29 | ```ts
30 | import { createApp, App } from 'vue'
31 | import vueMiddleware, { type MiddlewareContext } from 'vue-middleware'
32 | import App from './App.vue'
33 |
34 | const app: App = createApp(App)
35 |
36 | app.use(vueMiddleware, {
37 | middleware: {
38 | dashboard: ({ app, router, from, to, redirect, abort, guard }: MiddlewareContext) => {
39 | //
40 | },
41 | },
42 | })
43 |
44 | app.mount('#app')
45 | ```
46 |
47 | In vue-router routes we need to attach this middleware name in a route.
48 |
49 | ```ts
50 | export const routes = [
51 | {
52 | name: 'dashboard',
53 | path: '/dashboard',
54 | component: () => import('@/layouts/dashboard.vue'),
55 | meta: {
56 | middleware: 'dashboard', // This dashboard and its children are now guarded using the dashboard middleware
57 | },
58 | children: [
59 | {
60 | name: 'dashboard_home',
61 | path: '',
62 | component: () => import('@/pages/dashboard/index.vue'),
63 | },
64 | {
65 | name: 'dashboard_users',
66 | path: 'users',
67 | component: () => import('@/pages/dashboard/users.vue'),
68 | },
69 | ]
70 | }
71 | ]
72 | ```
73 |
74 | ## License
75 |
76 | vue-middleware is released under the MIT License.
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process'
2 | import { rimraf } from 'rimraf'
3 | import { rollup } from 'rollup'
4 | import fs from 'fs-extra'
5 | import chalk from 'chalk'
6 | import path from 'path'
7 | import typescript from '@rollup/plugin-typescript'
8 | import dts from 'rollup-plugin-dts'
9 |
10 | const packageDir = path.resolve('packages/vue-middleware')
11 | const entryFilePath = path.resolve(packageDir, 'src/index.ts')
12 | const outputDir = path.resolve(packageDir, 'dist')
13 | const tsconfigPath = path.resolve(packageDir, 'tsconfig.json')
14 |
15 | function generateConfig(format) {
16 | const extension = format === 'es'
17 | ? 'esm.js'
18 | : format === 'umd'
19 | ? 'js'
20 | : 'cjs'
21 |
22 | return {
23 | input: entryFilePath,
24 | external: ['vue', 'vue-router'],
25 | plugins: [
26 | typescript({
27 | tsconfig: tsconfigPath,
28 | declarationDir: outputDir,
29 | declaration: true,
30 | allowImportingTsExtensions: false,
31 | sourceMap: false,
32 | }),
33 | ],
34 | output: {
35 | format,
36 | name: format === 'umd' ? 'vue-middleware' : undefined,
37 | exports: format === 'umd' || format === 'cjs' ? 'named' : undefined,
38 | globals: {
39 | vue: 'Vue',
40 | 'vue-router': 'VueRouter',
41 | },
42 | },
43 | fileName: `vue-middleware.${extension}`,
44 | }
45 | }
46 |
47 | async function build() {
48 | rimraf.sync(outputDir)
49 |
50 | console.log(chalk.blackBright('⏳ Building library ./dist\n'))
51 |
52 | const formats = ['umd', 'es', 'cjs']
53 |
54 | for (const format of formats) {
55 | const { output, fileName, ...rest } = await generateConfig(format)
56 | const bundle = await rollup(rest)
57 | const { output: [{ code }] } = await bundle.generate(output)
58 |
59 | fs.outputFileSync(
60 | path.join(outputDir, fileName), code
61 | )
62 | console.log(chalk.cyan(`-> Generated library file ./dist/${fileName}`))
63 | }
64 |
65 | console.log(chalk.blackBright('\n⏳ Generating declarations...'))
66 |
67 | execSync(`tsc --project ${tsconfigPath}`, {
68 | stdio: 'inherit',
69 | })
70 |
71 | const dtsBundle = await rollup({
72 | input: path.resolve(outputDir, 'types/index.d.ts'),
73 | plugins: [dts()],
74 | })
75 |
76 | const { output: [{ code }] } = await dtsBundle.generate({
77 | format: 'es',
78 | })
79 |
80 | fs.outputFileSync(path.join(outputDir, 'vue-middleware.d.ts'), code)
81 |
82 | console.log(chalk.cyan('\n-> Declarations generated: vue-middleware.d.ts'))
83 |
84 | rimraf.sync(path.resolve(outputDir, 'types'))
85 | }
86 |
87 | build().catch((error) => {
88 | console.error(error)
89 | process.exit(1)
90 | })
91 |
--------------------------------------------------------------------------------
/docs/quick-usage.md:
--------------------------------------------------------------------------------
1 | # Quick usage
2 |
3 | Let's create a simple middleware for protecting our dashboard.
4 |
5 | In `main.ts` we're goning to register our first `dashboard` middleware the function receives all the parameters you might think of in the authentication process.
6 |
7 | ::: warning
8 | Notice that vue-middleware requires vue-router in order to work.
9 | :::
10 |
11 | ```ts
12 | import { createApp, type App } from 'vue'
13 | import vueMiddleware, { type MiddlewareContext } from 'vue-middleware'
14 | import App from './App.vue'
15 |
16 | const app: App = createApp(App)
17 |
18 | app.use(vueMiddleware, {
19 | middleware: {
20 | dashboard: ({ app, router, from, to, redirect, abort, guard }: MiddlewareContext) => {
21 | //
22 | },
23 | },
24 | })
25 |
26 | app.mount('#app')
27 | ```
28 |
29 | In routes file we need to attach this middleware name in a route or most likely a route with its children, e.g dashboard.
30 |
31 | ```ts
32 | export const routes = [
33 | {
34 | name: 'dashboard',
35 | path: '/dashboard',
36 | component: () => import('@/layouts/dashboard.vue'),
37 | meta: {
38 | // This dashboard and its children are now protected using the dashboard middleware
39 | middleware: 'dashboard',
40 | },
41 | children: [
42 | {
43 | name: 'dashboard_home',
44 | path: '',
45 | component: () => import('@/pages/dashboard/index.vue'),
46 | },
47 | {
48 | name: 'dashboard_users',
49 | path: 'users',
50 | component: () => import('@/pages/dashboard/users.vue'),
51 | },
52 | ]
53 | }
54 | ]
55 | ```
56 |
57 |
142 |
--------------------------------------------------------------------------------
/docs/roles-and-permissions.md:
--------------------------------------------------------------------------------
1 | # Roles And Permissions
2 |
3 | In this section we're gonna be discussing how to add roles and permissions into your application.
4 |
5 | ## Getting Started
6 |
7 | Roles and permissions as we've already mentioned is a route-based, in order to work we have to store the roles and permissions somehow when a user logged in.
8 |
9 | Typically, when a user logged in we add the user, roles and permissions in the localStorage, vue-middleware requires the roles and permissions to be added to the window object as well, when using `LaravelPermissionsDriver` driver, the example below we'll assume we're using Laravel.
10 |
11 | ::: info
12 | But refreshing the page would waste these values, right? that's why you need to populate the window each time a refresh is made.
13 | :::
14 |
15 | ```ts
16 | ...
17 |
18 | const login = () => {
19 | const { data } = axios.post('/login', ...)
20 |
21 | localStorage.set('user', JSON.stringify({
22 | // The user..
23 | user: data.user,
24 |
25 | // An array of permissions ['view posts', 'create posts', 'edit posts', ...]
26 | permissions: data.permissions,
27 |
28 | // An array of roles ['admin']
29 | roles: data.roles,
30 | }))
31 |
32 | window.Laravel = data // A copy for the window.
33 | }
34 | ```
35 |
36 | **Tip**: in order to populate the roles and permissions in the window, beforeCreate hook is a perfect place to do so, for complete use-case senario please visit the [playground](https://github.com/themustafaomar/vue-middleware/tree/main/packages/playground) project.
37 |
38 | ```ts
39 | const app = createApp({
40 | ...
41 | beforeCreate: () => {
42 | const { roles, permissions } = JSON.parse(localStorage.get('user'))
43 |
44 | window.Laravel = {
45 | roles, // ['admin', ...]
46 | permissions, // ['view users', 'create users', 'edit users', 'delete users', ...]
47 | }
48 | },
49 | ...
50 | })
51 | ```
52 |
53 | ## Registering A Driver
54 |
55 | In this example we're gonna be using the `LaravelPermissionsDriver` driver.
56 |
57 | ```diff
58 | + import { LaravelPermissionsDriver } from 'vue-middleware'
59 |
60 | app.use(vueMiddleware, {
61 | middlewares,
62 | + permissions: {
63 | + driver: LaravelPermissionsDriver,
64 | + },
65 | })
66 | ```
67 |
68 | ## Attaching Permissions
69 |
70 | Once the driver is installed now we we're ready to add roles and permissions per route.
71 |
72 | ```ts
73 | {
74 | name: 'dashboard_users',
75 | path: 'users',
76 | component: () => import('@/pages/dashboard/users.vue'),
77 | meta: {
78 | // All permissions in the array must be granted in order to enter.
79 | permissions: ['view users'],
80 |
81 | // Using the string syntax.
82 | permissions: 'view users',
83 |
84 | // Any permissions
85 | permissions: 'view any | view users',
86 |
87 | // All permissions have to be granted,
88 | permissions: 'view dashboard & view users',
89 | },
90 | },
91 | ```
92 |
93 | ## Attaching Roles
94 |
95 | Attaching roles is is the same as attaching permissions.
96 |
97 | ```ts
98 | {
99 | name: 'dashboard_users',
100 | path: 'users',
101 | component: () => import('@/pages/dashboard/users.vue'),
102 | meta: {
103 | // All permissions in the array must be granted in order to enter.
104 | roles: ['admin'],
105 |
106 | // Using the string syntax.
107 | roles: 'admin',
108 |
109 | // Any roles
110 | roles: 'admin | moderator',
111 |
112 | // All roles have to be granted,
113 | roles: 'moderator & editor',
114 | },
115 | },
116 | ```
--------------------------------------------------------------------------------
/packages/vue-middleware/src/handler.ts:
--------------------------------------------------------------------------------
1 | import type { App } from 'vue'
2 | import type {
3 | Router,
4 | RouteLocationNormalized,
5 | RouteMeta,
6 | RouteLocationRaw,
7 | } from 'vue-router'
8 | import { Driver } from './drivers/driver'
9 |
10 | export interface MiddlewareContext {
11 | app: App
12 | router: Router
13 | from: RouteLocationNormalized
14 | to: RouteLocationNormalized
15 | redirect: (to: RouteLocationRaw) => void
16 | abort: () => Symbol | boolean
17 | guard?: string | undefined
18 | }
19 |
20 | export interface Middleware {
21 | [key: string]: (ctx: MiddlewareContext) => void
22 | }
23 |
24 | type MiddlewareName = [string, string | undefined]
25 |
26 | export interface Options {
27 | middleware: Middleware
28 | pageTitle: {
29 | template: (name: string, meta: RouteMeta) => string
30 | } | boolean
31 | permissions: {
32 | driver: new (app: App) => Driver
33 | }
34 | hooks: {
35 | onBeforeEach?: (to: RouteLocationNormalized, from: RouteLocationNormalized) => void
36 | onAfterEach?: (to: RouteLocationNormalized, from: RouteLocationNormalized) => void
37 | }
38 | }
39 |
40 | const ABORT_KEY = Symbol('VMAbort')
41 | const abort = () => ABORT_KEY
42 |
43 | export function handler(
44 | app: App,
45 | router: Router,
46 | options: Partial
47 | ) {
48 | const {
49 | pageTitle,
50 | middleware = {},
51 | permissions,
52 | hooks,
53 | } = options
54 |
55 | let permissionsDriver = null
56 | if (permissions?.driver) {
57 | permissionsDriver = new permissions.driver(app)
58 | if (!(permissionsDriver instanceof Driver)) {
59 | throw new Error(
60 | "The driver is not compatible with our base driver are you sure your're extending the base driver."
61 | )
62 | }
63 | permissionsDriver._lookup()
64 | }
65 |
66 | router.beforeEach((to, from, next) => {
67 | if (hooks?.onBeforeEach) {
68 | hooks.onBeforeEach(to, from)
69 | }
70 |
71 | const { name, matched, meta } = to
72 |
73 | // Add a title and id to the current page
74 | if (pageTitle !== false && name) {
75 | document.title = typeof pageTitle === 'object'
76 | ? pageTitle.template(String(name), meta)
77 | : createTitle(String(name))
78 | }
79 |
80 | // Handle the role and permissions if any
81 | if (permissionsDriver) {
82 | const fallbackTo = meta.fallbackTo as string | undefined || ''
83 | if (permissionsDriver._hasntRole(meta)) {
84 | return next({
85 | path: fallbackTo,
86 | })
87 | } else if (permissionsDriver._hasntPermissions(meta)) {
88 | return next({
89 | path: fallbackTo,
90 | })
91 | }
92 | }
93 |
94 | const redirect = (to: RouteLocationRaw) => {
95 | router.push(to)
96 | }
97 |
98 | const ctx: MiddlewareContext = {
99 | app,
100 | router,
101 | from,
102 | to,
103 | redirect,
104 | abort,
105 | }
106 |
107 | // Well, looks like we don't have any middleware to run, so we can call
108 | // next now, otherwise we will ensure that each middleware doesn't return
109 | // failure by returning explicit `false` or `ABORT_KEY`..
110 | const middlewaresToRun = getMiddlewares(
111 | matched.map(match => match.meta)
112 | )
113 | if (!middlewaresToRun.length) {
114 | next()
115 | } else {
116 | const result: boolean[] | Symbol[] | unknown[] = middlewaresToRun.map((middlewareName) => {
117 | return runMiddleware(middleware, middlewareName, ctx)
118 | })
119 | if (result.some(
120 | (value: unknown) => value === false || value == ABORT_KEY
121 | )) {
122 | return next(false)
123 | }
124 | next()
125 | }
126 | })
127 |
128 | router.afterEach((to, from) => {
129 | if (hooks?.onAfterEach) {
130 | hooks.onAfterEach(to, from)
131 | }
132 | })
133 | }
134 |
135 | // Create a title from a given name
136 | function createTitle(name: string) {
137 | return (name.charAt(0).toUpperCase() + name.slice(1)).replace(/_|-/gi, ' - ')
138 | }
139 |
140 | // Let's run the middleware and give the
141 | // middleware a bunch of parameters to play around with.
142 | function runMiddleware(
143 | middlewares: Middleware,
144 | name: string,
145 | ctx: MiddlewareContext
146 | ) {
147 | const [middleware, guard]: MiddlewareName = name.split(':') as MiddlewareName
148 |
149 | if (!Array.prototype.hasOwnProperty.call(middlewares, middleware)) {
150 | throw new Error(
151 | `Unknown [${middleware}] middleware, did you register this middleware?`
152 | )
153 | }
154 |
155 | return middlewares[middleware]({
156 | ...ctx,
157 | guard,
158 | })
159 | }
160 |
161 | function getMiddlewares(middlewares: RouteMeta[]) {
162 | return middlewares.reduce((middlewares: string[], meta: RouteMeta) => {
163 | if (meta.excludeMiddleware) {
164 | const excludes = Array.isArray(meta.excludeMiddleware)
165 | ? meta.excludeMiddleware
166 | : [meta.excludeMiddleware]
167 | middlewares = middlewares.filter(name => !excludes.includes(name))
168 | }
169 |
170 | if (Array.isArray(meta.middleware)) {
171 | middlewares.push(...meta.middleware as [])
172 | } else if (typeof meta.middleware === 'string') {
173 | middlewares.push(meta.middleware)
174 | }
175 |
176 | return middlewares
177 | }, [])
178 | }
179 |
--------------------------------------------------------------------------------
/docs/basics.md:
--------------------------------------------------------------------------------
1 | # Basic Middleware
2 |
3 | As we mentioned earlier that we can create middlewares inlined so to say, each middleware function is receiving a context parameters you may need it.
4 |
5 | ```ts
6 | app.use(vueMiddleware, {
7 | middleware: {
8 | dashboard({
9 | app,
10 | router,
11 | from,
12 | to,
13 | redirect,
14 | abort,
15 | guard
16 | }) {
17 | //
18 | },
19 | },
20 | })
21 | ```
22 |
23 | ## Putting It Where It Belongs
24 |
25 | It's prefered to create a middleware directory at the root of your project `@/middleware`, and create an index.ts file that exports all your middlewares, in `index.ts`.
26 |
27 | ```ts
28 | import dashboard from './dashboard'
29 |
30 | export {
31 | dashboard,
32 | // More middlewares..
33 | }
34 | ```
35 |
36 | ### Creating Middleware File
37 |
38 | In the middleware module file we will define our middleware, at the end it's probably will be something like that for protecting a dashboard.
39 |
40 | ```ts
41 | import { type MiddlewareContext } from 'vue-middleware'
42 |
43 | export default ({ redirect, guard }: MiddlewareContext) => {
44 | // Assuming this is a Pinia store..
45 | const { loggedIn } = storeToRefs(useAuthStore())
46 |
47 | // The user is logged in and trying to
48 | // access auth page e.g: login and register.
49 | if (loggedIn.value && guard) {
50 | redirect('/dashboard')
51 | }
52 |
53 | // The user is not logged in and it's not entering
54 | // an auth page such as login or register.
55 | if (!loggedIn.value && !guard) {
56 | redirect('/auth/login')
57 | }
58 |
59 | //
60 | }
61 | ```
62 |
63 | ## Attaching Middleware
64 |
65 | Now we've done registering our first middleware, it's time to attach this middleware somewehre on a bunch of routes or a particular route.
66 |
67 | In routes file we can attach the middleware in the meta property with middleware property with string or array syntax, notice that we can run multiple middlewares together by order.
68 |
69 | ```ts
70 | {
71 | name: 'dashboard',
72 | path: '/dashboard',
73 | component: () => import('@/layouts/dashboard.vue'),
74 | meta: {
75 | middleware: 'dashboard',
76 | // or using array syntax..
77 | middleware: ['dashboard', 'logger'], // use extra middleware in sub routes..
78 | },
79 | }
80 | ```
81 |
82 | ## Guard
83 |
84 | When you create your authentication system, likely you will need to check if the user is trying to access login, register pages.. when it's already logged in
85 |
86 | In this senario the guard comes in to play, notice how we're going to use the same middleware but this time with guest guard to identity the guest pages, take a look the [earlier example](#creating-middleware-file) to get it into your mind.
87 |
88 | Feel free to add new value as a guard and you will receive this value in the middleware, by default if you don't pass a guard value it's be undefined.
89 |
90 | ```ts
91 | {
92 | name: 'login',
93 | path: 'login',
94 | component: () => import('@/pages/login.vue'),
95 | meta: {
96 | middleware: 'dashboard:guest',
97 | },
98 | },
99 | ```
100 |
101 | ## Redirect
102 |
103 | When a middleware runs you may use router.push function to redirect an unauthenticated user or for other reasons somewhere, or using the redirect utility to do the same.
104 |
105 | Notice that you don't need to call the next navigation guard to move the next route, for aborting the navigation take a look at the next section.
106 |
107 | ```ts
108 | export default ({ redirect }) => {
109 | const store = useAuthStore()
110 |
111 | if (!store.loggedIn) {
112 | redirect('/login')
113 | }
114 |
115 | // ...
116 | }
117 | ```
118 |
119 | ## Aborting Navigation
120 |
121 | There are two ways to abort a navigation by whether using the utility function abort or returning false explicitly.
122 |
123 | ```ts
124 | export default ({ abort })) => {
125 | if (forSomeReasonWillAbort()) {
126 | return abort()
127 | }
128 |
129 | // Using `false` explicitly.
130 | if (forSomeReasonWillAbort()) {
131 | return false
132 | }
133 | }
134 | ```
135 |
136 | ## Advanced
137 |
138 | Sometimes you may want to apply multiple middlewares in the parent layout but some reasons you want to exclude this middleware from a child page, you may achieve this with excludeMiddleware property, actaully you can apply another middleware children.
139 |
140 | ```ts
141 | {
142 | name: 'dashboard',
143 | path: '/dashboard',
144 | component: () => import('@/layouts/dashboard.vue'),
145 | meta: {
146 | middleware: ['dashboard', 'reporter'],
147 | },
148 | children: [
149 | {
150 | name: 'dashboard_home',
151 | path: '',
152 | component: () => import('@/pages/dashboard/index.vue'),
153 | },
154 | {
155 | // Final middleware list of this route would be: ['dashboard']
156 | name: 'dashboard_users',
157 | path: 'users',
158 | component: () => import('@/pages/dashboard/users.vue'),
159 | meta: {
160 | excludeMiddleware: 'reporter',
161 | },
162 | },
163 | {
164 | // Final middleware list of this route would be: ['dashboard', 'reporter', 'logger']
165 | name: 'dashboard_posts',
166 | path: 'posts',
167 | component: () => import('@/pages/dashboard/posts.vue'),
168 | meta: {
169 | middleware: 'logger',
170 | },
171 | },
172 | ]
173 | }
174 | ```
175 |
176 | ## Context
177 |
178 | | Function | Description | Type |
179 | | ------------| ------------- | --------- |
180 | | `app` | The app instance | `App` |
181 | | `router` | The router instance | `Router` |
182 | | `from` | The current route location | `RouteLocationNormalized` |
183 | | `to` | The target route location | `RouteLocationNormalized` |
184 | | `redirect` | Redirect to location | `(to: RouteLocationRaw) => void` |
185 | | `abort` | Abort the navigation | `() => Symbol \| boolean` |
186 | | `guard` | The guard identifier | `boolean` |
--------------------------------------------------------------------------------
/docs/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | vitepress:
9 | specifier: ^1.1.3
10 | version: 1.1.3(@algolia/client-search@4.23.3)(search-insights@2.13.0)
11 |
12 | packages:
13 |
14 | /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0):
15 | resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==}
16 | dependencies:
17 | '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0)
18 | '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)
19 | transitivePeerDependencies:
20 | - '@algolia/client-search'
21 | - algoliasearch
22 | - search-insights
23 | dev: false
24 |
25 | /@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0):
26 | resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==}
27 | peerDependencies:
28 | search-insights: '>= 1 < 3'
29 | dependencies:
30 | '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)
31 | search-insights: 2.13.0
32 | transitivePeerDependencies:
33 | - '@algolia/client-search'
34 | - algoliasearch
35 | dev: false
36 |
37 | /@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3):
38 | resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==}
39 | peerDependencies:
40 | '@algolia/client-search': '>= 4.9.1 < 6'
41 | algoliasearch: '>= 4.9.1 < 6'
42 | dependencies:
43 | '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)
44 | '@algolia/client-search': 4.23.3
45 | algoliasearch: 4.23.3
46 | dev: false
47 |
48 | /@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3):
49 | resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==}
50 | peerDependencies:
51 | '@algolia/client-search': '>= 4.9.1 < 6'
52 | algoliasearch: '>= 4.9.1 < 6'
53 | dependencies:
54 | '@algolia/client-search': 4.23.3
55 | algoliasearch: 4.23.3
56 | dev: false
57 |
58 | /@algolia/cache-browser-local-storage@4.23.3:
59 | resolution: {integrity: sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==}
60 | dependencies:
61 | '@algolia/cache-common': 4.23.3
62 | dev: false
63 |
64 | /@algolia/cache-common@4.23.3:
65 | resolution: {integrity: sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==}
66 | dev: false
67 |
68 | /@algolia/cache-in-memory@4.23.3:
69 | resolution: {integrity: sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==}
70 | dependencies:
71 | '@algolia/cache-common': 4.23.3
72 | dev: false
73 |
74 | /@algolia/client-account@4.23.3:
75 | resolution: {integrity: sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==}
76 | dependencies:
77 | '@algolia/client-common': 4.23.3
78 | '@algolia/client-search': 4.23.3
79 | '@algolia/transporter': 4.23.3
80 | dev: false
81 |
82 | /@algolia/client-analytics@4.23.3:
83 | resolution: {integrity: sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==}
84 | dependencies:
85 | '@algolia/client-common': 4.23.3
86 | '@algolia/client-search': 4.23.3
87 | '@algolia/requester-common': 4.23.3
88 | '@algolia/transporter': 4.23.3
89 | dev: false
90 |
91 | /@algolia/client-common@4.23.3:
92 | resolution: {integrity: sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==}
93 | dependencies:
94 | '@algolia/requester-common': 4.23.3
95 | '@algolia/transporter': 4.23.3
96 | dev: false
97 |
98 | /@algolia/client-personalization@4.23.3:
99 | resolution: {integrity: sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==}
100 | dependencies:
101 | '@algolia/client-common': 4.23.3
102 | '@algolia/requester-common': 4.23.3
103 | '@algolia/transporter': 4.23.3
104 | dev: false
105 |
106 | /@algolia/client-search@4.23.3:
107 | resolution: {integrity: sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==}
108 | dependencies:
109 | '@algolia/client-common': 4.23.3
110 | '@algolia/requester-common': 4.23.3
111 | '@algolia/transporter': 4.23.3
112 | dev: false
113 |
114 | /@algolia/logger-common@4.23.3:
115 | resolution: {integrity: sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==}
116 | dev: false
117 |
118 | /@algolia/logger-console@4.23.3:
119 | resolution: {integrity: sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==}
120 | dependencies:
121 | '@algolia/logger-common': 4.23.3
122 | dev: false
123 |
124 | /@algolia/recommend@4.23.3:
125 | resolution: {integrity: sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==}
126 | dependencies:
127 | '@algolia/cache-browser-local-storage': 4.23.3
128 | '@algolia/cache-common': 4.23.3
129 | '@algolia/cache-in-memory': 4.23.3
130 | '@algolia/client-common': 4.23.3
131 | '@algolia/client-search': 4.23.3
132 | '@algolia/logger-common': 4.23.3
133 | '@algolia/logger-console': 4.23.3
134 | '@algolia/requester-browser-xhr': 4.23.3
135 | '@algolia/requester-common': 4.23.3
136 | '@algolia/requester-node-http': 4.23.3
137 | '@algolia/transporter': 4.23.3
138 | dev: false
139 |
140 | /@algolia/requester-browser-xhr@4.23.3:
141 | resolution: {integrity: sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==}
142 | dependencies:
143 | '@algolia/requester-common': 4.23.3
144 | dev: false
145 |
146 | /@algolia/requester-common@4.23.3:
147 | resolution: {integrity: sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==}
148 | dev: false
149 |
150 | /@algolia/requester-node-http@4.23.3:
151 | resolution: {integrity: sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==}
152 | dependencies:
153 | '@algolia/requester-common': 4.23.3
154 | dev: false
155 |
156 | /@algolia/transporter@4.23.3:
157 | resolution: {integrity: sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==}
158 | dependencies:
159 | '@algolia/cache-common': 4.23.3
160 | '@algolia/logger-common': 4.23.3
161 | '@algolia/requester-common': 4.23.3
162 | dev: false
163 |
164 | /@babel/helper-string-parser@7.24.1:
165 | resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==}
166 | engines: {node: '>=6.9.0'}
167 | dev: false
168 |
169 | /@babel/helper-validator-identifier@7.22.20:
170 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
171 | engines: {node: '>=6.9.0'}
172 | dev: false
173 |
174 | /@babel/parser@7.24.4:
175 | resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==}
176 | engines: {node: '>=6.0.0'}
177 | hasBin: true
178 | dependencies:
179 | '@babel/types': 7.24.0
180 | dev: false
181 |
182 | /@babel/types@7.24.0:
183 | resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
184 | engines: {node: '>=6.9.0'}
185 | dependencies:
186 | '@babel/helper-string-parser': 7.24.1
187 | '@babel/helper-validator-identifier': 7.22.20
188 | to-fast-properties: 2.0.0
189 | dev: false
190 |
191 | /@docsearch/css@3.6.0:
192 | resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==}
193 | dev: false
194 |
195 | /@docsearch/js@3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0):
196 | resolution: {integrity: sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==}
197 | dependencies:
198 | '@docsearch/react': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0)
199 | preact: 10.20.2
200 | transitivePeerDependencies:
201 | - '@algolia/client-search'
202 | - '@types/react'
203 | - react
204 | - react-dom
205 | - search-insights
206 | dev: false
207 |
208 | /@docsearch/react@3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0):
209 | resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==}
210 | peerDependencies:
211 | '@types/react': '>= 16.8.0 < 19.0.0'
212 | react: '>= 16.8.0 < 19.0.0'
213 | react-dom: '>= 16.8.0 < 19.0.0'
214 | search-insights: '>= 1 < 3'
215 | peerDependenciesMeta:
216 | '@types/react':
217 | optional: true
218 | react:
219 | optional: true
220 | react-dom:
221 | optional: true
222 | search-insights:
223 | optional: true
224 | dependencies:
225 | '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)(search-insights@2.13.0)
226 | '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.23.3)(algoliasearch@4.23.3)
227 | '@docsearch/css': 3.6.0
228 | algoliasearch: 4.23.3
229 | search-insights: 2.13.0
230 | transitivePeerDependencies:
231 | - '@algolia/client-search'
232 | dev: false
233 |
234 | /@esbuild/aix-ppc64@0.20.2:
235 | resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
236 | engines: {node: '>=12'}
237 | cpu: [ppc64]
238 | os: [aix]
239 | requiresBuild: true
240 | dev: false
241 | optional: true
242 |
243 | /@esbuild/android-arm64@0.20.2:
244 | resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
245 | engines: {node: '>=12'}
246 | cpu: [arm64]
247 | os: [android]
248 | requiresBuild: true
249 | dev: false
250 | optional: true
251 |
252 | /@esbuild/android-arm@0.20.2:
253 | resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
254 | engines: {node: '>=12'}
255 | cpu: [arm]
256 | os: [android]
257 | requiresBuild: true
258 | dev: false
259 | optional: true
260 |
261 | /@esbuild/android-x64@0.20.2:
262 | resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
263 | engines: {node: '>=12'}
264 | cpu: [x64]
265 | os: [android]
266 | requiresBuild: true
267 | dev: false
268 | optional: true
269 |
270 | /@esbuild/darwin-arm64@0.20.2:
271 | resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
272 | engines: {node: '>=12'}
273 | cpu: [arm64]
274 | os: [darwin]
275 | requiresBuild: true
276 | dev: false
277 | optional: true
278 |
279 | /@esbuild/darwin-x64@0.20.2:
280 | resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
281 | engines: {node: '>=12'}
282 | cpu: [x64]
283 | os: [darwin]
284 | requiresBuild: true
285 | dev: false
286 | optional: true
287 |
288 | /@esbuild/freebsd-arm64@0.20.2:
289 | resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
290 | engines: {node: '>=12'}
291 | cpu: [arm64]
292 | os: [freebsd]
293 | requiresBuild: true
294 | dev: false
295 | optional: true
296 |
297 | /@esbuild/freebsd-x64@0.20.2:
298 | resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
299 | engines: {node: '>=12'}
300 | cpu: [x64]
301 | os: [freebsd]
302 | requiresBuild: true
303 | dev: false
304 | optional: true
305 |
306 | /@esbuild/linux-arm64@0.20.2:
307 | resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
308 | engines: {node: '>=12'}
309 | cpu: [arm64]
310 | os: [linux]
311 | requiresBuild: true
312 | dev: false
313 | optional: true
314 |
315 | /@esbuild/linux-arm@0.20.2:
316 | resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
317 | engines: {node: '>=12'}
318 | cpu: [arm]
319 | os: [linux]
320 | requiresBuild: true
321 | dev: false
322 | optional: true
323 |
324 | /@esbuild/linux-ia32@0.20.2:
325 | resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
326 | engines: {node: '>=12'}
327 | cpu: [ia32]
328 | os: [linux]
329 | requiresBuild: true
330 | dev: false
331 | optional: true
332 |
333 | /@esbuild/linux-loong64@0.20.2:
334 | resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
335 | engines: {node: '>=12'}
336 | cpu: [loong64]
337 | os: [linux]
338 | requiresBuild: true
339 | dev: false
340 | optional: true
341 |
342 | /@esbuild/linux-mips64el@0.20.2:
343 | resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
344 | engines: {node: '>=12'}
345 | cpu: [mips64el]
346 | os: [linux]
347 | requiresBuild: true
348 | dev: false
349 | optional: true
350 |
351 | /@esbuild/linux-ppc64@0.20.2:
352 | resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
353 | engines: {node: '>=12'}
354 | cpu: [ppc64]
355 | os: [linux]
356 | requiresBuild: true
357 | dev: false
358 | optional: true
359 |
360 | /@esbuild/linux-riscv64@0.20.2:
361 | resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
362 | engines: {node: '>=12'}
363 | cpu: [riscv64]
364 | os: [linux]
365 | requiresBuild: true
366 | dev: false
367 | optional: true
368 |
369 | /@esbuild/linux-s390x@0.20.2:
370 | resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
371 | engines: {node: '>=12'}
372 | cpu: [s390x]
373 | os: [linux]
374 | requiresBuild: true
375 | dev: false
376 | optional: true
377 |
378 | /@esbuild/linux-x64@0.20.2:
379 | resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
380 | engines: {node: '>=12'}
381 | cpu: [x64]
382 | os: [linux]
383 | requiresBuild: true
384 | dev: false
385 | optional: true
386 |
387 | /@esbuild/netbsd-x64@0.20.2:
388 | resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
389 | engines: {node: '>=12'}
390 | cpu: [x64]
391 | os: [netbsd]
392 | requiresBuild: true
393 | dev: false
394 | optional: true
395 |
396 | /@esbuild/openbsd-x64@0.20.2:
397 | resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
398 | engines: {node: '>=12'}
399 | cpu: [x64]
400 | os: [openbsd]
401 | requiresBuild: true
402 | dev: false
403 | optional: true
404 |
405 | /@esbuild/sunos-x64@0.20.2:
406 | resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
407 | engines: {node: '>=12'}
408 | cpu: [x64]
409 | os: [sunos]
410 | requiresBuild: true
411 | dev: false
412 | optional: true
413 |
414 | /@esbuild/win32-arm64@0.20.2:
415 | resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
416 | engines: {node: '>=12'}
417 | cpu: [arm64]
418 | os: [win32]
419 | requiresBuild: true
420 | dev: false
421 | optional: true
422 |
423 | /@esbuild/win32-ia32@0.20.2:
424 | resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
425 | engines: {node: '>=12'}
426 | cpu: [ia32]
427 | os: [win32]
428 | requiresBuild: true
429 | dev: false
430 | optional: true
431 |
432 | /@esbuild/win32-x64@0.20.2:
433 | resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
434 | engines: {node: '>=12'}
435 | cpu: [x64]
436 | os: [win32]
437 | requiresBuild: true
438 | dev: false
439 | optional: true
440 |
441 | /@jridgewell/sourcemap-codec@1.4.15:
442 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
443 | dev: false
444 |
445 | /@rollup/rollup-android-arm-eabi@4.16.4:
446 | resolution: {integrity: sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==}
447 | cpu: [arm]
448 | os: [android]
449 | requiresBuild: true
450 | dev: false
451 | optional: true
452 |
453 | /@rollup/rollup-android-arm64@4.16.4:
454 | resolution: {integrity: sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==}
455 | cpu: [arm64]
456 | os: [android]
457 | requiresBuild: true
458 | dev: false
459 | optional: true
460 |
461 | /@rollup/rollup-darwin-arm64@4.16.4:
462 | resolution: {integrity: sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==}
463 | cpu: [arm64]
464 | os: [darwin]
465 | requiresBuild: true
466 | dev: false
467 | optional: true
468 |
469 | /@rollup/rollup-darwin-x64@4.16.4:
470 | resolution: {integrity: sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==}
471 | cpu: [x64]
472 | os: [darwin]
473 | requiresBuild: true
474 | dev: false
475 | optional: true
476 |
477 | /@rollup/rollup-linux-arm-gnueabihf@4.16.4:
478 | resolution: {integrity: sha512-ADm/xt86JUnmAfA9mBqFcRp//RVRt1ohGOYF6yL+IFCYqOBNwy5lbEK05xTsEoJq+/tJzg8ICUtS82WinJRuIw==}
479 | cpu: [arm]
480 | os: [linux]
481 | requiresBuild: true
482 | dev: false
483 | optional: true
484 |
485 | /@rollup/rollup-linux-arm-musleabihf@4.16.4:
486 | resolution: {integrity: sha512-tJfJaXPiFAG+Jn3cutp7mCs1ePltuAgRqdDZrzb1aeE3TktWWJ+g7xK9SNlaSUFw6IU4QgOxAY4rA+wZUT5Wfg==}
487 | cpu: [arm]
488 | os: [linux]
489 | requiresBuild: true
490 | dev: false
491 | optional: true
492 |
493 | /@rollup/rollup-linux-arm64-gnu@4.16.4:
494 | resolution: {integrity: sha512-7dy1BzQkgYlUTapDTvK997cgi0Orh5Iu7JlZVBy1MBURk7/HSbHkzRnXZa19ozy+wwD8/SlpJnOOckuNZtJR9w==}
495 | cpu: [arm64]
496 | os: [linux]
497 | requiresBuild: true
498 | dev: false
499 | optional: true
500 |
501 | /@rollup/rollup-linux-arm64-musl@4.16.4:
502 | resolution: {integrity: sha512-zsFwdUw5XLD1gQe0aoU2HVceI6NEW7q7m05wA46eUAyrkeNYExObfRFQcvA6zw8lfRc5BHtan3tBpo+kqEOxmg==}
503 | cpu: [arm64]
504 | os: [linux]
505 | requiresBuild: true
506 | dev: false
507 | optional: true
508 |
509 | /@rollup/rollup-linux-powerpc64le-gnu@4.16.4:
510 | resolution: {integrity: sha512-p8C3NnxXooRdNrdv6dBmRTddEapfESEUflpICDNKXpHvTjRRq1J82CbU5G3XfebIZyI3B0s074JHMWD36qOW6w==}
511 | cpu: [ppc64]
512 | os: [linux]
513 | requiresBuild: true
514 | dev: false
515 | optional: true
516 |
517 | /@rollup/rollup-linux-riscv64-gnu@4.16.4:
518 | resolution: {integrity: sha512-Lh/8ckoar4s4Id2foY7jNgitTOUQczwMWNYi+Mjt0eQ9LKhr6sK477REqQkmy8YHY3Ca3A2JJVdXnfb3Rrwkng==}
519 | cpu: [riscv64]
520 | os: [linux]
521 | requiresBuild: true
522 | dev: false
523 | optional: true
524 |
525 | /@rollup/rollup-linux-s390x-gnu@4.16.4:
526 | resolution: {integrity: sha512-1xwwn9ZCQYuqGmulGsTZoKrrn0z2fAur2ujE60QgyDpHmBbXbxLaQiEvzJWDrscRq43c8DnuHx3QorhMTZgisQ==}
527 | cpu: [s390x]
528 | os: [linux]
529 | requiresBuild: true
530 | dev: false
531 | optional: true
532 |
533 | /@rollup/rollup-linux-x64-gnu@4.16.4:
534 | resolution: {integrity: sha512-LuOGGKAJ7dfRtxVnO1i3qWc6N9sh0Em/8aZ3CezixSTM+E9Oq3OvTsvC4sm6wWjzpsIlOCnZjdluINKESflJLA==}
535 | cpu: [x64]
536 | os: [linux]
537 | requiresBuild: true
538 | dev: false
539 | optional: true
540 |
541 | /@rollup/rollup-linux-x64-musl@4.16.4:
542 | resolution: {integrity: sha512-ch86i7KkJKkLybDP2AtySFTRi5fM3KXp0PnHocHuJMdZwu7BuyIKi35BE9guMlmTpwwBTB3ljHj9IQXnTCD0vA==}
543 | cpu: [x64]
544 | os: [linux]
545 | requiresBuild: true
546 | dev: false
547 | optional: true
548 |
549 | /@rollup/rollup-win32-arm64-msvc@4.16.4:
550 | resolution: {integrity: sha512-Ma4PwyLfOWZWayfEsNQzTDBVW8PZ6TUUN1uFTBQbF2Chv/+sjenE86lpiEwj2FiviSmSZ4Ap4MaAfl1ciF4aSA==}
551 | cpu: [arm64]
552 | os: [win32]
553 | requiresBuild: true
554 | dev: false
555 | optional: true
556 |
557 | /@rollup/rollup-win32-ia32-msvc@4.16.4:
558 | resolution: {integrity: sha512-9m/ZDrQsdo/c06uOlP3W9G2ENRVzgzbSXmXHT4hwVaDQhYcRpi9bgBT0FTG9OhESxwK0WjQxYOSfv40cU+T69w==}
559 | cpu: [ia32]
560 | os: [win32]
561 | requiresBuild: true
562 | dev: false
563 | optional: true
564 |
565 | /@rollup/rollup-win32-x64-msvc@4.16.4:
566 | resolution: {integrity: sha512-YunpoOAyGLDseanENHmbFvQSfVL5BxW3k7hhy0eN4rb3gS/ct75dVD0EXOWIqFT/nE8XYW6LP6vz6ctKRi0k9A==}
567 | cpu: [x64]
568 | os: [win32]
569 | requiresBuild: true
570 | dev: false
571 | optional: true
572 |
573 | /@shikijs/core@1.3.0:
574 | resolution: {integrity: sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA==}
575 | dev: false
576 |
577 | /@shikijs/transformers@1.3.0:
578 | resolution: {integrity: sha512-3mlpg2I9CjhjE96dEWQOGeCWoPcyTov3s4aAsHmgvnTHa8MBknEnCQy8/xivJPSpD+olqOqIEoHnLfbNJK29AA==}
579 | dependencies:
580 | shiki: 1.3.0
581 | dev: false
582 |
583 | /@types/estree@1.0.5:
584 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
585 | dev: false
586 |
587 | /@types/linkify-it@3.0.5:
588 | resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==}
589 | dev: false
590 |
591 | /@types/markdown-it@14.0.1:
592 | resolution: {integrity: sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==}
593 | dependencies:
594 | '@types/linkify-it': 3.0.5
595 | '@types/mdurl': 1.0.5
596 | dev: false
597 |
598 | /@types/mdurl@1.0.5:
599 | resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==}
600 | dev: false
601 |
602 | /@types/web-bluetooth@0.0.20:
603 | resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
604 | dev: false
605 |
606 | /@vitejs/plugin-vue@5.0.4(vite@5.2.10)(vue@3.4.25):
607 | resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
608 | engines: {node: ^18.0.0 || >=20.0.0}
609 | peerDependencies:
610 | vite: ^5.0.0
611 | vue: ^3.2.25
612 | dependencies:
613 | vite: 5.2.10
614 | vue: 3.4.25
615 | dev: false
616 |
617 | /@vue/compiler-core@3.4.25:
618 | resolution: {integrity: sha512-Y2pLLopaElgWnMNolgG8w3C5nNUVev80L7hdQ5iIKPtMJvhVpG0zhnBG/g3UajJmZdvW0fktyZTotEHD1Srhbg==}
619 | dependencies:
620 | '@babel/parser': 7.24.4
621 | '@vue/shared': 3.4.25
622 | entities: 4.5.0
623 | estree-walker: 2.0.2
624 | source-map-js: 1.2.0
625 | dev: false
626 |
627 | /@vue/compiler-dom@3.4.25:
628 | resolution: {integrity: sha512-Ugz5DusW57+HjllAugLci19NsDK+VyjGvmbB2TXaTcSlQxwL++2PETHx/+Qv6qFwNLzSt7HKepPe4DcTE3pBWg==}
629 | dependencies:
630 | '@vue/compiler-core': 3.4.25
631 | '@vue/shared': 3.4.25
632 | dev: false
633 |
634 | /@vue/compiler-sfc@3.4.25:
635 | resolution: {integrity: sha512-m7rryuqzIoQpOBZ18wKyq05IwL6qEpZxFZfRxlNYuIPDqywrXQxgUwLXIvoU72gs6cRdY6wHD0WVZIFE4OEaAQ==}
636 | dependencies:
637 | '@babel/parser': 7.24.4
638 | '@vue/compiler-core': 3.4.25
639 | '@vue/compiler-dom': 3.4.25
640 | '@vue/compiler-ssr': 3.4.25
641 | '@vue/shared': 3.4.25
642 | estree-walker: 2.0.2
643 | magic-string: 0.30.10
644 | postcss: 8.4.38
645 | source-map-js: 1.2.0
646 | dev: false
647 |
648 | /@vue/compiler-ssr@3.4.25:
649 | resolution: {integrity: sha512-H2ohvM/Pf6LelGxDBnfbbXFPyM4NE3hrw0e/EpwuSiYu8c819wx+SVGdJ65p/sFrYDd6OnSDxN1MB2mN07hRSQ==}
650 | dependencies:
651 | '@vue/compiler-dom': 3.4.25
652 | '@vue/shared': 3.4.25
653 | dev: false
654 |
655 | /@vue/devtools-api@7.1.3(vue@3.4.25):
656 | resolution: {integrity: sha512-W8IwFJ/o5iUk78jpqhvScbgCsPiOp2uileDVC0NDtW38gCWhsnu9SeBTjcdu3lbwLdsjc+H1c5Msd/x9ApbcFA==}
657 | dependencies:
658 | '@vue/devtools-kit': 7.1.3(vue@3.4.25)
659 | transitivePeerDependencies:
660 | - vue
661 | dev: false
662 |
663 | /@vue/devtools-kit@7.1.3(vue@3.4.25):
664 | resolution: {integrity: sha512-NFskFSJMVCBXTkByuk2llzI3KD3Blcm7WqiRorWjD6nClHPgkH5BobDH08rfulqq5ocRt5xV+3qOT1Q9FXJrwQ==}
665 | peerDependencies:
666 | vue: ^3.0.0
667 | dependencies:
668 | '@vue/devtools-shared': 7.1.3
669 | hookable: 5.5.3
670 | mitt: 3.0.1
671 | perfect-debounce: 1.0.0
672 | speakingurl: 14.0.1
673 | vue: 3.4.25
674 | dev: false
675 |
676 | /@vue/devtools-shared@7.1.3:
677 | resolution: {integrity: sha512-KJ3AfgjTn3tJz/XKF+BlVShNPecim3G21oHRue+YQOsooW+0s+qXvm09U09aO7yBza5SivL1QgxSrzAbiKWjhQ==}
678 | dependencies:
679 | rfdc: 1.3.1
680 | dev: false
681 |
682 | /@vue/reactivity@3.4.25:
683 | resolution: {integrity: sha512-mKbEtKr1iTxZkAG3vm3BtKHAOhuI4zzsVcN0epDldU/THsrvfXRKzq+lZnjczZGnTdh3ojd86/WrP+u9M51pWQ==}
684 | dependencies:
685 | '@vue/shared': 3.4.25
686 | dev: false
687 |
688 | /@vue/runtime-core@3.4.25:
689 | resolution: {integrity: sha512-3qhsTqbEh8BMH3pXf009epCI5E7bKu28fJLi9O6W+ZGt/6xgSfMuGPqa5HRbUxLoehTNp5uWvzCr60KuiRIL0Q==}
690 | dependencies:
691 | '@vue/reactivity': 3.4.25
692 | '@vue/shared': 3.4.25
693 | dev: false
694 |
695 | /@vue/runtime-dom@3.4.25:
696 | resolution: {integrity: sha512-ode0sj77kuwXwSc+2Yhk8JMHZh1sZp9F/51wdBiz3KGaWltbKtdihlJFhQG4H6AY+A06zzeMLkq6qu8uDSsaoA==}
697 | dependencies:
698 | '@vue/runtime-core': 3.4.25
699 | '@vue/shared': 3.4.25
700 | csstype: 3.1.3
701 | dev: false
702 |
703 | /@vue/server-renderer@3.4.25(vue@3.4.25):
704 | resolution: {integrity: sha512-8VTwq0Zcu3K4dWV0jOwIVINESE/gha3ifYCOKEhxOj6MEl5K5y8J8clQncTcDhKF+9U765nRw4UdUEXvrGhyVQ==}
705 | peerDependencies:
706 | vue: 3.4.25
707 | dependencies:
708 | '@vue/compiler-ssr': 3.4.25
709 | '@vue/shared': 3.4.25
710 | vue: 3.4.25
711 | dev: false
712 |
713 | /@vue/shared@3.4.25:
714 | resolution: {integrity: sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA==}
715 | dev: false
716 |
717 | /@vueuse/core@10.9.0(vue@3.4.25):
718 | resolution: {integrity: sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==}
719 | dependencies:
720 | '@types/web-bluetooth': 0.0.20
721 | '@vueuse/metadata': 10.9.0
722 | '@vueuse/shared': 10.9.0(vue@3.4.25)
723 | vue-demi: 0.14.7(vue@3.4.25)
724 | transitivePeerDependencies:
725 | - '@vue/composition-api'
726 | - vue
727 | dev: false
728 |
729 | /@vueuse/integrations@10.9.0(focus-trap@7.5.4)(vue@3.4.25):
730 | resolution: {integrity: sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q==}
731 | peerDependencies:
732 | async-validator: '*'
733 | axios: '*'
734 | change-case: '*'
735 | drauu: '*'
736 | focus-trap: '*'
737 | fuse.js: '*'
738 | idb-keyval: '*'
739 | jwt-decode: '*'
740 | nprogress: '*'
741 | qrcode: '*'
742 | sortablejs: '*'
743 | universal-cookie: '*'
744 | peerDependenciesMeta:
745 | async-validator:
746 | optional: true
747 | axios:
748 | optional: true
749 | change-case:
750 | optional: true
751 | drauu:
752 | optional: true
753 | focus-trap:
754 | optional: true
755 | fuse.js:
756 | optional: true
757 | idb-keyval:
758 | optional: true
759 | jwt-decode:
760 | optional: true
761 | nprogress:
762 | optional: true
763 | qrcode:
764 | optional: true
765 | sortablejs:
766 | optional: true
767 | universal-cookie:
768 | optional: true
769 | dependencies:
770 | '@vueuse/core': 10.9.0(vue@3.4.25)
771 | '@vueuse/shared': 10.9.0(vue@3.4.25)
772 | focus-trap: 7.5.4
773 | vue-demi: 0.14.7(vue@3.4.25)
774 | transitivePeerDependencies:
775 | - '@vue/composition-api'
776 | - vue
777 | dev: false
778 |
779 | /@vueuse/metadata@10.9.0:
780 | resolution: {integrity: sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==}
781 | dev: false
782 |
783 | /@vueuse/shared@10.9.0(vue@3.4.25):
784 | resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==}
785 | dependencies:
786 | vue-demi: 0.14.7(vue@3.4.25)
787 | transitivePeerDependencies:
788 | - '@vue/composition-api'
789 | - vue
790 | dev: false
791 |
792 | /algoliasearch@4.23.3:
793 | resolution: {integrity: sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==}
794 | dependencies:
795 | '@algolia/cache-browser-local-storage': 4.23.3
796 | '@algolia/cache-common': 4.23.3
797 | '@algolia/cache-in-memory': 4.23.3
798 | '@algolia/client-account': 4.23.3
799 | '@algolia/client-analytics': 4.23.3
800 | '@algolia/client-common': 4.23.3
801 | '@algolia/client-personalization': 4.23.3
802 | '@algolia/client-search': 4.23.3
803 | '@algolia/logger-common': 4.23.3
804 | '@algolia/logger-console': 4.23.3
805 | '@algolia/recommend': 4.23.3
806 | '@algolia/requester-browser-xhr': 4.23.3
807 | '@algolia/requester-common': 4.23.3
808 | '@algolia/requester-node-http': 4.23.3
809 | '@algolia/transporter': 4.23.3
810 | dev: false
811 |
812 | /csstype@3.1.3:
813 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
814 | dev: false
815 |
816 | /entities@4.5.0:
817 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
818 | engines: {node: '>=0.12'}
819 | dev: false
820 |
821 | /esbuild@0.20.2:
822 | resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
823 | engines: {node: '>=12'}
824 | hasBin: true
825 | requiresBuild: true
826 | optionalDependencies:
827 | '@esbuild/aix-ppc64': 0.20.2
828 | '@esbuild/android-arm': 0.20.2
829 | '@esbuild/android-arm64': 0.20.2
830 | '@esbuild/android-x64': 0.20.2
831 | '@esbuild/darwin-arm64': 0.20.2
832 | '@esbuild/darwin-x64': 0.20.2
833 | '@esbuild/freebsd-arm64': 0.20.2
834 | '@esbuild/freebsd-x64': 0.20.2
835 | '@esbuild/linux-arm': 0.20.2
836 | '@esbuild/linux-arm64': 0.20.2
837 | '@esbuild/linux-ia32': 0.20.2
838 | '@esbuild/linux-loong64': 0.20.2
839 | '@esbuild/linux-mips64el': 0.20.2
840 | '@esbuild/linux-ppc64': 0.20.2
841 | '@esbuild/linux-riscv64': 0.20.2
842 | '@esbuild/linux-s390x': 0.20.2
843 | '@esbuild/linux-x64': 0.20.2
844 | '@esbuild/netbsd-x64': 0.20.2
845 | '@esbuild/openbsd-x64': 0.20.2
846 | '@esbuild/sunos-x64': 0.20.2
847 | '@esbuild/win32-arm64': 0.20.2
848 | '@esbuild/win32-ia32': 0.20.2
849 | '@esbuild/win32-x64': 0.20.2
850 | dev: false
851 |
852 | /estree-walker@2.0.2:
853 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
854 | dev: false
855 |
856 | /focus-trap@7.5.4:
857 | resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
858 | dependencies:
859 | tabbable: 6.2.0
860 | dev: false
861 |
862 | /fsevents@2.3.3:
863 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
864 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
865 | os: [darwin]
866 | requiresBuild: true
867 | dev: false
868 | optional: true
869 |
870 | /hookable@5.5.3:
871 | resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
872 | dev: false
873 |
874 | /magic-string@0.30.10:
875 | resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
876 | dependencies:
877 | '@jridgewell/sourcemap-codec': 1.4.15
878 | dev: false
879 |
880 | /mark.js@8.11.1:
881 | resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
882 | dev: false
883 |
884 | /minisearch@6.3.0:
885 | resolution: {integrity: sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==}
886 | dev: false
887 |
888 | /mitt@3.0.1:
889 | resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
890 | dev: false
891 |
892 | /nanoid@3.3.7:
893 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
894 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
895 | hasBin: true
896 | dev: false
897 |
898 | /perfect-debounce@1.0.0:
899 | resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
900 | dev: false
901 |
902 | /picocolors@1.0.0:
903 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
904 | dev: false
905 |
906 | /postcss@8.4.38:
907 | resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
908 | engines: {node: ^10 || ^12 || >=14}
909 | dependencies:
910 | nanoid: 3.3.7
911 | picocolors: 1.0.0
912 | source-map-js: 1.2.0
913 | dev: false
914 |
915 | /preact@10.20.2:
916 | resolution: {integrity: sha512-S1d1ernz3KQ+Y2awUxKakpfOg2CEmJmwOP+6igPx6dgr6pgDvenqYviyokWso2rhHvGtTlWWnJDa7RaPbQerTg==}
917 | dev: false
918 |
919 | /rfdc@1.3.1:
920 | resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==}
921 | dev: false
922 |
923 | /rollup@4.16.4:
924 | resolution: {integrity: sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA==}
925 | engines: {node: '>=18.0.0', npm: '>=8.0.0'}
926 | hasBin: true
927 | dependencies:
928 | '@types/estree': 1.0.5
929 | optionalDependencies:
930 | '@rollup/rollup-android-arm-eabi': 4.16.4
931 | '@rollup/rollup-android-arm64': 4.16.4
932 | '@rollup/rollup-darwin-arm64': 4.16.4
933 | '@rollup/rollup-darwin-x64': 4.16.4
934 | '@rollup/rollup-linux-arm-gnueabihf': 4.16.4
935 | '@rollup/rollup-linux-arm-musleabihf': 4.16.4
936 | '@rollup/rollup-linux-arm64-gnu': 4.16.4
937 | '@rollup/rollup-linux-arm64-musl': 4.16.4
938 | '@rollup/rollup-linux-powerpc64le-gnu': 4.16.4
939 | '@rollup/rollup-linux-riscv64-gnu': 4.16.4
940 | '@rollup/rollup-linux-s390x-gnu': 4.16.4
941 | '@rollup/rollup-linux-x64-gnu': 4.16.4
942 | '@rollup/rollup-linux-x64-musl': 4.16.4
943 | '@rollup/rollup-win32-arm64-msvc': 4.16.4
944 | '@rollup/rollup-win32-ia32-msvc': 4.16.4
945 | '@rollup/rollup-win32-x64-msvc': 4.16.4
946 | fsevents: 2.3.3
947 | dev: false
948 |
949 | /search-insights@2.13.0:
950 | resolution: {integrity: sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==}
951 | dev: false
952 |
953 | /shiki@1.3.0:
954 | resolution: {integrity: sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww==}
955 | dependencies:
956 | '@shikijs/core': 1.3.0
957 | dev: false
958 |
959 | /source-map-js@1.2.0:
960 | resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
961 | engines: {node: '>=0.10.0'}
962 | dev: false
963 |
964 | /speakingurl@14.0.1:
965 | resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
966 | engines: {node: '>=0.10.0'}
967 | dev: false
968 |
969 | /tabbable@6.2.0:
970 | resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
971 | dev: false
972 |
973 | /to-fast-properties@2.0.0:
974 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
975 | engines: {node: '>=4'}
976 | dev: false
977 |
978 | /vite@5.2.10:
979 | resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==}
980 | engines: {node: ^18.0.0 || >=20.0.0}
981 | hasBin: true
982 | peerDependencies:
983 | '@types/node': ^18.0.0 || >=20.0.0
984 | less: '*'
985 | lightningcss: ^1.21.0
986 | sass: '*'
987 | stylus: '*'
988 | sugarss: '*'
989 | terser: ^5.4.0
990 | peerDependenciesMeta:
991 | '@types/node':
992 | optional: true
993 | less:
994 | optional: true
995 | lightningcss:
996 | optional: true
997 | sass:
998 | optional: true
999 | stylus:
1000 | optional: true
1001 | sugarss:
1002 | optional: true
1003 | terser:
1004 | optional: true
1005 | dependencies:
1006 | esbuild: 0.20.2
1007 | postcss: 8.4.38
1008 | rollup: 4.16.4
1009 | optionalDependencies:
1010 | fsevents: 2.3.3
1011 | dev: false
1012 |
1013 | /vitepress@1.1.3(@algolia/client-search@4.23.3)(search-insights@2.13.0):
1014 | resolution: {integrity: sha512-hGrIYN0w9IHWs0NQSnlMjKV/v/HLfD+Ywv5QdvCSkiT32mpNOOwUrZjnqZv/JL/WBPpUc94eghTUvmipxw0xrA==}
1015 | hasBin: true
1016 | peerDependencies:
1017 | markdown-it-mathjax3: ^4
1018 | postcss: ^8
1019 | peerDependenciesMeta:
1020 | markdown-it-mathjax3:
1021 | optional: true
1022 | postcss:
1023 | optional: true
1024 | dependencies:
1025 | '@docsearch/css': 3.6.0
1026 | '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.13.0)
1027 | '@shikijs/core': 1.3.0
1028 | '@shikijs/transformers': 1.3.0
1029 | '@types/markdown-it': 14.0.1
1030 | '@vitejs/plugin-vue': 5.0.4(vite@5.2.10)(vue@3.4.25)
1031 | '@vue/devtools-api': 7.1.3(vue@3.4.25)
1032 | '@vueuse/core': 10.9.0(vue@3.4.25)
1033 | '@vueuse/integrations': 10.9.0(focus-trap@7.5.4)(vue@3.4.25)
1034 | focus-trap: 7.5.4
1035 | mark.js: 8.11.1
1036 | minisearch: 6.3.0
1037 | shiki: 1.3.0
1038 | vite: 5.2.10
1039 | vue: 3.4.25
1040 | transitivePeerDependencies:
1041 | - '@algolia/client-search'
1042 | - '@types/node'
1043 | - '@types/react'
1044 | - '@vue/composition-api'
1045 | - async-validator
1046 | - axios
1047 | - change-case
1048 | - drauu
1049 | - fuse.js
1050 | - idb-keyval
1051 | - jwt-decode
1052 | - less
1053 | - lightningcss
1054 | - nprogress
1055 | - qrcode
1056 | - react
1057 | - react-dom
1058 | - sass
1059 | - search-insights
1060 | - sortablejs
1061 | - stylus
1062 | - sugarss
1063 | - terser
1064 | - typescript
1065 | - universal-cookie
1066 | dev: false
1067 |
1068 | /vue-demi@0.14.7(vue@3.4.25):
1069 | resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
1070 | engines: {node: '>=12'}
1071 | hasBin: true
1072 | requiresBuild: true
1073 | peerDependencies:
1074 | '@vue/composition-api': ^1.0.0-rc.1
1075 | vue: ^3.0.0-0 || ^2.6.0
1076 | peerDependenciesMeta:
1077 | '@vue/composition-api':
1078 | optional: true
1079 | dependencies:
1080 | vue: 3.4.25
1081 | dev: false
1082 |
1083 | /vue@3.4.25:
1084 | resolution: {integrity: sha512-HWyDqoBHMgav/OKiYA2ZQg+kjfMgLt/T0vg4cbIF7JbXAjDexRf5JRg+PWAfrAkSmTd2I8aPSXtooBFWHB98cg==}
1085 | peerDependencies:
1086 | typescript: '*'
1087 | peerDependenciesMeta:
1088 | typescript:
1089 | optional: true
1090 | dependencies:
1091 | '@vue/compiler-dom': 3.4.25
1092 | '@vue/compiler-sfc': 3.4.25
1093 | '@vue/runtime-dom': 3.4.25
1094 | '@vue/server-renderer': 3.4.25(vue@3.4.25)
1095 | '@vue/shared': 3.4.25
1096 | dev: false
1097 |
--------------------------------------------------------------------------------