├── .devcontainer ├── Dockerfile ├── postCreate.sh └── devcontainer.json ├── playground ├── src │ ├── vite-env.d.ts │ ├── main.ts │ ├── App.vue │ ├── style.css │ └── components │ │ └── TaskList.vue ├── .vscode │ └── extensions.json ├── tsconfig.json ├── vite.config.ts ├── .gitignore ├── index.html ├── sampleData.jsonl ├── convex │ ├── _generated │ │ ├── api.js │ │ ├── api.d.ts │ │ ├── dataModel.d.ts │ │ ├── server.js │ │ └── server.d.ts │ ├── tsconfig.json │ ├── tasks.ts │ └── README.md ├── tsconfig.app.json ├── package.json ├── tsconfig.node.json ├── README.md ├── public │ └── vite.svg └── pnpm-lock.yaml ├── .vscode ├── mcp.json ├── extensions.json └── settings.json ├── vitest.config.ts ├── src ├── composables │ ├── useConvexContext.ts │ ├── useConvexClient.ts │ ├── useConvexHttpClient.ts │ ├── useConvexHttpQuery.ts │ ├── useConvexQuery │ │ ├── lib │ │ │ ├── useQueryArgs.ts │ │ │ └── useServerQuery.ts │ │ ├── types.ts │ │ └── index.ts │ └── useConvexMutation.ts ├── index.ts ├── types.ts └── plugin.ts ├── tsconfig.json ├── .gitignore ├── eslint.config.js ├── LICENSE.md ├── package.json ├── README.md └── CHANGELOG.md /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM namesmt/images-alpine:node-dev 2 | -------------------------------------------------------------------------------- /playground/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /playground/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "./tsconfig.app.json" }, 4 | { "path": "./tsconfig.node.json" } 5 | ], 6 | "files": [] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": { 3 | "convex-mcp": { 4 | "type": "stdio", 5 | "command": "npx", 6 | "args": ["-y", "convex@latest", "mcp", "start"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.devcontainer/postCreate.sh: -------------------------------------------------------------------------------- 1 | # Force pnpm to use global store instead of $PWD, ref: https://stackoverflow.com/questions/76573556/pnpm-use-docker-volume-as-store-path 2 | pnpm config -g set store-dir $PNPM_HOME/store 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "streetsidesoftware.code-spell-checker", 5 | "usernamehw.errorlens", 6 | "yoavbls.pretty-ts-errors" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /playground/src/main.ts: -------------------------------------------------------------------------------- 1 | import { convexVue } from 'convex-vue' 2 | 3 | import { createApp } from 'vue' 4 | import App from './App.vue' 5 | 6 | import './style.css' 7 | 8 | const app = createApp(App) 9 | 10 | app.use(convexVue, { 11 | url: import.meta.env.VITE_CONVEX_URL, 12 | }) 13 | 14 | app.mount('#app') 15 | -------------------------------------------------------------------------------- /playground/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import vue from '@vitejs/plugin-vue' 3 | import { defineConfig } from 'vite' 4 | 5 | // https://vite.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | resolve: { 9 | alias: { 10 | 'convex-vue': path.resolve(__dirname, '../src/index.ts'), 11 | }, 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import type { ViteUserConfig } from 'vitest/config' 2 | import { configDefaults, defineConfig } from 'vitest/config' 3 | 4 | export default defineConfig({ 5 | test: { 6 | coverage: { 7 | exclude: [ 8 | ...configDefaults.coverage.exclude!, 9 | 'src/helpers/logger.ts', 10 | ], 11 | }, 12 | }, 13 | }) as ViteUserConfig 14 | -------------------------------------------------------------------------------- /playground/.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 | -------------------------------------------------------------------------------- /src/composables/useConvexContext.ts: -------------------------------------------------------------------------------- 1 | import type { ConvexVueContext } from '#src/plugin.ts' 2 | import { inject } from 'vue' 3 | 4 | /** 5 | * Returns the Convex plugin context 6 | */ 7 | export function useConvexContext(): ConvexVueContext { 8 | const convexVueContext = inject('convex-vue') 9 | if (!convexVueContext) 10 | throw new Error('Context not found') 11 | 12 | return convexVueContext 13 | } 14 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { useConvexClient } from './composables/useConvexClient' 2 | export { useConvexHttpClient } from './composables/useConvexHttpClient' 3 | 4 | export { useConvexHttpQuery } from './composables/useConvexHttpQuery' 5 | export { useConvexMutation } from './composables/useConvexMutation' 6 | export { useConvexQuery } from './composables/useConvexQuery' 7 | 8 | export * from './plugin' 9 | export * from './types' 10 | -------------------------------------------------------------------------------- /playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /playground/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | 22 | -------------------------------------------------------------------------------- /src/composables/useConvexClient.ts: -------------------------------------------------------------------------------- 1 | import type { ConvexClient } from 'convex/browser' 2 | import { useConvexContext } from './useConvexContext' 3 | 4 | /** 5 | * Returns the Convex client instance. 6 | */ 7 | export function useConvexClient(): ConvexClient { 8 | const convexVueContext = useConvexContext() 9 | 10 | if (!convexVueContext.clientRef.value) 11 | throw new Error('Client not initialized') 12 | 13 | return convexVueContext.clientRef.value 14 | } 15 | -------------------------------------------------------------------------------- /playground/sampleData.jsonl: -------------------------------------------------------------------------------- 1 | {"text": "Buy groceries", "isCompleted": true} 2 | {"text": "Go for a swim", "isCompleted": true} 3 | {"text": "Integrate Convex", "isCompleted": false} 4 | {"text": "Build a Vue app", "isCompleted": false} 5 | {"text": "Write a blog post", "isCompleted": false} 6 | {"text": "Read a book", "isCompleted": true} 7 | {"text": "Go for a walk", "isCompleted": false} 8 | {"text": "Cook dinner", "isCompleted": true} 9 | {"text": "Watch a movie", "isCompleted": false} 10 | -------------------------------------------------------------------------------- /src/composables/useConvexHttpClient.ts: -------------------------------------------------------------------------------- 1 | import type { ConvexHttpClient } from 'convex/browser' 2 | import { useConvexContext } from './useConvexContext' 3 | 4 | /** 5 | * Returns the Convex HTTP client instance. 6 | */ 7 | export function useConvexHttpClient(): ConvexHttpClient { 8 | const convexVueContext = useConvexContext() 9 | 10 | if (!convexVueContext.httpClientRef.value) 11 | throw new Error('HTTP client not initialized') 12 | 13 | return convexVueContext.httpClientRef.value 14 | } 15 | -------------------------------------------------------------------------------- /playground/convex/_generated/api.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated `api` utility. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { anyApi } from "convex/server"; 12 | 13 | /** 14 | * A utility for referencing Convex functions in your app's API. 15 | * 16 | * Usage: 17 | * ```js 18 | * const myFunctionReference = api.myModule.myFunction; 19 | * ``` 20 | */ 21 | export const api = anyApi; 22 | export const internal = anyApi; 23 | -------------------------------------------------------------------------------- /playground/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | 6 | "paths": { 7 | "convex-vue": ["../src"] 8 | }, 9 | /* Linting */ 10 | "strict": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "erasableSyntaxOnly": true, 15 | "noUncheckedSideEffectImports": true 16 | }, 17 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "../src/**/*.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "convex:dev": "convex dev", 9 | "build": "vue-tsc -b && vite build", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "convex": "^1.24.1", 14 | "vue": "^3.5.13" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^22.15.17", 18 | "@vitejs/plugin-vue": "^5.2.3", 19 | "@vue/tsconfig": "^0.7.0", 20 | "typescript": "~5.8.3", 21 | "vite": "^6.3.5", 22 | "vue-tsc": "^2.2.8" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@vue/tsconfig/tsconfig.dom.json", 4 | "compilerOptions": { 5 | "target": "ESNext", 6 | "baseUrl": ".", 7 | "module": "ESNext", 8 | "moduleResolution": "Bundler", 9 | "types": [ 10 | "vitest" 11 | ], 12 | "strict": true, 13 | "declaration": true, 14 | "inlineSources": false, 15 | "esModuleInterop": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "isolatedModules": true, 18 | "preserveWatchOutput": true, 19 | "skipLibCheck": true 20 | }, 21 | "exclude": [ 22 | "dist", 23 | "node_modules", 24 | "playground" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | .pnp 4 | .pnp.js 5 | .pnpm-store 6 | 7 | # testing 8 | coverage/ 9 | lib-cov/ 10 | 11 | # dev/builds/outputs 12 | .output/ 13 | .out/ 14 | .data/ 15 | .nuxt/ 16 | .next/ 17 | .nitro/ 18 | .cache/ 19 | dist/ 20 | 21 | # misc 22 | .DS_Store 23 | .fleet 24 | .idea 25 | temp 26 | tmp 27 | logs 28 | tsconfig.tsbuildinfo 29 | *.log 30 | *.pem 31 | 32 | # debug 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | .pnpm-debug.log* 37 | 38 | # local env files 39 | .env.*.ignored 40 | 41 | # turbo 42 | .turbo 43 | 44 | # cloud services 45 | .vercel 46 | .netlify 47 | 48 | # sst 49 | .sst 50 | 51 | # pnpm have a bug with tsx that creates the `tsx-0` folder everywhere 52 | tsx-0 53 | -------------------------------------------------------------------------------- /src/composables/useConvexHttpQuery.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server' 2 | import type { MaybeRefOrGetter } from 'vue' 3 | import { toValue } from 'vue' 4 | import { useConvexHttpClient } from './useConvexHttpClient' 5 | 6 | /** 7 | * A composable that returns a function to call a Convex query via the Convex HTTP API. 8 | * This is useful for server-side rendering or static site generation. 9 | */ 10 | export function useConvexHttpQuery>(query: Query, args: MaybeRefOrGetter> = {}): Promise> { 11 | const client = useConvexHttpClient() 12 | 13 | return client.query(query, toValue(args)) 14 | } 15 | -------------------------------------------------------------------------------- /src/composables/useConvexQuery/lib/useQueryArgs.ts: -------------------------------------------------------------------------------- 1 | import type { OptionalRestArgsAndOptions } from '#src/types.ts' 2 | import type { FunctionReference } from 'convex/server' 3 | import type { UseConvexQueryOptions } from '../types' 4 | import { computed, toValue } from 'vue' 5 | 6 | /** 7 | * A composable that returns the query arguments and options for use in Convex queries. 8 | * It determines whether the query arguments are optional and provides a computed reference to the arguments and options. 9 | */ 10 | export function useQueryArgs>(rest: OptionalRestArgsAndOptions) { 11 | const args = computed(() => toValue(rest[0])) 12 | const options = rest[1] 13 | 14 | return { args, options } 15 | } 16 | -------------------------------------------------------------------------------- /playground/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "moduleDetection": "force", 7 | "module": "ESNext", 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "paths": { 12 | "convex-vue": ["../src"] 13 | }, 14 | "allowImportingTsExtensions": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noEmit": true, 22 | "verbatimModuleSyntax": true, 23 | "erasableSyntaxOnly": true, 24 | "skipLibCheck": true, 25 | "noUncheckedSideEffectImports": true 26 | }, 27 | "include": ["vite.config.ts"] 28 | } 29 | -------------------------------------------------------------------------------- /playground/convex/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | /* This TypeScript project config describes the environment that 3 | * Convex functions run in and is used to typecheck them. 4 | * You can modify it, but some settings are required to use Convex. 5 | */ 6 | "compilerOptions": { 7 | 8 | /* These compiler options are required by Convex */ 9 | "target": "ESNext", 10 | "jsx": "react-jsx", 11 | "lib": ["ES2021", "dom"], 12 | "module": "ESNext", 13 | "moduleResolution": "Bundler", 14 | /* These settings are not required by Convex and can be modified. */ 15 | "allowJs": true, 16 | "strict": true, 17 | "noEmit": true, 18 | "allowSyntheticDefaultImports": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "isolatedModules": true, 21 | "skipLibCheck": true 22 | }, 23 | "include": ["./**/*"], 24 | "exclude": ["./_generated"] 25 | } 26 | -------------------------------------------------------------------------------- /playground/convex/_generated/api.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated `api` utility. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import type { 12 | ApiFromModules, 13 | FilterApi, 14 | FunctionReference, 15 | } from "convex/server"; 16 | import type * as tasks from "../tasks.js"; 17 | 18 | /** 19 | * A utility for referencing Convex functions in your app's API. 20 | * 21 | * Usage: 22 | * ```js 23 | * const myFunctionReference = api.myModule.myFunction; 24 | * ``` 25 | */ 26 | declare const fullApi: ApiFromModules<{ 27 | tasks: typeof tasks; 28 | }>; 29 | export declare const api: FilterApi< 30 | typeof fullApi, 31 | FunctionReference 32 | >; 33 | export declare const internal: FilterApi< 34 | typeof fullApi, 35 | FunctionReference 36 | >; 37 | -------------------------------------------------------------------------------- /playground/convex/tasks.ts: -------------------------------------------------------------------------------- 1 | import { v } from 'convex/values' 2 | import { mutation, query } from './_generated/server' 3 | 4 | export const get = query({ 5 | args: { 6 | // for testing reactive query args 7 | limit: v.optional(v.number()), 8 | }, 9 | handler: async (ctx, args) => { 10 | return await ctx.db.query('tasks').collect().then(tasks => tasks.splice(0, args.limit ?? tasks.length)) 11 | }, 12 | }) 13 | 14 | export const add = mutation({ 15 | args: { text: v.string() }, 16 | handler: async (ctx, args) => { 17 | return ctx.db.insert('tasks', { text: args.text }) 18 | }, 19 | }) 20 | 21 | export const remove = mutation({ 22 | args: { id: v.id('tasks') }, 23 | handler: async (ctx, args) => { 24 | const task = await ctx.db.get(args.id) 25 | if (!task) { 26 | throw new Error('Task not found') 27 | } 28 | await ctx.db.delete(task._id) 29 | }, 30 | }) 31 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionReference } from 'convex/server' 2 | import type { MaybeRefOrGetter } from 'vue' 3 | 4 | export type IsOptionalKey 5 | = object extends Pick ? true : false 6 | 7 | export type AreAllPropertiesOptional 8 | = true extends { 9 | [K in keyof T]: IsOptionalKey extends true ? never : true 10 | }[keyof T] 11 | ? false 12 | : true 13 | 14 | export type OptionalRestArgs> = AreAllPropertiesOptional extends true 15 | ? [args?: MaybeRefOrGetter] 16 | : [args: MaybeRefOrGetter] 17 | 18 | export type OptionalRestArgsAndOptions, Options> = AreAllPropertiesOptional extends true 19 | ? [args?: MaybeRefOrGetter, options?: Options] 20 | : [args: MaybeRefOrGetter, options?: Options] 21 | -------------------------------------------------------------------------------- /src/composables/useConvexQuery/types.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionReference, FunctionReturnType } from 'convex/server' 2 | import type { Ref } from 'vue' 3 | 4 | export interface UseConvexQueryOptions { 5 | /** 6 | * Set to `false` to disable this query during server-side rendering. 7 | */ 8 | server?: boolean 9 | } 10 | 11 | export interface UseConvexQueryReturn> { 12 | data: Ref | undefined> 13 | error: Ref 14 | isPending: Ref 15 | suspense: () => Promise> 16 | refetch: () => Promise> 17 | } 18 | 19 | export interface UseConvexQueryReturn> { 20 | data: Ref | undefined> 21 | error: Ref 22 | isPending: Ref 23 | suspense: () => Promise> 24 | refetch: () => Promise> 25 | } 26 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | 3 | export default antfu( 4 | { 5 | // Type of the project. 'lib' | 'app' (default 'app') 6 | type: 'lib', 7 | 8 | // Customize the stylistic rules 9 | stylistic: { 10 | indent: 2, // 4, or 'tab' 11 | quotes: 'single', // or 'double' 12 | }, 13 | 14 | // TypeScript and Vue are autodetected, you can also explicitly enable them: 15 | typescript: true, 16 | // vue: true, 17 | 18 | // ESLint ignore globs here 19 | ignores: [ 20 | '**/convex/_generated/**', 21 | ], 22 | }, 23 | { 24 | rules: { 25 | // Relaxes inline statements a bit 26 | 'style/max-statements-per-line': ['error', { max: 2 }], 27 | 'ts/explicit-function-return-type': 'off', 28 | }, 29 | }, 30 | // Allow trailing space for markdown formatting 31 | { 32 | files: ['**/*.md'], 33 | rules: { 34 | // // Experimental: allow multiple empty lines, this reduce conflicts AI Agents docs edits. 35 | // 'style/no-multiple-empty-lines': 'off', 36 | 'style/no-trailing-spaces': 'off', 37 | }, 38 | }, 39 | ) 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2025 Chris Visser 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/composables/useConvexMutation.ts: -------------------------------------------------------------------------------- 1 | import type { OptimisticUpdate } from 'convex/browser' 2 | import type { FunctionArgs, FunctionReference } from 'convex/server' 3 | import type { MaybeRefOrGetter } from 'vue' 4 | import { computed, ref, toValue } from 'vue' 5 | import { useConvexClient } from './useConvexClient' 6 | 7 | export interface MutationOptions> { 8 | optimisticUpdate?: OptimisticUpdate> 9 | } 10 | 11 | /** 12 | * Appliess a mutation to the Convex server. 13 | */ 14 | export function useConvexMutation>( 15 | mutationReference: Mutation, 16 | { optimisticUpdate }: MutationOptions = {}, 17 | ) { 18 | const client = useConvexClient() 19 | const error = ref(null) 20 | const isPendingCount = ref(0) 21 | 22 | const mutate = async (args: MaybeRefOrGetter>) => { 23 | ++isPendingCount.value 24 | error.value = null 25 | 26 | return await client.mutation(mutationReference, toValue(args), { optimisticUpdate }) 27 | .catch((e) => { 28 | error.value = e 29 | throw e 30 | }) 31 | .finally(() => { 32 | --isPendingCount.value 33 | }) 34 | } 35 | 36 | return { 37 | mutate, 38 | error, 39 | isPending: computed(() => Boolean(isPendingCount.value)), 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Disable the default formatter, use eslint instead 3 | "prettier.enable": false, 4 | "editor.formatOnSave": false, 5 | 6 | // Auto fix 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": "explicit", 9 | "source.organizeImports": "never" 10 | }, 11 | 12 | /* // Silent the stylistic rules in you IDE, but still auto fix them 13 | "eslint.rules.customizations": [ 14 | { "rule": "style/*", "severity": "off" }, 15 | { "rule": "format/*", "severity": "off" }, 16 | { "rule": "*-indent", "severity": "off" }, 17 | { "rule": "*-spacing", "severity": "off" }, 18 | { "rule": "*-spaces", "severity": "off" }, 19 | { "rule": "*-order", "severity": "off" }, 20 | { "rule": "*-dangle", "severity": "off" }, 21 | { "rule": "*-newline", "severity": "off" }, 22 | { "rule": "*quotes", "severity": "off" }, 23 | { "rule": "*semi", "severity": "off" } 24 | ], */ 25 | 26 | // Enable eslint for all supported languages 27 | "eslint.validate": [ 28 | "javascript", 29 | "javascriptreact", 30 | "typescript", 31 | "typescriptreact", 32 | "vue", 33 | "html", 34 | "markdown", 35 | "json", 36 | "jsonc", 37 | "yaml", 38 | "toml", 39 | "xml", 40 | "gql", 41 | "graphql", 42 | "astro", 43 | "svelte", 44 | "css", 45 | "less", 46 | "scss", 47 | "pcss", 48 | "postcss" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /playground/README.md: -------------------------------------------------------------------------------- 1 | # Convex Vue Playground 2 | 3 | This is a sample playground project for experimenting with the `convex-vue` integration. It demonstrates how to use Convex as a backend with a Vue 3 frontend, including real-time queries and composables. 4 | 5 | ## Features 6 | - Vue 3 + Vite setup 7 | - Convex backend integration 8 | - Example Convex query (tasks list) 9 | - TypeScript support 10 | 11 | ## Getting Started 12 | 13 | ### 1. Install dependencies 14 | 15 | ```bash 16 | bun install # or npm install, yarn, pnpm 17 | ``` 18 | 19 | ### 2. Start the Convex backend 20 | 21 | ```bash 22 | bun run convex:dev # or npm run convex:dev 23 | ``` 24 | 25 | This will start the Convex backend locally and generate the API files. 26 | 27 | ### 3. Start the frontend 28 | 29 | ```bash 30 | bun run dev # or npm run dev 31 | ``` 32 | 33 | Open [http://localhost:5173](http://localhost:5173) to view the app. 34 | 35 | ## Project Structure 36 | - `src/` – Vue app source code 37 | - `convex/` – Convex backend functions (see `tasks.ts` for example) 38 | - `sampleData.jsonl` – Example data for tasks 39 | 40 | ## Example Usage 41 | The main app (`src/App.vue`) shows how to use the `useConvexQuery` composable to fetch and display tasks from Convex in real time. 42 | 43 | ## Environment Variables 44 | Set your Convex deployment URL in `.env.local`: 45 | 46 | ``` 47 | VITE_CONVEX_URL=https://your-convex-deployment.convex.cloud 48 | ``` 49 | 50 | ## License 51 | MIT 52 | -------------------------------------------------------------------------------- /playground/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /playground/src/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | a { 17 | font-weight: 500; 18 | color: #646cff; 19 | text-decoration: inherit; 20 | } 21 | a:hover { 22 | color: #535bf2; 23 | } 24 | 25 | body { 26 | margin: 0; 27 | display: flex; 28 | place-items: center; 29 | min-width: 320px; 30 | min-height: 100vh; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | .card { 58 | padding: 2em; 59 | } 60 | 61 | #app { 62 | max-width: 1280px; 63 | margin: 0 auto; 64 | padding: 2rem; 65 | text-align: center; 66 | } 67 | 68 | @media (prefers-color-scheme: light) { 69 | :root { 70 | color: #213547; 71 | background-color: #ffffff; 72 | } 73 | a:hover { 74 | color: #747bff; 75 | } 76 | button { 77 | background-color: #f9f9f9; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsonc/comma-dangle */ 2 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 3 | // README at: https://github.com/devcontainers/templates/tree/main/src/alpine 4 | { 5 | "name": "Alpine-NamesMT", 6 | "image": "namesmt/images-alpine:node-dev", 7 | // "build": { 8 | // "dockerfile": "Dockerfile" 9 | // }, 10 | // Features to add to the dev container. More info: https://containers.dev/features. 11 | "features": { 12 | "ghcr.io/stuartleeks/dev-container-features/shell-history:0": { 13 | "version": "latest" 14 | } 15 | }, 16 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 17 | // "forwardPorts": [], 18 | // Use 'postCreateCommand' to run commands after the container is created. 19 | "postCreateCommand": "sh .devcontainer/postCreate.sh", 20 | // Configure tool-specific properties. 21 | "customizations": { 22 | "vscode": { 23 | "extensions": [ 24 | "dbaeumer.vscode-eslint", 25 | "streetsidesoftware.code-spell-checker", 26 | "usernamehw.errorlens", 27 | "yoavbls.pretty-ts-errors", 28 | ] 29 | } 30 | }, 31 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 32 | // "remoteUser": "root", 33 | "runArgs": [ 34 | // mount pnpm store for shared caching/linking 35 | // "--volume=${env:PNPM_HOME}/store:${env:PNPM_HOME}/store", 36 | 37 | // // Enable both in this block if you want to mount home dir 38 | // mount user home directory for things like git, ssh, and other configs 39 | // "--volume=${env:HOME}:${env:HOME}", 40 | // isolate the .vscode-server folder so you don't overwrite settings from remote ssh vscode 41 | // "--volume=${localWorkspaceFolder}/.cache/vscode-server:${env:HOME}/.vscode-server", 42 | // // 43 | ], 44 | } 45 | -------------------------------------------------------------------------------- /playground/convex/_generated/dataModel.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated data model types. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { AnyDataModel } from "convex/server"; 12 | import type { GenericId } from "convex/values"; 13 | 14 | /** 15 | * No `schema.ts` file found! 16 | * 17 | * This generated code has permissive types like `Doc = any` because 18 | * Convex doesn't know your schema. If you'd like more type safety, see 19 | * https://docs.convex.dev/using/schemas for instructions on how to add a 20 | * schema file. 21 | * 22 | * After you change a schema, rerun codegen with `npx convex dev`. 23 | */ 24 | 25 | /** 26 | * The names of all of your Convex tables. 27 | */ 28 | export type TableNames = string; 29 | 30 | /** 31 | * The type of a document stored in Convex. 32 | */ 33 | export type Doc = any; 34 | 35 | /** 36 | * An identifier for a document in Convex. 37 | * 38 | * Convex documents are uniquely identified by their `Id`, which is accessible 39 | * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). 40 | * 41 | * Documents can be loaded using `db.get(id)` in query and mutation functions. 42 | * 43 | * IDs are just strings at runtime, but this type can be used to distinguish them from other 44 | * strings when type checking. 45 | */ 46 | export type Id = 47 | GenericId; 48 | 49 | /** 50 | * A type describing your Convex data model. 51 | * 52 | * This type includes information about what tables you have, the type of 53 | * documents stored in those tables, and the indexes defined on them. 54 | * 55 | * This type is used to parameterize methods like `queryGeneric` and 56 | * `mutationGeneric` to make them type-safe. 57 | */ 58 | export type DataModel = AnyDataModel; 59 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import type { ConvexClientOptions } from 'convex/browser' 2 | import type { ObjectPlugin, Ref } from 'vue' 3 | import { ConvexClient, ConvexHttpClient } from 'convex/browser' 4 | import { shallowRef } from 'vue' 5 | 6 | export interface ConvexAuthOptions { 7 | forceRefreshToken: boolean 8 | } 9 | 10 | export interface ConvexVueOptions { 11 | url: string 12 | clientOptions?: ConvexClientOptions 13 | auth?: ConvexAuthOptions 14 | 15 | /** 16 | * If true, the global default client wont be initialized automatically, 17 | * you will need to init the client yourself before using the composables. 18 | */ 19 | manualInit?: boolean 20 | /** 21 | * Set to `false` to disable queries during server-side rendering. 22 | * This global option can be overridden for individual queries by setting their `server` option to `true` 23 | */ 24 | server?: boolean 25 | } 26 | 27 | export interface ConvexVueContext { 28 | options: ConvexVueOptions 29 | clientRef: Ref 30 | httpClientRef: Ref 31 | 32 | /** 33 | * (Re-)init the global convex client with specified options. 34 | */ 35 | initClient: (options?: ConvexVueOptions) => void 36 | } 37 | 38 | export const convexVue: ObjectPlugin = { 39 | install(app, initialOptions) { 40 | const clientRef = shallowRef() 41 | const httpClientRef = shallowRef() 42 | 43 | const initClient = (options?: ConvexVueOptions): void => { 44 | options ??= initialOptions 45 | clientRef.value = new ConvexClient(options.url, options.clientOptions) 46 | httpClientRef.value = new ConvexHttpClient(options.url, { 47 | logger: options?.clientOptions?.logger, 48 | skipConvexDeploymentUrlCheck: options.clientOptions?.skipConvexDeploymentUrlCheck, 49 | }) 50 | } 51 | 52 | if (!initialOptions.manualInit) 53 | initClient(initialOptions) 54 | 55 | app.provide('convex-vue', { 56 | options: initialOptions, 57 | clientRef, 58 | httpClientRef, 59 | initClient, 60 | }) 61 | }, 62 | } 63 | -------------------------------------------------------------------------------- /src/composables/useConvexQuery/lib/useServerQuery.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionReference, FunctionReturnType } from 'convex/server' 2 | import type { UseConvexQueryOptions, UseConvexQueryReturn } from '../types.ts' 3 | import { useConvexContext } from '#src/composables/useConvexContext.ts' 4 | import { useConvexHttpClient } from '#src/composables/useConvexHttpClient.ts' 5 | import { computed, ref } from 'vue' 6 | 7 | /** 8 | * A composable that provides the server-side implementation of a Convex query. The output equals that of 9 | * `useConvexQuery`, but it does not support reactivity. It should only be used on the server side. 10 | */ 11 | export function useServerQuery>(query: Query, args: Query['_args'], options?: UseConvexQueryOptions): UseConvexQueryReturn { 12 | const convex = useConvexHttpClient() 13 | const convexContext = useConvexContext() 14 | 15 | const isServer = typeof window === 'undefined' 16 | 17 | if (!isServer) { 18 | throw new Error('useServerQuery should only be called on the server side') 19 | } 20 | 21 | const isServerDisabled = options?.server ?? convexContext.options.server ?? true 22 | 23 | if (isServerDisabled) { 24 | return { 25 | data: ref(undefined), 26 | error: ref(null), 27 | isPending: ref(false), 28 | suspense: () => Promise.resolve(undefined), 29 | refetch: () => Promise.resolve(undefined), 30 | } 31 | } 32 | 33 | const data = ref | undefined>(undefined) 34 | const error = ref(null) 35 | 36 | const executeQuery = () => { 37 | return convex.query(query, args.value).then((result) => { 38 | data.value = result 39 | error.value = null 40 | return result 41 | }).catch((err) => { 42 | data.value = undefined 43 | error.value = err 44 | throw err 45 | }) 46 | } 47 | 48 | // no need to watch queryArgs on server since reactivity is disabled during ssr 49 | const promise = executeQuery() 50 | 51 | return { 52 | data, 53 | error, 54 | isPending: computed(() => data.value === undefined), 55 | suspense: () => promise, 56 | refetch: executeQuery, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convex-vue", 3 | "type": "module", 4 | "version": "0.1.5", 5 | "packageManager": "pnpm@10.11.0", 6 | "description": "Convex integration for Vue", 7 | "author": "chris-visser", 8 | "license": "MIT", 9 | "funding": "https://github.com/sponsors/chris-visser", 10 | "homepage": "https://github.com/chris-visser/convex-vue#readme", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/chris-visser/convex-vue.git" 14 | }, 15 | "bugs": "https://github.com/chris-visser/convex-vue/issues", 16 | "keywords": [ 17 | "vue", 18 | "convex" 19 | ], 20 | "sideEffects": false, 21 | "imports": { 22 | "#src/*": "./src/*" 23 | }, 24 | "exports": { 25 | ".": { 26 | "types": "./dist/index.d.mts", 27 | "import": "./dist/index.mjs" 28 | } 29 | }, 30 | "main": "./dist/index.mjs", 31 | "source": "./src/index.ts", 32 | "files": [ 33 | "dist" 34 | ], 35 | "engines": { 36 | "node": ">=22.14.0" 37 | }, 38 | "scripts": { 39 | "lint": "eslint .", 40 | "test": "vitest", 41 | "test:types": "tsc --noEmit", 42 | "quickcheck": "pnpm run lint && pnpm run test:types", 43 | "check": "pnpm run lint && pnpm run test:types", 44 | "stub": "obuild --stub", 45 | "build": "obuild", 46 | "prerelease": "pnpm run check", 47 | "release": "pnpm dlx changelogen@latest --release --push --publish", 48 | "prepare": "simple-git-hooks", 49 | "prepublishOnly": "pnpm run build" 50 | }, 51 | "peerDependencies": { 52 | "convex": ">=1.24.0", 53 | "vue": ">=3.5.0" 54 | }, 55 | "dependencies": { 56 | "consola": "^3.4.2", 57 | "std-env": "^3.9.0" 58 | }, 59 | "devDependencies": { 60 | "@antfu/eslint-config": "^4.14.1", 61 | "@types/node": "^22.15.32", 62 | "@vitest/coverage-v8": "^3.2.3", 63 | "@vue/tsconfig": "^0.7.0", 64 | "eslint": "^9.29.0", 65 | "lint-staged": "^16.1.2", 66 | "obuild": "^0.1.1", 67 | "simple-git-hooks": "^2.13.0", 68 | "tsx": "^4.20.3", 69 | "typescript": "^5.8.3", 70 | "vitest": "^3.2.3" 71 | }, 72 | "pnpm": { 73 | "overrides": { 74 | "is-core-module": "npm:@nolyfill/is-core-module@^1.0.39" 75 | }, 76 | "onlyBuiltDependencies": [ 77 | "esbuild", 78 | "simple-git-hooks", 79 | "unrs-resolver" 80 | ] 81 | }, 82 | "simple-git-hooks": { 83 | "pre-commit": "pnpm lint-staged" 84 | }, 85 | "lint-staged": { 86 | "*": "eslint --fix" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /playground/convex/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Convex functions directory! 2 | 3 | Write your Convex functions here. 4 | See https://docs.convex.dev/functions for more. 5 | 6 | A query function that takes two arguments looks like: 7 | 8 | ```ts 9 | // functions.js 10 | import { v } from 'convex/values' 11 | import { query } from './_generated/server' 12 | 13 | export const myQueryFunction = query({ 14 | // Validators for arguments. 15 | args: { 16 | first: v.number(), 17 | second: v.string(), 18 | }, 19 | 20 | // Function implementation. 21 | handler: async (ctx, args) => { 22 | // Read the database as many times as you need here. 23 | // See https://docs.convex.dev/database/reading-data. 24 | const documents = await ctx.db.query('tablename').collect() 25 | 26 | // Arguments passed from the client are properties of the args object. 27 | console.log(args.first, args.second) 28 | 29 | // Write arbitrary JavaScript here: filter, aggregate, build derived data, 30 | // remove non-public properties, or create new objects. 31 | return documents 32 | }, 33 | }) 34 | ``` 35 | 36 | Using this query function in a React component looks like: 37 | 38 | ```ts 39 | const data = useQuery(api.functions.myQueryFunction, { 40 | first: 10, 41 | second: 'hello', 42 | }) 43 | ``` 44 | 45 | A mutation function looks like: 46 | 47 | ```ts 48 | // functions.js 49 | import { v } from 'convex/values' 50 | import { mutation } from './_generated/server' 51 | 52 | export const myMutationFunction = mutation({ 53 | // Validators for arguments. 54 | args: { 55 | first: v.string(), 56 | second: v.string(), 57 | }, 58 | 59 | // Function implementation. 60 | handler: async (ctx, args) => { 61 | // Insert or modify documents in the database here. 62 | // Mutations can also read from the database like queries. 63 | // See https://docs.convex.dev/database/writing-data. 64 | const message = { body: args.first, author: args.second } 65 | const id = await ctx.db.insert('messages', message) 66 | 67 | // Optionally, return a value from your mutation. 68 | return await ctx.db.get(id) 69 | }, 70 | }) 71 | ``` 72 | 73 | Using this mutation function in a React component looks like: 74 | 75 | ```ts 76 | const mutation = useMutation(api.functions.myMutationFunction) 77 | function handleButtonPress() { 78 | // fire and forget, the most common way to use mutations 79 | mutation({ first: 'Hello!', second: 'me' }) 80 | // OR 81 | // use the result once the mutation has completed 82 | mutation({ first: 'Hello!', second: 'me' }).then(result => 83 | console.log(result), 84 | ) 85 | } 86 | ``` 87 | 88 | Use the Convex CLI to push your functions to a deployment. See everything 89 | the Convex CLI can do by running `npx convex -h` in your project root 90 | directory. To learn more, launch the docs with `npx convex docs`. 91 | -------------------------------------------------------------------------------- /playground/src/components/TaskList.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 104 | 105 | 133 | -------------------------------------------------------------------------------- /src/composables/useConvexQuery/index.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server' 2 | import type { Ref } from 'vue' 3 | import type { OptionalRestArgsAndOptions } from '../../types.ts' 4 | import type { UseConvexQueryOptions, UseConvexQueryReturn } from './types.ts' 5 | import { 6 | getFunctionName, 7 | } from 'convex/server' 8 | import { computed, onScopeDispose, ref, watch } from 'vue' 9 | import { useConvexClient } from '../useConvexClient' 10 | import { useQueryArgs } from './lib/useQueryArgs.ts' 11 | import { useServerQuery } from './lib/useServerQuery.ts' 12 | 13 | /** 14 | * A composable that provides a Realtime Convex query. It supports reactivity and can be used both on the client and server side. 15 | * @param query The Convex query function. 16 | * @param rest The arguments and options for the query. 17 | * @returns The result of the query. 18 | */ 19 | export function useConvexQuery>(query: Query, ...rest: OptionalRestArgsAndOptions): UseConvexQueryReturn { 20 | const { args, options } = useQueryArgs(rest) 21 | 22 | const isServer = typeof window === 'undefined' 23 | 24 | // use http client on server-side 25 | if (isServer) { 26 | return useServerQuery(query, args, options) 27 | } 28 | 29 | const convex = useConvexClient() 30 | 31 | // Initial data 32 | const data: Ref | undefined> = ref | undefined>(convex.client.localQueryResult(getFunctionName(query), args.value)) 33 | const error = ref(null) 34 | 35 | const suspense = () => { 36 | if (data.value) { 37 | return Promise.resolve(data.value) 38 | } 39 | if (error.value) { 40 | return Promise.reject(error.value) 41 | } 42 | 43 | return new Promise>((resolve, reject) => { 44 | const stop = watch( 45 | () => [data.value, error.value], 46 | ([newData, newError]) => { 47 | if (newData) { 48 | stop() 49 | resolve(newData) 50 | } 51 | else if (newError) { 52 | stop() 53 | reject(newError) 54 | } 55 | }, 56 | { immediate: true }, 57 | ) 58 | }) 59 | } 60 | 61 | const handleError = (err: Error) => { 62 | data.value = null 63 | error.value = err 64 | } 65 | 66 | const handleResult = (result: FunctionReturnType) => { 67 | data.value = result 68 | error.value = null 69 | } 70 | 71 | const refetch = async () => { 72 | try { 73 | const result = await convex.query(query, args.value) 74 | handleResult(result) 75 | return result 76 | } 77 | catch (err) { 78 | const error_ = err instanceof Error ? err : new Error('Unknown error occurred') 79 | handleError(error_) 80 | throw error_ 81 | } 82 | } 83 | 84 | const createSubscription = (args: FunctionArgs) => { 85 | return convex.onUpdate( 86 | query, 87 | args, 88 | handleResult, 89 | handleError, 90 | ) 91 | } 92 | 93 | // recreate subscription when args change 94 | let cancelSubscription: () => void | undefined 95 | watch(args, (newArgs) => { 96 | cancelSubscription?.() 97 | 98 | cancelSubscription = createSubscription(newArgs) 99 | }, { 100 | immediate: true, 101 | }) 102 | 103 | // cleanup subscription when component is unmounted 104 | onScopeDispose(() => cancelSubscription?.()) 105 | 106 | return { 107 | data, 108 | error, 109 | isPending: computed(() => data.value === undefined), 110 | suspense, 111 | refetch, 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /playground/convex/_generated/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated utilities for implementing server-side Convex query and mutation functions. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { 12 | actionGeneric, 13 | httpActionGeneric, 14 | queryGeneric, 15 | mutationGeneric, 16 | internalActionGeneric, 17 | internalMutationGeneric, 18 | internalQueryGeneric, 19 | } from "convex/server"; 20 | 21 | /** 22 | * Define a query in this Convex app's public API. 23 | * 24 | * This function will be allowed to read your Convex database and will be accessible from the client. 25 | * 26 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 27 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 28 | */ 29 | export const query = queryGeneric; 30 | 31 | /** 32 | * Define a query that is only accessible from other Convex functions (but not from the client). 33 | * 34 | * This function will be allowed to read from your Convex database. It will not be accessible from the client. 35 | * 36 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 37 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 38 | */ 39 | export const internalQuery = internalQueryGeneric; 40 | 41 | /** 42 | * Define a mutation in this Convex app's public API. 43 | * 44 | * This function will be allowed to modify your Convex database and will be accessible from the client. 45 | * 46 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 47 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 48 | */ 49 | export const mutation = mutationGeneric; 50 | 51 | /** 52 | * Define a mutation that is only accessible from other Convex functions (but not from the client). 53 | * 54 | * This function will be allowed to modify your Convex database. It will not be accessible from the client. 55 | * 56 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 57 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 58 | */ 59 | export const internalMutation = internalMutationGeneric; 60 | 61 | /** 62 | * Define an action in this Convex app's public API. 63 | * 64 | * An action is a function which can execute any JavaScript code, including non-deterministic 65 | * code and code with side-effects, like calling third-party services. 66 | * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. 67 | * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. 68 | * 69 | * @param func - The action. It receives an {@link ActionCtx} as its first argument. 70 | * @returns The wrapped action. Include this as an `export` to name it and make it accessible. 71 | */ 72 | export const action = actionGeneric; 73 | 74 | /** 75 | * Define an action that is only accessible from other Convex functions (but not from the client). 76 | * 77 | * @param func - The function. It receives an {@link ActionCtx} as its first argument. 78 | * @returns The wrapped function. Include this as an `export` to name it and make it accessible. 79 | */ 80 | export const internalAction = internalActionGeneric; 81 | 82 | /** 83 | * Define a Convex HTTP action. 84 | * 85 | * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object 86 | * as its second. 87 | * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`. 88 | */ 89 | export const httpAction = httpActionGeneric; 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Convex Vue

4 | 5 |

Convex integration for Vue!

6 | 7 | 8 |
9 | 10 | # convex-vue ![TypeScript heart icon](https://img.shields.io/badge/♡-%23007ACC.svg?logo=typescript&logoColor=white) 11 | 12 | [![npm version][npm-version-src]][npm-version-href] 13 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 14 | [![Codecov][codecov-src]][codecov-href] 15 | [![Bundlejs][bundlejs-src]][bundlejs-href] 16 | 17 | * [convex-vue ](#convex-vue-) 18 | * [Overview](#overview) 19 | * [Features](#features) 20 | * [Usage](#usage) 21 | * [Install package](#install-package) 22 | * [Import and use](#import-and-use) 23 | * [Composables](#composables) 24 | * [useConvexClient](#useconvexclient) 25 | * [useConvexQuery](#useconvexquery) 26 | * [useConvexMutation](#useconvexmutation) 27 | * [Suspense and SSR Support](#suspense-and-ssr-support) 28 | * [License](#license) 29 | 30 | ## Overview 31 | 32 | **convex-vue** is a Vue.js integration library for [Convex](https://convex.dev) - the backend application platform with a built-in database. 33 | 34 | ## Features 35 | 36 | + 👌 Supports Convex realtime queries 37 | + 🔄️ SSR and SSG support via suspense 38 | + 🎉 Optimistic updates for mutations 39 | 40 | ## Usage 41 | 42 | ### Install package 43 | 44 | ```sh 45 | # npm 46 | npm install convex-vue 47 | 48 | # bun 49 | bun add convex-vue 50 | 51 | # pnpm 52 | pnpm install convex-vue 53 | ``` 54 | 55 | ### Import and use 56 | 57 | Convex Vue is a Vue 3 plugin. Simply add it to your Vue app and use the provided composables. 58 | 59 | ```js 60 | import { convexVue } from 'convex-vue' 61 | import { createApp } from 'vue' 62 | import App from './App.vue' 63 | 64 | const app = createApp(App) 65 | 66 | app.use(convexVue, { 67 | url: 'your-convex-deployment-url' 68 | }) 69 | 70 | app.mount('#app') 71 | ``` 72 | 73 | ### Composables 74 | 75 | #### useConvexClient 76 | 77 | The `useConvexClient` composable provides access to the Convex client instance. You can use it to call Convex functions directly or implement custom logic. 78 | 79 | ```js 80 | import { useConvexClient } from 'convex-vue' 81 | 82 | // In your component 83 | const client = useConvexClient() 84 | ``` 85 | 86 | #### useConvexQuery 87 | 88 | The `useConvexQuery` composable is used to fetch data from Convex. It automatically handles subscriptions and reactivity, so your components will update in real time when the data changes. 89 | On the server, it will trigger a standard query (without subscription). 90 | 91 | ```js 92 | import { useConvexQuery } from 'convex-vue' 93 | import { api } from '../convex/_generated/api' 94 | 95 | // In your component or composable 96 | const { data, isPending, error } = useConvexQuery(api.myModule.myQuery, { param: 'value' }) 97 | ``` 98 | 99 | #### useConvexMutation 100 | 101 | The `useConvexMutation` composable is used to call Convex mutations. It provides a function that you can call to trigger the mutation, and it automatically handles loading and error states. 102 | 103 | ```js 104 | import { useConvexMutation } from 'convex-vue' 105 | import { api } from '../convex/_generated/api' 106 | // In your component or composable 107 | const { mutate, isPending, error } = useConvexMutation(api.myModule.myMutation) 108 | 109 | async function handleClick() { 110 | // mutate returns a promise with an object that contains a result or error property 111 | const { result, error: mutationError } = mutate({ param: 'value' }) 112 | } 113 | ``` 114 | 115 | #### Suspense and SSR Support 116 | 117 | By default when using `useConvexQuery`, the data property will be undefined until the query is complete. 118 | By using the suspense function, you can await the result of the query. This is useful for server-side rendering (SSR) 119 | and Static Site Generation (SSG) where you want to wait for the query to complete before rendering the component. 120 | 121 | Convex subscriptions on the server are not supported. `useConvexQuery` therefore triggers a standard query. 122 | If you await the suspense function, it will resolve when the query is complete or reject with an error. 123 | 124 | ```tsx 125 | // In your component or composable 126 | const { data, suspense } = useConvexQuery(api.myModule.myQuery, { param: 'value' }) 127 | 128 | const result = await suspense() 129 | // Now you can use the data or result 130 | console.log(data) 131 | console.log(result) 132 | ``` 133 | 134 | Either use the result of the suspense function like below, or simply use the data property. 135 | The suspense function will only return the result once on server and client (like a normal promise). 136 | The data property will be reactive and update when the query result changes on the client. 137 | Recommended to use the data property for SSR support and realtime clientside updates. 138 | 139 | ## License 140 | 141 | [![License][license-src]][license-href] 142 | 143 | 144 | 145 | [npm-version-src]: https://img.shields.io/npm/v/convex-vue?labelColor=18181B&color=F0DB4F 146 | [npm-version-href]: https://npmjs.com/package/convex-vue 147 | [npm-downloads-src]: https://img.shields.io/npm/dm/convex-vue?labelColor=18181B&color=F0DB4F 148 | [npm-downloads-href]: https://npmjs.com/package/convex-vue 149 | [codecov-src]: https://img.shields.io/codecov/c/gh/chris-visser/convex-vue/main?labelColor=18181B&color=F0DB4F 150 | [codecov-href]: https://codecov.io/gh/chris-visser/convex-vue 151 | [license-src]: https://img.shields.io/github/license/chris-visser/convex-vue.svg?labelColor=18181B&color=F0DB4F 152 | [license-href]: https://github.com/chris-visser/convex-vue/blob/main/LICENSE 153 | [bundlejs-src]: https://img.shields.io/bundlejs/size/convex-vue?labelColor=18181B&color=F0DB4F 154 | [bundlejs-href]: https://bundlejs.com/?q=convex-vue 155 | -------------------------------------------------------------------------------- /playground/convex/_generated/server.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated utilities for implementing server-side Convex query and mutation functions. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { 12 | ActionBuilder, 13 | HttpActionBuilder, 14 | MutationBuilder, 15 | QueryBuilder, 16 | GenericActionCtx, 17 | GenericMutationCtx, 18 | GenericQueryCtx, 19 | GenericDatabaseReader, 20 | GenericDatabaseWriter, 21 | } from "convex/server"; 22 | import type { DataModel } from "./dataModel.js"; 23 | 24 | /** 25 | * Define a query in this Convex app's public API. 26 | * 27 | * This function will be allowed to read your Convex database and will be accessible from the client. 28 | * 29 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 30 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 31 | */ 32 | export declare const query: QueryBuilder; 33 | 34 | /** 35 | * Define a query that is only accessible from other Convex functions (but not from the client). 36 | * 37 | * This function will be allowed to read from your Convex database. It will not be accessible from the client. 38 | * 39 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 40 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 41 | */ 42 | export declare const internalQuery: QueryBuilder; 43 | 44 | /** 45 | * Define a mutation in this Convex app's public API. 46 | * 47 | * This function will be allowed to modify your Convex database and will be accessible from the client. 48 | * 49 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 50 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 51 | */ 52 | export declare const mutation: MutationBuilder; 53 | 54 | /** 55 | * Define a mutation that is only accessible from other Convex functions (but not from the client). 56 | * 57 | * This function will be allowed to modify your Convex database. It will not be accessible from the client. 58 | * 59 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 60 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 61 | */ 62 | export declare const internalMutation: MutationBuilder; 63 | 64 | /** 65 | * Define an action in this Convex app's public API. 66 | * 67 | * An action is a function which can execute any JavaScript code, including non-deterministic 68 | * code and code with side-effects, like calling third-party services. 69 | * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. 70 | * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. 71 | * 72 | * @param func - The action. It receives an {@link ActionCtx} as its first argument. 73 | * @returns The wrapped action. Include this as an `export` to name it and make it accessible. 74 | */ 75 | export declare const action: ActionBuilder; 76 | 77 | /** 78 | * Define an action that is only accessible from other Convex functions (but not from the client). 79 | * 80 | * @param func - The function. It receives an {@link ActionCtx} as its first argument. 81 | * @returns The wrapped function. Include this as an `export` to name it and make it accessible. 82 | */ 83 | export declare const internalAction: ActionBuilder; 84 | 85 | /** 86 | * Define an HTTP action. 87 | * 88 | * This function will be used to respond to HTTP requests received by a Convex 89 | * deployment if the requests matches the path and method where this action 90 | * is routed. Be sure to route your action in `convex/http.js`. 91 | * 92 | * @param func - The function. It receives an {@link ActionCtx} as its first argument. 93 | * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. 94 | */ 95 | export declare const httpAction: HttpActionBuilder; 96 | 97 | /** 98 | * A set of services for use within Convex query functions. 99 | * 100 | * The query context is passed as the first argument to any Convex query 101 | * function run on the server. 102 | * 103 | * This differs from the {@link MutationCtx} because all of the services are 104 | * read-only. 105 | */ 106 | export type QueryCtx = GenericQueryCtx; 107 | 108 | /** 109 | * A set of services for use within Convex mutation functions. 110 | * 111 | * The mutation context is passed as the first argument to any Convex mutation 112 | * function run on the server. 113 | */ 114 | export type MutationCtx = GenericMutationCtx; 115 | 116 | /** 117 | * A set of services for use within Convex action functions. 118 | * 119 | * The action context is passed as the first argument to any Convex action 120 | * function run on the server. 121 | */ 122 | export type ActionCtx = GenericActionCtx; 123 | 124 | /** 125 | * An interface to read from the database within Convex query functions. 126 | * 127 | * The two entry points are {@link DatabaseReader.get}, which fetches a single 128 | * document by its {@link Id}, or {@link DatabaseReader.query}, which starts 129 | * building a query. 130 | */ 131 | export type DatabaseReader = GenericDatabaseReader; 132 | 133 | /** 134 | * An interface to read from and write to the database within Convex mutation 135 | * functions. 136 | * 137 | * Convex guarantees that all writes within a single mutation are 138 | * executed atomically, so you never have to worry about partial writes leaving 139 | * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) 140 | * for the guarantees Convex provides your functions. 141 | */ 142 | export type DatabaseWriter = GenericDatabaseWriter; 143 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v0.1.5 5 | 6 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.1.4...v0.1.5) 7 | 8 | ### 🌊 Types 9 | 10 | - Fix duplicated type and better naming ([bfe18cb](https://github.com/chris-visser/convex-vue/commit/bfe18cb)) 11 | 12 | ### ❤️ Contributors 13 | 14 | - NamesMT ([@NamesMT](https://github.com/NamesMT)) 15 | 16 | ## v0.1.4 17 | 18 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.1.3...v0.1.4) 19 | 20 | ### 🏡 Chore 21 | 22 | - Revert `initClient` options interface ([0e632fb](https://github.com/chris-visser/convex-vue/commit/0e632fb)) 23 | 24 | ### ❤️ Contributors 25 | 26 | - NamesMT ([@NamesMT](https://github.com/NamesMT)) 27 | 28 | ## v0.1.3 29 | 30 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.1.2...v0.1.3) 31 | 32 | ## v0.1.2 33 | 34 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.1.1...v0.1.2) 35 | 36 | ### 🚀 Enhancements 37 | 38 | - Use http client on server ([af631c0](https://github.com/chris-visser/convex-vue/commit/af631c0)) 39 | - Option to disable queries on server ([e51513c](https://github.com/chris-visser/convex-vue/commit/e51513c)) 40 | 41 | ### 🌊 Types 42 | 43 | - **useConvexQuery:** Improve condition, only require `args` input when necessary ([7ec72f8](https://github.com/chris-visser/convex-vue/commit/7ec72f8)) 44 | 45 | ### 🏡 Chore 46 | 47 | - **release:** V0.1.1 ([70900ef](https://github.com/chris-visser/convex-vue/commit/70900ef)) 48 | 49 | ### ❤️ Contributors 50 | 51 | - Louis Haftmann ([@LouisHaftmann](https://github.com/LouisHaftmann)) 52 | - NamesMT ([@NamesMT](https://github.com/NamesMT)) 53 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 54 | 55 | ## v0.1.1 56 | 57 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.1.0...v0.1.1) 58 | 59 | ### 🩹 Fixes 60 | 61 | - Add `undefined` to data return type of `useConvexQuery` ([77ffae5](https://github.com/chris-visser/convex-vue/commit/77ffae5)) 62 | - Reactive query args & scope cleanup ([963e54f](https://github.com/chris-visser/convex-vue/commit/963e54f)) 63 | 64 | ### 📖 Documentation 65 | 66 | - Update readme for optimistic updates ([97c1985](https://github.com/chris-visser/convex-vue/commit/97c1985)) 67 | 68 | ### ❤️ Contributors 69 | 70 | - Louis Haftmann ([@LouisHaftmann](https://github.com/LouisHaftmann)) 71 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 72 | 73 | ## v0.1.0 74 | 75 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.0.5...v0.1.0) 76 | 77 | ### 🚀 Enhancements 78 | 79 | - Support auth ([8fb4c46](https://github.com/chris-visser/convex-vue/commit/8fb4c46)) 80 | - Add ability to manually and re-init the global client ([d1963e2](https://github.com/chris-visser/convex-vue/commit/d1963e2)) 81 | - Support optimistic updates ([dd207b1](https://github.com/chris-visser/convex-vue/commit/dd207b1)) 82 | 83 | ### 💅 Refactors 84 | 85 | - Tiny code style fix ([ab32971](https://github.com/chris-visser/convex-vue/commit/ab32971)) 86 | - Enforce consistent code style ([85e0f21](https://github.com/chris-visser/convex-vue/commit/85e0f21)) 87 | - ⚠️ Major codebase refactor + various fixes ([cf80a47](https://github.com/chris-visser/convex-vue/commit/cf80a47)) 88 | - ⚠️ UseContextMutation ([bb1eeab](https://github.com/chris-visser/convex-vue/commit/bb1eeab)) 89 | 90 | ### 🏡 Chore 91 | 92 | - Set `packageManager` ([ba7d764](https://github.com/chris-visser/convex-vue/commit/ba7d764)) 93 | - Remove unintended log ([2f93a93](https://github.com/chris-visser/convex-vue/commit/2f93a93)) 94 | - ⚠️ Change inject naming to `convex-vue` ([5d27a8a](https://github.com/chris-visser/convex-vue/commit/5d27a8a)) 95 | - Post-restructure corrections ([adc33c9](https://github.com/chris-visser/convex-vue/commit/adc33c9)) 96 | - ⚠️ Rename `isLoading` => `isPending` ([cf526c8](https://github.com/chris-visser/convex-vue/commit/cf526c8)) 97 | 98 | #### ⚠️ Breaking Changes 99 | 100 | - ⚠️ Major codebase refactor + various fixes ([cf80a47](https://github.com/chris-visser/convex-vue/commit/cf80a47)) 101 | - ⚠️ UseContextMutation ([bb1eeab](https://github.com/chris-visser/convex-vue/commit/bb1eeab)) 102 | - ⚠️ Change inject naming to `convex-vue` ([5d27a8a](https://github.com/chris-visser/convex-vue/commit/5d27a8a)) 103 | - ⚠️ Rename `isLoading` => `isPending` ([cf526c8](https://github.com/chris-visser/convex-vue/commit/cf526c8)) 104 | 105 | ### ❤️ Contributors 106 | 107 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 108 | - NamesMT ([@NamesMT](https://github.com/NamesMT)) 109 | - Trung Dang ([@NamesMT](https://github.com/NamesMT)) 110 | 111 | ## v0.0.6 112 | 113 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.0.5...v0.2.0) 114 | 115 | ### 🚀 Enhancements 116 | 117 | - Support auth ([8fb4c46](https://github.com/chris-visser/convex-vue/commit/8fb4c46)) 118 | - Add ability to manually and re-init the global client ([d1963e2](https://github.com/chris-visser/convex-vue/commit/d1963e2)) 119 | - Support optimistic updates ([0946c6e](https://github.com/chris-visser/convex-vue/commit/0946c6e)) 120 | 121 | ### 💅 Refactors 122 | 123 | - Tiny code style fix ([ab32971](https://github.com/chris-visser/convex-vue/commit/ab32971)) 124 | - Enforce consistent code style ([85e0f21](https://github.com/chris-visser/convex-vue/commit/85e0f21)) 125 | - ⚠️ Major codebase refactor + various fixes ([cf80a47](https://github.com/chris-visser/convex-vue/commit/cf80a47)) 126 | - ⚠️ UseContextMutation ([bb1eeab](https://github.com/chris-visser/convex-vue/commit/bb1eeab)) 127 | 128 | ### 🏡 Chore 129 | 130 | - Set `packageManager` ([ba7d764](https://github.com/chris-visser/convex-vue/commit/ba7d764)) 131 | - Remove unintended log ([2f93a93](https://github.com/chris-visser/convex-vue/commit/2f93a93)) 132 | - ⚠️ Change inject naming to `convex-vue` ([5d27a8a](https://github.com/chris-visser/convex-vue/commit/5d27a8a)) 133 | - Post-restructure corrections ([adc33c9](https://github.com/chris-visser/convex-vue/commit/adc33c9)) 134 | - ⚠️ Rename `isLoading` => `isPending` ([cf526c8](https://github.com/chris-visser/convex-vue/commit/cf526c8)) 135 | - Minor package bumps ([ec581fb](https://github.com/chris-visser/convex-vue/commit/ec581fb)) 136 | 137 | #### ⚠️ Breaking Changes 138 | 139 | - ⚠️ Major codebase refactor + various fixes ([cf80a47](https://github.com/chris-visser/convex-vue/commit/cf80a47)) 140 | - ⚠️ UseContextMutation ([bb1eeab](https://github.com/chris-visser/convex-vue/commit/bb1eeab)) 141 | - ⚠️ Change inject naming to `convex-vue` ([5d27a8a](https://github.com/chris-visser/convex-vue/commit/5d27a8a)) 142 | - ⚠️ Rename `isLoading` => `isPending` ([cf526c8](https://github.com/chris-visser/convex-vue/commit/cf526c8)) 143 | 144 | ### ❤️ Contributors 145 | 146 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 147 | - NamesMT ([@NamesMT](https://github.com/NamesMT)) 148 | - Trung Dang ([@NamesMT](https://github.com/NamesMT)) 149 | 150 | ## v0.0.5 151 | 152 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.0.3...v0.0.5) 153 | 154 | ### 🚀 Enhancements 155 | 156 | - Return isLoading and error refs on mutations ([72b5f65](https://github.com/chris-visser/convex-vue/commit/72b5f65)) 157 | 158 | ### 🩹 Fixes 159 | 160 | - Suspense infinite loading on server ([43de07c](https://github.com/chris-visser/convex-vue/commit/43de07c)) 161 | 162 | ### 🏡 Chore 163 | 164 | - **release:** V0.0.4 ([631dab2](https://github.com/chris-visser/convex-vue/commit/631dab2)) 165 | 166 | ### ❤️ Contributors 167 | 168 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 169 | 170 | ## v0.0.4 171 | 172 | [compare changes](https://github.com/chris-visser/convex-vue/compare/v0.0.3...v0.0.4) 173 | 174 | ### 🚀 Enhancements 175 | 176 | - Return isLoading and error refs on mutations ([72b5f65](https://github.com/chris-visser/convex-vue/commit/72b5f65)) 177 | 178 | ### ❤️ Contributors 179 | 180 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 181 | 182 | ## v0.0.3 183 | 184 | 185 | ### 🚀 Enhancements 186 | 187 | - Support for basic mutations ([a4ff3eb](https://github.com/chris-visser/convex-vue/commit/a4ff3eb)) 188 | 189 | ### 🏡 Chore 190 | 191 | - Add Readme ([3f90d64](https://github.com/chris-visser/convex-vue/commit/3f90d64)) 192 | - **release:** V0.0.1 ([9254097](https://github.com/chris-visser/convex-vue/commit/9254097)) 193 | - Sets up package ci/cd for publishing to npm ([d29b22b](https://github.com/chris-visser/convex-vue/commit/d29b22b)) 194 | 195 | ### ❤️ Contributors 196 | 197 | - Chris Visser ([@chris-visser](https://github.com/chris-visser)) 198 | 199 | ## v0.0.1 200 | 201 | - Initial release 202 | 203 | -------------------------------------------------------------------------------- /playground/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | convex: 9 | specifier: ^1.24.1 10 | version: 1.24.8 11 | vue: 12 | specifier: ^3.5.13 13 | version: 3.5.16(typescript@5.8.3) 14 | 15 | devDependencies: 16 | '@types/node': 17 | specifier: ^22.15.17 18 | version: 22.15.32 19 | '@vitejs/plugin-vue': 20 | specifier: ^5.2.3 21 | version: 5.2.4(vite@6.3.5)(vue@3.5.16) 22 | '@vue/tsconfig': 23 | specifier: ^0.7.0 24 | version: 0.7.0(typescript@5.8.3)(vue@3.5.16) 25 | typescript: 26 | specifier: ~5.8.3 27 | version: 5.8.3 28 | vite: 29 | specifier: ^6.3.5 30 | version: 6.3.5(@types/node@22.15.32) 31 | vue-tsc: 32 | specifier: ^2.2.8 33 | version: 2.2.10(typescript@5.8.3) 34 | 35 | packages: 36 | 37 | /@babel/helper-string-parser@7.27.1: 38 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 39 | engines: {node: '>=6.9.0'} 40 | 41 | /@babel/helper-validator-identifier@7.27.1: 42 | resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 43 | engines: {node: '>=6.9.0'} 44 | 45 | /@babel/parser@7.27.5: 46 | resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} 47 | engines: {node: '>=6.0.0'} 48 | hasBin: true 49 | dependencies: 50 | '@babel/types': 7.27.6 51 | 52 | /@babel/types@7.27.6: 53 | resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} 54 | engines: {node: '>=6.9.0'} 55 | dependencies: 56 | '@babel/helper-string-parser': 7.27.1 57 | '@babel/helper-validator-identifier': 7.27.1 58 | 59 | /@esbuild/aix-ppc64@0.25.2: 60 | resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} 61 | engines: {node: '>=18'} 62 | cpu: [ppc64] 63 | os: [aix] 64 | requiresBuild: true 65 | dev: false 66 | optional: true 67 | 68 | /@esbuild/aix-ppc64@0.25.5: 69 | resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} 70 | engines: {node: '>=18'} 71 | cpu: [ppc64] 72 | os: [aix] 73 | requiresBuild: true 74 | dev: true 75 | optional: true 76 | 77 | /@esbuild/android-arm64@0.25.2: 78 | resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} 79 | engines: {node: '>=18'} 80 | cpu: [arm64] 81 | os: [android] 82 | requiresBuild: true 83 | dev: false 84 | optional: true 85 | 86 | /@esbuild/android-arm64@0.25.5: 87 | resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} 88 | engines: {node: '>=18'} 89 | cpu: [arm64] 90 | os: [android] 91 | requiresBuild: true 92 | dev: true 93 | optional: true 94 | 95 | /@esbuild/android-arm@0.25.2: 96 | resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} 97 | engines: {node: '>=18'} 98 | cpu: [arm] 99 | os: [android] 100 | requiresBuild: true 101 | dev: false 102 | optional: true 103 | 104 | /@esbuild/android-arm@0.25.5: 105 | resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} 106 | engines: {node: '>=18'} 107 | cpu: [arm] 108 | os: [android] 109 | requiresBuild: true 110 | dev: true 111 | optional: true 112 | 113 | /@esbuild/android-x64@0.25.2: 114 | resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} 115 | engines: {node: '>=18'} 116 | cpu: [x64] 117 | os: [android] 118 | requiresBuild: true 119 | dev: false 120 | optional: true 121 | 122 | /@esbuild/android-x64@0.25.5: 123 | resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} 124 | engines: {node: '>=18'} 125 | cpu: [x64] 126 | os: [android] 127 | requiresBuild: true 128 | dev: true 129 | optional: true 130 | 131 | /@esbuild/darwin-arm64@0.25.2: 132 | resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} 133 | engines: {node: '>=18'} 134 | cpu: [arm64] 135 | os: [darwin] 136 | requiresBuild: true 137 | dev: false 138 | optional: true 139 | 140 | /@esbuild/darwin-arm64@0.25.5: 141 | resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} 142 | engines: {node: '>=18'} 143 | cpu: [arm64] 144 | os: [darwin] 145 | requiresBuild: true 146 | dev: true 147 | optional: true 148 | 149 | /@esbuild/darwin-x64@0.25.2: 150 | resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} 151 | engines: {node: '>=18'} 152 | cpu: [x64] 153 | os: [darwin] 154 | requiresBuild: true 155 | dev: false 156 | optional: true 157 | 158 | /@esbuild/darwin-x64@0.25.5: 159 | resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} 160 | engines: {node: '>=18'} 161 | cpu: [x64] 162 | os: [darwin] 163 | requiresBuild: true 164 | dev: true 165 | optional: true 166 | 167 | /@esbuild/freebsd-arm64@0.25.2: 168 | resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} 169 | engines: {node: '>=18'} 170 | cpu: [arm64] 171 | os: [freebsd] 172 | requiresBuild: true 173 | dev: false 174 | optional: true 175 | 176 | /@esbuild/freebsd-arm64@0.25.5: 177 | resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} 178 | engines: {node: '>=18'} 179 | cpu: [arm64] 180 | os: [freebsd] 181 | requiresBuild: true 182 | dev: true 183 | optional: true 184 | 185 | /@esbuild/freebsd-x64@0.25.2: 186 | resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} 187 | engines: {node: '>=18'} 188 | cpu: [x64] 189 | os: [freebsd] 190 | requiresBuild: true 191 | dev: false 192 | optional: true 193 | 194 | /@esbuild/freebsd-x64@0.25.5: 195 | resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} 196 | engines: {node: '>=18'} 197 | cpu: [x64] 198 | os: [freebsd] 199 | requiresBuild: true 200 | dev: true 201 | optional: true 202 | 203 | /@esbuild/linux-arm64@0.25.2: 204 | resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} 205 | engines: {node: '>=18'} 206 | cpu: [arm64] 207 | os: [linux] 208 | requiresBuild: true 209 | dev: false 210 | optional: true 211 | 212 | /@esbuild/linux-arm64@0.25.5: 213 | resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} 214 | engines: {node: '>=18'} 215 | cpu: [arm64] 216 | os: [linux] 217 | requiresBuild: true 218 | dev: true 219 | optional: true 220 | 221 | /@esbuild/linux-arm@0.25.2: 222 | resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} 223 | engines: {node: '>=18'} 224 | cpu: [arm] 225 | os: [linux] 226 | requiresBuild: true 227 | dev: false 228 | optional: true 229 | 230 | /@esbuild/linux-arm@0.25.5: 231 | resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} 232 | engines: {node: '>=18'} 233 | cpu: [arm] 234 | os: [linux] 235 | requiresBuild: true 236 | dev: true 237 | optional: true 238 | 239 | /@esbuild/linux-ia32@0.25.2: 240 | resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} 241 | engines: {node: '>=18'} 242 | cpu: [ia32] 243 | os: [linux] 244 | requiresBuild: true 245 | dev: false 246 | optional: true 247 | 248 | /@esbuild/linux-ia32@0.25.5: 249 | resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} 250 | engines: {node: '>=18'} 251 | cpu: [ia32] 252 | os: [linux] 253 | requiresBuild: true 254 | dev: true 255 | optional: true 256 | 257 | /@esbuild/linux-loong64@0.25.2: 258 | resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} 259 | engines: {node: '>=18'} 260 | cpu: [loong64] 261 | os: [linux] 262 | requiresBuild: true 263 | dev: false 264 | optional: true 265 | 266 | /@esbuild/linux-loong64@0.25.5: 267 | resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} 268 | engines: {node: '>=18'} 269 | cpu: [loong64] 270 | os: [linux] 271 | requiresBuild: true 272 | dev: true 273 | optional: true 274 | 275 | /@esbuild/linux-mips64el@0.25.2: 276 | resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} 277 | engines: {node: '>=18'} 278 | cpu: [mips64el] 279 | os: [linux] 280 | requiresBuild: true 281 | dev: false 282 | optional: true 283 | 284 | /@esbuild/linux-mips64el@0.25.5: 285 | resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} 286 | engines: {node: '>=18'} 287 | cpu: [mips64el] 288 | os: [linux] 289 | requiresBuild: true 290 | dev: true 291 | optional: true 292 | 293 | /@esbuild/linux-ppc64@0.25.2: 294 | resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} 295 | engines: {node: '>=18'} 296 | cpu: [ppc64] 297 | os: [linux] 298 | requiresBuild: true 299 | dev: false 300 | optional: true 301 | 302 | /@esbuild/linux-ppc64@0.25.5: 303 | resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} 304 | engines: {node: '>=18'} 305 | cpu: [ppc64] 306 | os: [linux] 307 | requiresBuild: true 308 | dev: true 309 | optional: true 310 | 311 | /@esbuild/linux-riscv64@0.25.2: 312 | resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} 313 | engines: {node: '>=18'} 314 | cpu: [riscv64] 315 | os: [linux] 316 | requiresBuild: true 317 | dev: false 318 | optional: true 319 | 320 | /@esbuild/linux-riscv64@0.25.5: 321 | resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} 322 | engines: {node: '>=18'} 323 | cpu: [riscv64] 324 | os: [linux] 325 | requiresBuild: true 326 | dev: true 327 | optional: true 328 | 329 | /@esbuild/linux-s390x@0.25.2: 330 | resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} 331 | engines: {node: '>=18'} 332 | cpu: [s390x] 333 | os: [linux] 334 | requiresBuild: true 335 | dev: false 336 | optional: true 337 | 338 | /@esbuild/linux-s390x@0.25.5: 339 | resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} 340 | engines: {node: '>=18'} 341 | cpu: [s390x] 342 | os: [linux] 343 | requiresBuild: true 344 | dev: true 345 | optional: true 346 | 347 | /@esbuild/linux-x64@0.25.2: 348 | resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} 349 | engines: {node: '>=18'} 350 | cpu: [x64] 351 | os: [linux] 352 | requiresBuild: true 353 | dev: false 354 | optional: true 355 | 356 | /@esbuild/linux-x64@0.25.5: 357 | resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} 358 | engines: {node: '>=18'} 359 | cpu: [x64] 360 | os: [linux] 361 | requiresBuild: true 362 | dev: true 363 | optional: true 364 | 365 | /@esbuild/netbsd-arm64@0.25.2: 366 | resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} 367 | engines: {node: '>=18'} 368 | cpu: [arm64] 369 | os: [netbsd] 370 | requiresBuild: true 371 | dev: false 372 | optional: true 373 | 374 | /@esbuild/netbsd-arm64@0.25.5: 375 | resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} 376 | engines: {node: '>=18'} 377 | cpu: [arm64] 378 | os: [netbsd] 379 | requiresBuild: true 380 | dev: true 381 | optional: true 382 | 383 | /@esbuild/netbsd-x64@0.25.2: 384 | resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} 385 | engines: {node: '>=18'} 386 | cpu: [x64] 387 | os: [netbsd] 388 | requiresBuild: true 389 | dev: false 390 | optional: true 391 | 392 | /@esbuild/netbsd-x64@0.25.5: 393 | resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} 394 | engines: {node: '>=18'} 395 | cpu: [x64] 396 | os: [netbsd] 397 | requiresBuild: true 398 | dev: true 399 | optional: true 400 | 401 | /@esbuild/openbsd-arm64@0.25.2: 402 | resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} 403 | engines: {node: '>=18'} 404 | cpu: [arm64] 405 | os: [openbsd] 406 | requiresBuild: true 407 | dev: false 408 | optional: true 409 | 410 | /@esbuild/openbsd-arm64@0.25.5: 411 | resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} 412 | engines: {node: '>=18'} 413 | cpu: [arm64] 414 | os: [openbsd] 415 | requiresBuild: true 416 | dev: true 417 | optional: true 418 | 419 | /@esbuild/openbsd-x64@0.25.2: 420 | resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} 421 | engines: {node: '>=18'} 422 | cpu: [x64] 423 | os: [openbsd] 424 | requiresBuild: true 425 | dev: false 426 | optional: true 427 | 428 | /@esbuild/openbsd-x64@0.25.5: 429 | resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} 430 | engines: {node: '>=18'} 431 | cpu: [x64] 432 | os: [openbsd] 433 | requiresBuild: true 434 | dev: true 435 | optional: true 436 | 437 | /@esbuild/sunos-x64@0.25.2: 438 | resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} 439 | engines: {node: '>=18'} 440 | cpu: [x64] 441 | os: [sunos] 442 | requiresBuild: true 443 | dev: false 444 | optional: true 445 | 446 | /@esbuild/sunos-x64@0.25.5: 447 | resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} 448 | engines: {node: '>=18'} 449 | cpu: [x64] 450 | os: [sunos] 451 | requiresBuild: true 452 | dev: true 453 | optional: true 454 | 455 | /@esbuild/win32-arm64@0.25.2: 456 | resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} 457 | engines: {node: '>=18'} 458 | cpu: [arm64] 459 | os: [win32] 460 | requiresBuild: true 461 | dev: false 462 | optional: true 463 | 464 | /@esbuild/win32-arm64@0.25.5: 465 | resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} 466 | engines: {node: '>=18'} 467 | cpu: [arm64] 468 | os: [win32] 469 | requiresBuild: true 470 | dev: true 471 | optional: true 472 | 473 | /@esbuild/win32-ia32@0.25.2: 474 | resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} 475 | engines: {node: '>=18'} 476 | cpu: [ia32] 477 | os: [win32] 478 | requiresBuild: true 479 | dev: false 480 | optional: true 481 | 482 | /@esbuild/win32-ia32@0.25.5: 483 | resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} 484 | engines: {node: '>=18'} 485 | cpu: [ia32] 486 | os: [win32] 487 | requiresBuild: true 488 | dev: true 489 | optional: true 490 | 491 | /@esbuild/win32-x64@0.25.2: 492 | resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} 493 | engines: {node: '>=18'} 494 | cpu: [x64] 495 | os: [win32] 496 | requiresBuild: true 497 | dev: false 498 | optional: true 499 | 500 | /@esbuild/win32-x64@0.25.5: 501 | resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} 502 | engines: {node: '>=18'} 503 | cpu: [x64] 504 | os: [win32] 505 | requiresBuild: true 506 | dev: true 507 | optional: true 508 | 509 | /@jridgewell/sourcemap-codec@1.5.0: 510 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 511 | 512 | /@rollup/rollup-android-arm-eabi@4.43.0: 513 | resolution: {integrity: sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==} 514 | cpu: [arm] 515 | os: [android] 516 | requiresBuild: true 517 | dev: true 518 | optional: true 519 | 520 | /@rollup/rollup-android-arm64@4.43.0: 521 | resolution: {integrity: sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==} 522 | cpu: [arm64] 523 | os: [android] 524 | requiresBuild: true 525 | dev: true 526 | optional: true 527 | 528 | /@rollup/rollup-darwin-arm64@4.43.0: 529 | resolution: {integrity: sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==} 530 | cpu: [arm64] 531 | os: [darwin] 532 | requiresBuild: true 533 | dev: true 534 | optional: true 535 | 536 | /@rollup/rollup-darwin-x64@4.43.0: 537 | resolution: {integrity: sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==} 538 | cpu: [x64] 539 | os: [darwin] 540 | requiresBuild: true 541 | dev: true 542 | optional: true 543 | 544 | /@rollup/rollup-freebsd-arm64@4.43.0: 545 | resolution: {integrity: sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==} 546 | cpu: [arm64] 547 | os: [freebsd] 548 | requiresBuild: true 549 | dev: true 550 | optional: true 551 | 552 | /@rollup/rollup-freebsd-x64@4.43.0: 553 | resolution: {integrity: sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==} 554 | cpu: [x64] 555 | os: [freebsd] 556 | requiresBuild: true 557 | dev: true 558 | optional: true 559 | 560 | /@rollup/rollup-linux-arm-gnueabihf@4.43.0: 561 | resolution: {integrity: sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==} 562 | cpu: [arm] 563 | os: [linux] 564 | requiresBuild: true 565 | dev: true 566 | optional: true 567 | 568 | /@rollup/rollup-linux-arm-musleabihf@4.43.0: 569 | resolution: {integrity: sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==} 570 | cpu: [arm] 571 | os: [linux] 572 | requiresBuild: true 573 | dev: true 574 | optional: true 575 | 576 | /@rollup/rollup-linux-arm64-gnu@4.43.0: 577 | resolution: {integrity: sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==} 578 | cpu: [arm64] 579 | os: [linux] 580 | requiresBuild: true 581 | dev: true 582 | optional: true 583 | 584 | /@rollup/rollup-linux-arm64-musl@4.43.0: 585 | resolution: {integrity: sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==} 586 | cpu: [arm64] 587 | os: [linux] 588 | requiresBuild: true 589 | dev: true 590 | optional: true 591 | 592 | /@rollup/rollup-linux-loongarch64-gnu@4.43.0: 593 | resolution: {integrity: sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==} 594 | cpu: [loong64] 595 | os: [linux] 596 | requiresBuild: true 597 | dev: true 598 | optional: true 599 | 600 | /@rollup/rollup-linux-powerpc64le-gnu@4.43.0: 601 | resolution: {integrity: sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==} 602 | cpu: [ppc64] 603 | os: [linux] 604 | requiresBuild: true 605 | dev: true 606 | optional: true 607 | 608 | /@rollup/rollup-linux-riscv64-gnu@4.43.0: 609 | resolution: {integrity: sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==} 610 | cpu: [riscv64] 611 | os: [linux] 612 | requiresBuild: true 613 | dev: true 614 | optional: true 615 | 616 | /@rollup/rollup-linux-riscv64-musl@4.43.0: 617 | resolution: {integrity: sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==} 618 | cpu: [riscv64] 619 | os: [linux] 620 | requiresBuild: true 621 | dev: true 622 | optional: true 623 | 624 | /@rollup/rollup-linux-s390x-gnu@4.43.0: 625 | resolution: {integrity: sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==} 626 | cpu: [s390x] 627 | os: [linux] 628 | requiresBuild: true 629 | dev: true 630 | optional: true 631 | 632 | /@rollup/rollup-linux-x64-gnu@4.43.0: 633 | resolution: {integrity: sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==} 634 | cpu: [x64] 635 | os: [linux] 636 | requiresBuild: true 637 | dev: true 638 | optional: true 639 | 640 | /@rollup/rollup-linux-x64-musl@4.43.0: 641 | resolution: {integrity: sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==} 642 | cpu: [x64] 643 | os: [linux] 644 | requiresBuild: true 645 | dev: true 646 | optional: true 647 | 648 | /@rollup/rollup-win32-arm64-msvc@4.43.0: 649 | resolution: {integrity: sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==} 650 | cpu: [arm64] 651 | os: [win32] 652 | requiresBuild: true 653 | dev: true 654 | optional: true 655 | 656 | /@rollup/rollup-win32-ia32-msvc@4.43.0: 657 | resolution: {integrity: sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==} 658 | cpu: [ia32] 659 | os: [win32] 660 | requiresBuild: true 661 | dev: true 662 | optional: true 663 | 664 | /@rollup/rollup-win32-x64-msvc@4.43.0: 665 | resolution: {integrity: sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==} 666 | cpu: [x64] 667 | os: [win32] 668 | requiresBuild: true 669 | dev: true 670 | optional: true 671 | 672 | /@types/estree@1.0.7: 673 | resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} 674 | dev: true 675 | 676 | /@types/node@22.15.32: 677 | resolution: {integrity: sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==} 678 | dependencies: 679 | undici-types: 6.21.0 680 | dev: true 681 | 682 | /@vitejs/plugin-vue@5.2.4(vite@6.3.5)(vue@3.5.16): 683 | resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} 684 | engines: {node: ^18.0.0 || >=20.0.0} 685 | peerDependencies: 686 | vite: ^5.0.0 || ^6.0.0 687 | vue: ^3.2.25 688 | dependencies: 689 | vite: 6.3.5(@types/node@22.15.32) 690 | vue: 3.5.16(typescript@5.8.3) 691 | dev: true 692 | 693 | /@volar/language-core@2.4.14: 694 | resolution: {integrity: sha512-X6beusV0DvuVseaOEy7GoagS4rYHgDHnTrdOj5jeUb49fW5ceQyP9Ej5rBhqgz2wJggl+2fDbbojq1XKaxDi6w==} 695 | dependencies: 696 | '@volar/source-map': 2.4.14 697 | dev: true 698 | 699 | /@volar/source-map@2.4.14: 700 | resolution: {integrity: sha512-5TeKKMh7Sfxo8021cJfmBzcjfY1SsXsPMMjMvjY7ivesdnybqqS+GxGAoXHAOUawQTwtdUxgP65Im+dEmvWtYQ==} 701 | dev: true 702 | 703 | /@volar/typescript@2.4.14: 704 | resolution: {integrity: sha512-p8Z6f/bZM3/HyCdRNFZOEEzts51uV8WHeN8Tnfnm2EBv6FDB2TQLzfVx7aJvnl8ofKAOnS64B2O8bImBFaauRw==} 705 | dependencies: 706 | '@volar/language-core': 2.4.14 707 | path-browserify: 1.0.1 708 | vscode-uri: 3.1.0 709 | dev: true 710 | 711 | /@vue/compiler-core@3.5.16: 712 | resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} 713 | dependencies: 714 | '@babel/parser': 7.27.5 715 | '@vue/shared': 3.5.16 716 | entities: 4.5.0 717 | estree-walker: 2.0.2 718 | source-map-js: 1.2.1 719 | 720 | /@vue/compiler-dom@3.5.16: 721 | resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} 722 | dependencies: 723 | '@vue/compiler-core': 3.5.16 724 | '@vue/shared': 3.5.16 725 | 726 | /@vue/compiler-sfc@3.5.16: 727 | resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} 728 | dependencies: 729 | '@babel/parser': 7.27.5 730 | '@vue/compiler-core': 3.5.16 731 | '@vue/compiler-dom': 3.5.16 732 | '@vue/compiler-ssr': 3.5.16 733 | '@vue/shared': 3.5.16 734 | estree-walker: 2.0.2 735 | magic-string: 0.30.17 736 | postcss: 8.5.6 737 | source-map-js: 1.2.1 738 | 739 | /@vue/compiler-ssr@3.5.16: 740 | resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} 741 | dependencies: 742 | '@vue/compiler-dom': 3.5.16 743 | '@vue/shared': 3.5.16 744 | 745 | /@vue/compiler-vue2@2.7.16: 746 | resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} 747 | dependencies: 748 | de-indent: 1.0.2 749 | he: 1.2.0 750 | dev: true 751 | 752 | /@vue/language-core@2.2.10(typescript@5.8.3): 753 | resolution: {integrity: sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==} 754 | peerDependencies: 755 | typescript: '*' 756 | peerDependenciesMeta: 757 | typescript: 758 | optional: true 759 | dependencies: 760 | '@volar/language-core': 2.4.14 761 | '@vue/compiler-dom': 3.5.16 762 | '@vue/compiler-vue2': 2.7.16 763 | '@vue/shared': 3.5.16 764 | alien-signals: 1.0.13 765 | minimatch: 9.0.5 766 | muggle-string: 0.4.1 767 | path-browserify: 1.0.1 768 | typescript: 5.8.3 769 | dev: true 770 | 771 | /@vue/reactivity@3.5.16: 772 | resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} 773 | dependencies: 774 | '@vue/shared': 3.5.16 775 | 776 | /@vue/runtime-core@3.5.16: 777 | resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} 778 | dependencies: 779 | '@vue/reactivity': 3.5.16 780 | '@vue/shared': 3.5.16 781 | 782 | /@vue/runtime-dom@3.5.16: 783 | resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} 784 | dependencies: 785 | '@vue/reactivity': 3.5.16 786 | '@vue/runtime-core': 3.5.16 787 | '@vue/shared': 3.5.16 788 | csstype: 3.1.3 789 | 790 | /@vue/server-renderer@3.5.16(vue@3.5.16): 791 | resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} 792 | peerDependencies: 793 | vue: 3.5.16 794 | dependencies: 795 | '@vue/compiler-ssr': 3.5.16 796 | '@vue/shared': 3.5.16 797 | vue: 3.5.16(typescript@5.8.3) 798 | 799 | /@vue/shared@3.5.16: 800 | resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} 801 | 802 | /@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.16): 803 | resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==} 804 | peerDependencies: 805 | typescript: 5.x 806 | vue: ^3.4.0 807 | peerDependenciesMeta: 808 | typescript: 809 | optional: true 810 | vue: 811 | optional: true 812 | dependencies: 813 | typescript: 5.8.3 814 | vue: 3.5.16(typescript@5.8.3) 815 | dev: true 816 | 817 | /alien-signals@1.0.13: 818 | resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} 819 | dev: true 820 | 821 | /balanced-match@1.0.2: 822 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 823 | dev: true 824 | 825 | /brace-expansion@2.0.2: 826 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 827 | dependencies: 828 | balanced-match: 1.0.2 829 | dev: true 830 | 831 | /convex@1.24.8: 832 | resolution: {integrity: sha512-WNKLXhOboRthS1cAi8EbBpiEsXJF2UD2H6rEd4f6BjISd+/5P+aDMw5xUpYMsbATdxqSmEoIl5QsciWJcXyW+A==} 833 | engines: {node: '>=18.0.0', npm: '>=7.0.0'} 834 | hasBin: true 835 | peerDependencies: 836 | '@auth0/auth0-react': ^2.0.1 837 | '@clerk/clerk-react': ^4.12.8 || ^5.0.0 838 | react: ^18.0.0 || ^19.0.0-0 || ^19.0.0 839 | peerDependenciesMeta: 840 | '@auth0/auth0-react': 841 | optional: true 842 | '@clerk/clerk-react': 843 | optional: true 844 | react: 845 | optional: true 846 | dependencies: 847 | esbuild: 0.25.2 848 | jwt-decode: 4.0.0 849 | prettier: 3.5.3 850 | dev: false 851 | 852 | /csstype@3.1.3: 853 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 854 | 855 | /de-indent@1.0.2: 856 | resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} 857 | dev: true 858 | 859 | /entities@4.5.0: 860 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 861 | engines: {node: '>=0.12'} 862 | 863 | /esbuild@0.25.2: 864 | resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} 865 | engines: {node: '>=18'} 866 | hasBin: true 867 | requiresBuild: true 868 | optionalDependencies: 869 | '@esbuild/aix-ppc64': 0.25.2 870 | '@esbuild/android-arm': 0.25.2 871 | '@esbuild/android-arm64': 0.25.2 872 | '@esbuild/android-x64': 0.25.2 873 | '@esbuild/darwin-arm64': 0.25.2 874 | '@esbuild/darwin-x64': 0.25.2 875 | '@esbuild/freebsd-arm64': 0.25.2 876 | '@esbuild/freebsd-x64': 0.25.2 877 | '@esbuild/linux-arm': 0.25.2 878 | '@esbuild/linux-arm64': 0.25.2 879 | '@esbuild/linux-ia32': 0.25.2 880 | '@esbuild/linux-loong64': 0.25.2 881 | '@esbuild/linux-mips64el': 0.25.2 882 | '@esbuild/linux-ppc64': 0.25.2 883 | '@esbuild/linux-riscv64': 0.25.2 884 | '@esbuild/linux-s390x': 0.25.2 885 | '@esbuild/linux-x64': 0.25.2 886 | '@esbuild/netbsd-arm64': 0.25.2 887 | '@esbuild/netbsd-x64': 0.25.2 888 | '@esbuild/openbsd-arm64': 0.25.2 889 | '@esbuild/openbsd-x64': 0.25.2 890 | '@esbuild/sunos-x64': 0.25.2 891 | '@esbuild/win32-arm64': 0.25.2 892 | '@esbuild/win32-ia32': 0.25.2 893 | '@esbuild/win32-x64': 0.25.2 894 | dev: false 895 | 896 | /esbuild@0.25.5: 897 | resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} 898 | engines: {node: '>=18'} 899 | hasBin: true 900 | requiresBuild: true 901 | optionalDependencies: 902 | '@esbuild/aix-ppc64': 0.25.5 903 | '@esbuild/android-arm': 0.25.5 904 | '@esbuild/android-arm64': 0.25.5 905 | '@esbuild/android-x64': 0.25.5 906 | '@esbuild/darwin-arm64': 0.25.5 907 | '@esbuild/darwin-x64': 0.25.5 908 | '@esbuild/freebsd-arm64': 0.25.5 909 | '@esbuild/freebsd-x64': 0.25.5 910 | '@esbuild/linux-arm': 0.25.5 911 | '@esbuild/linux-arm64': 0.25.5 912 | '@esbuild/linux-ia32': 0.25.5 913 | '@esbuild/linux-loong64': 0.25.5 914 | '@esbuild/linux-mips64el': 0.25.5 915 | '@esbuild/linux-ppc64': 0.25.5 916 | '@esbuild/linux-riscv64': 0.25.5 917 | '@esbuild/linux-s390x': 0.25.5 918 | '@esbuild/linux-x64': 0.25.5 919 | '@esbuild/netbsd-arm64': 0.25.5 920 | '@esbuild/netbsd-x64': 0.25.5 921 | '@esbuild/openbsd-arm64': 0.25.5 922 | '@esbuild/openbsd-x64': 0.25.5 923 | '@esbuild/sunos-x64': 0.25.5 924 | '@esbuild/win32-arm64': 0.25.5 925 | '@esbuild/win32-ia32': 0.25.5 926 | '@esbuild/win32-x64': 0.25.5 927 | dev: true 928 | 929 | /estree-walker@2.0.2: 930 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 931 | 932 | /fdir@6.4.6(picomatch@4.0.2): 933 | resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} 934 | peerDependencies: 935 | picomatch: ^3 || ^4 936 | peerDependenciesMeta: 937 | picomatch: 938 | optional: true 939 | dependencies: 940 | picomatch: 4.0.2 941 | dev: true 942 | 943 | /fsevents@2.3.3: 944 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 945 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 946 | os: [darwin] 947 | requiresBuild: true 948 | dev: true 949 | optional: true 950 | 951 | /he@1.2.0: 952 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 953 | hasBin: true 954 | dev: true 955 | 956 | /jwt-decode@4.0.0: 957 | resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} 958 | engines: {node: '>=18'} 959 | dev: false 960 | 961 | /magic-string@0.30.17: 962 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 963 | dependencies: 964 | '@jridgewell/sourcemap-codec': 1.5.0 965 | 966 | /minimatch@9.0.5: 967 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 968 | engines: {node: '>=16 || 14 >=14.17'} 969 | dependencies: 970 | brace-expansion: 2.0.2 971 | dev: true 972 | 973 | /muggle-string@0.4.1: 974 | resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} 975 | dev: true 976 | 977 | /nanoid@3.3.11: 978 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 979 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 980 | hasBin: true 981 | 982 | /path-browserify@1.0.1: 983 | resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 984 | dev: true 985 | 986 | /picocolors@1.1.1: 987 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 988 | 989 | /picomatch@4.0.2: 990 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 991 | engines: {node: '>=12'} 992 | dev: true 993 | 994 | /postcss@8.5.6: 995 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 996 | engines: {node: ^10 || ^12 || >=14} 997 | dependencies: 998 | nanoid: 3.3.11 999 | picocolors: 1.1.1 1000 | source-map-js: 1.2.1 1001 | 1002 | /prettier@3.5.3: 1003 | resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} 1004 | engines: {node: '>=14'} 1005 | hasBin: true 1006 | dev: false 1007 | 1008 | /rollup@4.43.0: 1009 | resolution: {integrity: sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==} 1010 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1011 | hasBin: true 1012 | dependencies: 1013 | '@types/estree': 1.0.7 1014 | optionalDependencies: 1015 | '@rollup/rollup-android-arm-eabi': 4.43.0 1016 | '@rollup/rollup-android-arm64': 4.43.0 1017 | '@rollup/rollup-darwin-arm64': 4.43.0 1018 | '@rollup/rollup-darwin-x64': 4.43.0 1019 | '@rollup/rollup-freebsd-arm64': 4.43.0 1020 | '@rollup/rollup-freebsd-x64': 4.43.0 1021 | '@rollup/rollup-linux-arm-gnueabihf': 4.43.0 1022 | '@rollup/rollup-linux-arm-musleabihf': 4.43.0 1023 | '@rollup/rollup-linux-arm64-gnu': 4.43.0 1024 | '@rollup/rollup-linux-arm64-musl': 4.43.0 1025 | '@rollup/rollup-linux-loongarch64-gnu': 4.43.0 1026 | '@rollup/rollup-linux-powerpc64le-gnu': 4.43.0 1027 | '@rollup/rollup-linux-riscv64-gnu': 4.43.0 1028 | '@rollup/rollup-linux-riscv64-musl': 4.43.0 1029 | '@rollup/rollup-linux-s390x-gnu': 4.43.0 1030 | '@rollup/rollup-linux-x64-gnu': 4.43.0 1031 | '@rollup/rollup-linux-x64-musl': 4.43.0 1032 | '@rollup/rollup-win32-arm64-msvc': 4.43.0 1033 | '@rollup/rollup-win32-ia32-msvc': 4.43.0 1034 | '@rollup/rollup-win32-x64-msvc': 4.43.0 1035 | fsevents: 2.3.3 1036 | dev: true 1037 | 1038 | /source-map-js@1.2.1: 1039 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1040 | engines: {node: '>=0.10.0'} 1041 | 1042 | /tinyglobby@0.2.14: 1043 | resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} 1044 | engines: {node: '>=12.0.0'} 1045 | dependencies: 1046 | fdir: 6.4.6(picomatch@4.0.2) 1047 | picomatch: 4.0.2 1048 | dev: true 1049 | 1050 | /typescript@5.8.3: 1051 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 1052 | engines: {node: '>=14.17'} 1053 | hasBin: true 1054 | 1055 | /undici-types@6.21.0: 1056 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 1057 | dev: true 1058 | 1059 | /vite@6.3.5(@types/node@22.15.32): 1060 | resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} 1061 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 1062 | hasBin: true 1063 | peerDependencies: 1064 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 1065 | jiti: '>=1.21.0' 1066 | less: '*' 1067 | lightningcss: ^1.21.0 1068 | sass: '*' 1069 | sass-embedded: '*' 1070 | stylus: '*' 1071 | sugarss: '*' 1072 | terser: ^5.16.0 1073 | tsx: ^4.8.1 1074 | yaml: ^2.4.2 1075 | peerDependenciesMeta: 1076 | '@types/node': 1077 | optional: true 1078 | jiti: 1079 | optional: true 1080 | less: 1081 | optional: true 1082 | lightningcss: 1083 | optional: true 1084 | sass: 1085 | optional: true 1086 | sass-embedded: 1087 | optional: true 1088 | stylus: 1089 | optional: true 1090 | sugarss: 1091 | optional: true 1092 | terser: 1093 | optional: true 1094 | tsx: 1095 | optional: true 1096 | yaml: 1097 | optional: true 1098 | dependencies: 1099 | '@types/node': 22.15.32 1100 | esbuild: 0.25.5 1101 | fdir: 6.4.6(picomatch@4.0.2) 1102 | picomatch: 4.0.2 1103 | postcss: 8.5.6 1104 | rollup: 4.43.0 1105 | tinyglobby: 0.2.14 1106 | optionalDependencies: 1107 | fsevents: 2.3.3 1108 | dev: true 1109 | 1110 | /vscode-uri@3.1.0: 1111 | resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} 1112 | dev: true 1113 | 1114 | /vue-tsc@2.2.10(typescript@5.8.3): 1115 | resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==} 1116 | hasBin: true 1117 | peerDependencies: 1118 | typescript: '>=5.0.0' 1119 | dependencies: 1120 | '@volar/typescript': 2.4.14 1121 | '@vue/language-core': 2.2.10(typescript@5.8.3) 1122 | typescript: 5.8.3 1123 | dev: true 1124 | 1125 | /vue@3.5.16(typescript@5.8.3): 1126 | resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} 1127 | peerDependencies: 1128 | typescript: '*' 1129 | peerDependenciesMeta: 1130 | typescript: 1131 | optional: true 1132 | dependencies: 1133 | '@vue/compiler-dom': 3.5.16 1134 | '@vue/compiler-sfc': 3.5.16 1135 | '@vue/runtime-dom': 3.5.16 1136 | '@vue/server-renderer': 3.5.16(vue@3.5.16) 1137 | '@vue/shared': 3.5.16 1138 | typescript: 5.8.3 1139 | --------------------------------------------------------------------------------