diff --git a/docs/framework/vue/adapter.md b/docs/framework/vue/adapter.md new file mode 100644 index 00000000..59f95364 --- /dev/null +++ b/docs/framework/vue/adapter.md @@ -0,0 +1,38 @@ +--- +title: TanStack Pacer Vue Adapter +id: adapter +--- + +If you are using TanStack Pacer in a Vue application, we recommend using the Vue Adapter. The Vue Adapter provides a set of easy-to-use composables on top of the core Pacer utilities. If you find yourself wanting to use the core Pacer classes/functions directly, the Vue Adapter will also re-export everything from the core package. + +## Installation + +```sh +npm install @tanstack/vue-pacer +``` + +## Vue Composables + +See the [Vue Functions Reference](./reference/index.md) to see the full list of composables available in the Vue Adapter. + +## Basic Usage + +Import a Vue specific composable from the Vue Adapter. + +```vue + +``` + +Or import a core Pacer class/function that is re-exported from the Vue Adapter. + +```ts +import { debounce, Debouncer } from '@tanstack/vue-pacer' // no need to install the core package separately +``` diff --git a/docs/framework/vue/reference/functions/usedebouncedvalue.md b/docs/framework/vue/reference/functions/usedebouncedvalue.md new file mode 100644 index 00000000..81c51f33 --- /dev/null +++ b/docs/framework/vue/reference/functions/usedebouncedvalue.md @@ -0,0 +1,110 @@ +--- +title: useDebouncedValue +id: usedebouncedvalue +--- + +# useDebouncedValue + +A Vue composable that creates a debounced value that updates only after a specified delay. This composable automatically tracks changes to the input value and updates the debounced value accordingly. + +## Usage + +```vue + + + +``` + +## Type Declaration + +```ts +function useDebouncedValue( + value: MaybeRefOrGetter, + options: DebouncerOptions<(value: TValue) => void> +): UseDebouncedValueReturn +``` + +## Parameters + +- `value`: The value to debounce. Can be: + - A raw value + - A Vue ref + - A getter function +- `options`: Configuration options for the debouncer + - `wait`: The number of milliseconds to delay + - `maxWait`: Optional maximum time the debouncer will wait before invoking + - `leading`: Optional, if true the debouncer will invoke on the leading edge + - `trailing`: Optional, if true the debouncer will invoke on the trailing edge + +## Returns + +Returns an object containing: +- `value`: A Vue ref containing the current debounced value +- `flush()`: Immediately invoke any pending debounced invocations +- `cancel()`: Cancel any pending debounced invocations +- `isPending`: A Vue ref indicating if there are pending updates diff --git a/docs/framework/vue/reference/functions/usedebouncer.md b/docs/framework/vue/reference/functions/usedebouncer.md new file mode 100644 index 00000000..8a91f369 --- /dev/null +++ b/docs/framework/vue/reference/functions/usedebouncer.md @@ -0,0 +1,113 @@ +--- +title: useDebouncer +id: usedebouncer +--- + +# useDebouncer + +A Vue composable that creates a debouncer instance with Vue reactivity integration. This composable provides a debounced value that updates only after a specified delay has passed since the last update. + +## Usage + +```vue + + + +``` + +## Type Declaration + +```ts +function useDebouncer( + initialValue: MaybeRef, + options: DebouncerOptions<(value: TValue) => void> +): UseDebouncerReturn +``` + +## Parameters + +- `initialValue`: The initial value for the debouncer. Can be a raw value or a Vue ref. +- `options`: Configuration options for the debouncer + - `wait`: The number of milliseconds to delay + - `maxWait`: Optional maximum time the debouncer will wait before invoking + - `leading`: Optional, if true the debouncer will invoke on the leading edge + - `trailing`: Optional, if true the debouncer will invoke on the trailing edge + - `enabled`: Optional, if false the debouncer will not execute (defaults to true) + +## Returns + +- `value`: A Vue ref containing the current debounced value +- `setValue`: Function to set a new value (will be debounced) +- `flush`: Function to force immediate update of the value +- `cancel`: Function to cancel any pending updates +- `isPending`: A computed ref indicating if there are any pending updates +- `executionCount`: A computed ref containing the number of times the value has been updated +- `setOptions`: Function to update debouncer options +- `getOptions`: Function to get current debouncer options diff --git a/docs/framework/vue/reference/index.md b/docs/framework/vue/reference/index.md new file mode 100644 index 00000000..3375c212 --- /dev/null +++ b/docs/framework/vue/reference/index.md @@ -0,0 +1,13 @@ +--- +title: Vue Functions Reference +id: reference +--- + +# Vue Functions Reference + +The Vue Adapter provides a set of composables that wrap the core Pacer utilities. These composables are designed to work seamlessly with Vue's reactivity system and provide an idiomatic Vue experience. + +## Debouncing + +- [useDebouncer](./functions/usedebouncer.md) - Creates a debouncer instance with Vue reactivity integration +- [useDebouncedValue](./functions/usedebouncedvalue.md) - Creates a debounced value that updates after a delay diff --git a/examples/vue/debounce/index.html b/examples/vue/debounce/index.html new file mode 100644 index 00000000..608b05f4 --- /dev/null +++ b/examples/vue/debounce/index.html @@ -0,0 +1,12 @@ + + + + + + Vue Debouncing Example - TanStack Pacer + + +
+ + + diff --git a/examples/vue/debounce/package.json b/examples/vue/debounce/package.json new file mode 100644 index 00000000..cc6e9b87 --- /dev/null +++ b/examples/vue/debounce/package.json @@ -0,0 +1,21 @@ +{ + "name": "@tanstack/pacer-example-vue-debounce", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/vue-pacer": "workspace:*", + "vue": "^3.3.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.5.0", + "typescript": "5.8.3", + "vite": "^6.3.5", + "vue-tsc": "^1.8.8" + } +} diff --git a/examples/vue/debounce/src/App.vue b/examples/vue/debounce/src/App.vue new file mode 100644 index 00000000..5d67de2d --- /dev/null +++ b/examples/vue/debounce/src/App.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/examples/vue/debounce/src/env.d.ts b/examples/vue/debounce/src/env.d.ts new file mode 100644 index 00000000..6977c557 --- /dev/null +++ b/examples/vue/debounce/src/env.d.ts @@ -0,0 +1,8 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/examples/vue/debounce/src/main.ts b/examples/vue/debounce/src/main.ts new file mode 100644 index 00000000..01433bca --- /dev/null +++ b/examples/vue/debounce/src/main.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/examples/vue/debounce/tsconfig.json b/examples/vue/debounce/tsconfig.json new file mode 100644 index 00000000..ee327f7d --- /dev/null +++ b/examples/vue/debounce/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "vite.config.ts"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/vue/debounce/tsconfig.node.json b/examples/vue/debounce/tsconfig.node.json new file mode 100644 index 00000000..42872c59 --- /dev/null +++ b/examples/vue/debounce/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/vue/debounce/vite.config.ts b/examples/vue/debounce/vite.config.ts new file mode 100644 index 00000000..c40aa3c3 --- /dev/null +++ b/examples/vue/debounce/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/packages/vue-pacer/eslint.config.js b/packages/vue-pacer/eslint.config.js new file mode 100644 index 00000000..ae0c9cca --- /dev/null +++ b/packages/vue-pacer/eslint.config.js @@ -0,0 +1,40 @@ +// @ts-check + +// @ts-ignore Needed due to moduleResolution Node vs Bundler +import { tanstackConfig } from '@tanstack/config/eslint' + +export default [ + ...tanstackConfig, + { + name: 'tanstack/vue', + rules: { + 'import/order': [ + 'error', + { + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + 'object', + 'type', + ], + pathGroups: [ + { + pattern: 'vue', + group: 'external', + position: 'before', + }, + ], + 'newlines-between': 'never', + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + }, + ], + }, + }, +] diff --git a/packages/vue-pacer/package.json b/packages/vue-pacer/package.json new file mode 100644 index 00000000..5c42ee4d --- /dev/null +++ b/packages/vue-pacer/package.json @@ -0,0 +1,87 @@ +{ + "name": "@tanstack/vue-pacer", + "version": "0.0.1", + "description": "Vue adapter for TanStack Pacer - Utilities for debouncing, throttling, rate limiting, queuing", + "author": "Sandeep Ramgolam", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/tanstack/pacer.git", + "directory": "packages/vue-pacer" + }, + "homepage": "https://tanstack.com/pacer", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "vue", + "vuejs", + "composable", + "debounce", + "throttle", + "rate-limit", + "queue", + "async" + ], + "type": "module", + "types": "dist/esm/index.d.ts", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./debouncer": { + "import": { + "types": "./dist/esm/debouncer/index.d.ts", + "default": "./dist/esm/debouncer/index.js" + }, + "require": { + "types": "./dist/cjs/debouncer/index.d.cts", + "default": "./dist/cjs/debouncer/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "scripts": { + "clean": "premove ./build ./dist", + "test:eslint": "eslint ./src", + "test:lib": "vitest --passWithNoTests", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "test:build": "publint --strict", + "build": "vite build" + }, + "files": [ + "dist", + "src" + ], + "dependencies": { + "@tanstack/pacer": "workspace:*" + }, + "devDependencies": { + "@tanstack/config": "0.18.0", + "@vitejs/plugin-vue": "^4.5.0", + "typescript": "5.8.3", + "vite": "^6.3.5", + "vitest": "^3.1.4" + }, + "peerDependencies": { + "vue": "^3.3.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/vue-pacer/src/debouncer/index.ts b/packages/vue-pacer/src/debouncer/index.ts new file mode 100644 index 00000000..c87c02b2 --- /dev/null +++ b/packages/vue-pacer/src/debouncer/index.ts @@ -0,0 +1,2 @@ +export { useDebouncer } from './useDebouncer' +export { useDebouncedValue } from './useDebouncedValue' diff --git a/packages/vue-pacer/src/debouncer/useDebouncedValue.ts b/packages/vue-pacer/src/debouncer/useDebouncedValue.ts new file mode 100644 index 00000000..21f3274d --- /dev/null +++ b/packages/vue-pacer/src/debouncer/useDebouncedValue.ts @@ -0,0 +1,128 @@ +import type { Ref } from 'vue' +import { unref, watch } from 'vue' +import { useDebouncer } from './useDebouncer' +import type { MaybeRefOrGetter } from '../types' +import type { DebouncerOptions } from '@tanstack/pacer' + +/** + * A Vue composable that creates a debounced value that updates only after a specified delay. + * This composable automatically tracks changes to the input value and updates the + * debounced value accordingly. + * + * The debounced value will only update after the specified wait time has elapsed since + * the last change to the input value. If the input value changes again before the wait + * time expires, the timer resets and starts waiting again. + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useDebouncedValue( + inputValue: MaybeRefOrGetter, + options: DebouncerOptions<(value: TValue) => void>, +): { + /** The current debounced value */ + value: Ref + /** Force immediate update of the value */ + flush: () => void + /** Cancel any pending updates */ + cancel: () => void + /** Check if there are any pending updates */ + isPending: Readonly> +} { + const getValue = + typeof inputValue === 'function' + ? (inputValue as () => TValue) + : () => unref(inputValue) + + const { + value: debouncedValue, + setValue, + flush, + cancel, + isPending, + } = useDebouncer(getValue(), options) + + watch( + getValue, + (newValue) => { + setValue(newValue) + }, + { immediate: true }, + ) + + return { + value: debouncedValue, + flush, + cancel, + isPending, + } +} diff --git a/packages/vue-pacer/src/debouncer/useDebouncer.ts b/packages/vue-pacer/src/debouncer/useDebouncer.ts new file mode 100644 index 00000000..01642fd6 --- /dev/null +++ b/packages/vue-pacer/src/debouncer/useDebouncer.ts @@ -0,0 +1,184 @@ +import type { Ref } from 'vue' +import { Debouncer } from '@tanstack/pacer' +import { readonly, ref, unref } from 'vue' +import type { MaybeRef } from '../types' +import type { DebouncerOptions } from '@tanstack/pacer' + +/** + * Creates a debouncer instance with Vue reactivity integration. + * This composable provides a debounced value that updates only after + * a specified delay has passed since the last update. + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useDebouncer( + initialValue: MaybeRef, + optionsInput: DebouncerOptions<(value: TValue) => void>, +): { + /** The current debounced value */ + value: Ref + /** Set a new value (will be debounced) */ + setValue: (newValue: TValue) => void + /** Force immediate update of the value */ + flush: () => void + /** Cancel any pending updates */ + cancel: () => void + /** Check if there are any pending updates */ + isPending: Readonly> + /** Get the number of times the value has been updated */ + executionCount: Readonly> + /** Update debouncer options */ + setOptions: ( + newOptions: Partial void>>, + ) => void + /** Get current debouncer options */ + getOptions: () => Required void>> +} { + const value = ref(unref(initialValue)) as Ref + const _isPending = ref(false) + const _executionCount = ref(0) + + const getEffectiveOptions = ( + currentOpts: DebouncerOptions<(v: TValue) => void>, + ) => { + return { + ...currentOpts, + onExecute: () => { + // Call original onExecute if it exists + if (currentOpts.onExecute) { + // Assuming currentOpts.onExecute expects the debouncer instance + currentOpts.onExecute(debouncer as any) // Type assertion for now, can be refined + } + _isPending.value = debouncer.getIsPending() // Sync after execution + _executionCount.value = debouncer.getExecutionCount() // Sync execution count + }, + } as Required void>> + } + + const debouncer = new Debouncer((newValue: TValue) => { + value.value = newValue + }, getEffectiveOptions(optionsInput)) + + // Initialize reactive refs with initial state from debouncer + _isPending.value = debouncer.getIsPending() + _executionCount.value = debouncer.getExecutionCount() + + const wrappedSetValue = (newValue: TValue) => { + debouncer.maybeExecute(newValue) + _isPending.value = debouncer.getIsPending() // Update after trying to execute + console.log( + `[useDebouncer.ts] after maybeExecute, _isPending.value = ${_isPending.value}`, + ) + } + + const wrappedCancel = () => { + debouncer.cancel() + _isPending.value = debouncer.getIsPending() // Update after cancelling + } + + const wrappedFlush = () => { + // The core debouncer doesn't have a 'pending value' to flush to. + // It flushes by cancelling current timer and executing with last *successful* value if leading, or current *input* value for trailing. + // The previous logic was: cancel and then maybeExecute the current `value.value`. + if (value.value !== undefined) { + // This is the current debounced value + debouncer.cancel() // This sets its internal _isPending to false + debouncer.maybeExecute(value.value) // This might make it pending again or execute immediately + } + _isPending.value = debouncer.getIsPending() // Sync after flush actions + } + + const wrappedSetOptions = ( + newOptions: Partial void>>, + ) => { + const currentCoreOptions = debouncer.getOptions() + const mergedOptions = { ...currentCoreOptions, ...newOptions } + debouncer.setOptions(getEffectiveOptions(mergedOptions)) // Re-wrap onExecute + _isPending.value = debouncer.getIsPending() // Sync after options change + // Note: if `wait` changes, the existing timer isn't automatically rescheduled by core setOptions. + // This behavior is inherited from the core debouncer. + } + + return { + value, + setValue: wrappedSetValue, + flush: wrappedFlush, + cancel: wrappedCancel, + isPending: readonly(_isPending), + executionCount: readonly(_executionCount), + setOptions: wrappedSetOptions, + getOptions: () => debouncer.getOptions(), + } +} diff --git a/packages/vue-pacer/src/index.ts b/packages/vue-pacer/src/index.ts new file mode 100644 index 00000000..d492d2de --- /dev/null +++ b/packages/vue-pacer/src/index.ts @@ -0,0 +1,2 @@ +export * from './debouncer' +export * from './types' diff --git a/packages/vue-pacer/src/types/index.ts b/packages/vue-pacer/src/types/index.ts new file mode 100644 index 00000000..27f7dc41 --- /dev/null +++ b/packages/vue-pacer/src/types/index.ts @@ -0,0 +1,11 @@ +import type { Ref } from 'vue' +import type { Debouncer } from '@tanstack/pacer/debouncer' + +export type VueDebouncer = Debouncer<(value: TValue) => void> & { + readonly value: TValue +} + +export type MaybeRef = T | Ref +export type MaybeRefOrGetter = MaybeRef | (() => T) +export type UnwrapRef = T extends Ref ? U : T +export type UnwrapMaybeRef = T extends Ref ? U : T diff --git a/packages/vue-pacer/tsconfig.json b/packages/vue-pacer/tsconfig.json new file mode 100644 index 00000000..44533f71 --- /dev/null +++ b/packages/vue-pacer/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "eslint.config.js", "vite.config.ts", "tests"] +} diff --git a/packages/vue-pacer/vite.config.ts b/packages/vue-pacer/vite.config.ts new file mode 100644 index 00000000..1e15167a --- /dev/null +++ b/packages/vue-pacer/vite.config.ts @@ -0,0 +1,27 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/config/vite' +import vue from '@vitejs/plugin-vue' +import packageJson from './package.json' + +const config = defineConfig({ + plugins: [vue()], + test: { + name: packageJson.name, + dir: './tests', + watch: false, + environment: 'jsdom', + globals: true, + }, +}) + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: [ + './src/debouncer/index.ts', + './src/index.ts', + './src/types/index.ts', + ], + srcDir: './src', + }), +) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ec46ae6..5e5fb667 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1264,6 +1264,28 @@ importers: specifier: ^2.11.6 version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + examples/vue/debounce: + dependencies: + '@tanstack/vue-pacer': + specifier: workspace:* + version: link:../../../packages/vue-pacer + vue: + specifier: ^3.3.0 + version: 3.5.16(typescript@5.8.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^4.5.0 + version: 4.6.2(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.16(typescript@5.8.3)) + typescript: + specifier: 5.8.3 + version: 5.8.3 + vite: + specifier: ^6.3.5 + version: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + vue-tsc: + specifier: ^1.8.8 + version: 1.8.27(typescript@5.8.3) + packages/pacer: {} packages/react-pacer: @@ -1307,6 +1329,31 @@ importers: specifier: ^2.11.6 version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + packages/vue-pacer: + dependencies: + '@tanstack/pacer': + specifier: workspace:* + version: link:../pacer + vue: + specifier: ^3.3.0 + version: 3.5.16(typescript@5.8.3) + devDependencies: + '@tanstack/config': + specifier: 0.18.0 + version: 0.18.0(@types/node@22.15.21)(eslint@9.27.0(jiti@2.4.2))(rollup@4.35.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0)) + '@vitejs/plugin-vue': + specifier: ^4.5.0 + version: 4.6.2(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.16(typescript@5.8.3)) + typescript: + specifier: 5.8.3 + version: 5.8.3 + vite: + specifier: ^6.3.5 + version: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + vitest: + specifier: ^3.1.4 + version: 3.1.4(@types/node@22.15.21)(jiti@2.4.2)(jsdom@26.1.0)(tsx@4.19.3)(yaml@2.7.0) + packages: '@adobe/css-tools@4.4.2': @@ -1397,10 +1444,18 @@ packages: resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.25.9': resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} @@ -1423,6 +1478,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.27.4': + resolution: {integrity: sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-proposal-private-methods@7.18.6': resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} @@ -1476,6 +1536,10 @@ packages: resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} + '@babel/types@7.27.3': + resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} + engines: {node: '>=6.9.0'} + '@changesets/apply-release-plan@7.0.12': resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} @@ -2399,6 +2463,13 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitejs/plugin-vue@4.6.2': + resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 || ^5.0.0 + vue: ^3.2.25 + '@vitest/expect@3.1.4': resolution: {integrity: sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==} @@ -2428,24 +2499,53 @@ packages: '@vitest/utils@3.1.4': resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==} + '@volar/language-core@1.11.1': + resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==} + '@volar/language-core@2.4.12': resolution: {integrity: sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA==} + '@volar/source-map@1.11.1': + resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==} + '@volar/source-map@2.4.12': resolution: {integrity: sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw==} + '@volar/typescript@1.11.1': + resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==} + '@volar/typescript@2.4.12': resolution: {integrity: sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g==} '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + '@vue/compiler-core@3.5.16': + resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} + '@vue/compiler-dom@3.5.13': resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + '@vue/compiler-dom@3.5.16': + resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} + + '@vue/compiler-sfc@3.5.16': + resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} + + '@vue/compiler-ssr@3.5.16': + resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} + '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + '@vue/language-core@1.8.27': + resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@vue/language-core@2.1.6': resolution: {integrity: sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==} peerDependencies: @@ -2454,9 +2554,26 @@ packages: typescript: optional: true + '@vue/reactivity@3.5.16': + resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} + + '@vue/runtime-core@3.5.16': + resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} + + '@vue/runtime-dom@3.5.16': + resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} + + '@vue/server-renderer@3.5.16': + resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} + peerDependencies: + vue: 3.5.16 + '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@vue/shared@3.5.16': + resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} + '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -3643,6 +3760,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + muggle-string@0.3.1: + resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} @@ -4478,6 +4598,23 @@ packages: peerDependencies: eslint: '>=6.0.0' + vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + + vue-tsc@1.8.27: + resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} + hasBin: true + peerDependencies: + typescript: '*' + + vue@3.5.16: + resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -4755,8 +4892,12 @@ snapshots: '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-option@7.25.9': {} '@babel/helpers@7.26.9': @@ -4777,6 +4918,10 @@ snapshots: dependencies: '@babel/types': 7.27.0 + '@babel/parser@7.27.4': + dependencies: + '@babel/types': 7.27.3 + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -4850,6 +4995,11 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@babel/types@7.27.3': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@changesets/apply-release-plan@7.0.12': dependencies: '@changesets/config': 3.1.1 @@ -5933,6 +6083,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-vue@4.6.2(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0))(vue@3.5.16(typescript@5.8.3))': + dependencies: + vite: 6.3.5(@types/node@22.15.21)(jiti@2.4.2)(tsx@4.19.3)(yaml@2.7.0) + vue: 3.5.16(typescript@5.8.3) + '@vitest/expect@3.1.4': dependencies: '@vitest/spy': 3.1.4 @@ -5973,12 +6128,25 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 + '@volar/language-core@1.11.1': + dependencies: + '@volar/source-map': 1.11.1 + '@volar/language-core@2.4.12': dependencies: '@volar/source-map': 2.4.12 + '@volar/source-map@1.11.1': + dependencies: + muggle-string: 0.3.1 + '@volar/source-map@2.4.12': {} + '@volar/typescript@1.11.1': + dependencies: + '@volar/language-core': 1.11.1 + path-browserify: 1.0.1 + '@volar/typescript@2.4.12': dependencies: '@volar/language-core': 2.4.12 @@ -5993,16 +6161,60 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 + '@vue/compiler-core@3.5.16': + dependencies: + '@babel/parser': 7.27.4 + '@vue/shared': 3.5.16 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-dom@3.5.13': dependencies: '@vue/compiler-core': 3.5.13 '@vue/shared': 3.5.13 + '@vue/compiler-dom@3.5.16': + dependencies: + '@vue/compiler-core': 3.5.16 + '@vue/shared': 3.5.16 + + '@vue/compiler-sfc@3.5.16': + dependencies: + '@babel/parser': 7.27.4 + '@vue/compiler-core': 3.5.16 + '@vue/compiler-dom': 3.5.16 + '@vue/compiler-ssr': 3.5.16 + '@vue/shared': 3.5.16 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.3 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.16': + dependencies: + '@vue/compiler-dom': 3.5.16 + '@vue/shared': 3.5.16 + '@vue/compiler-vue2@2.7.16': dependencies: de-indent: 1.0.2 he: 1.2.0 + '@vue/language-core@1.8.27(typescript@5.8.3)': + dependencies: + '@volar/language-core': 1.11.1 + '@volar/source-map': 1.11.1 + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 + computeds: 0.0.1 + minimatch: 9.0.5 + muggle-string: 0.3.1 + path-browserify: 1.0.1 + vue-template-compiler: 2.7.16 + optionalDependencies: + typescript: 5.8.3 + '@vue/language-core@2.1.6(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.12 @@ -6016,8 +6228,32 @@ snapshots: optionalDependencies: typescript: 5.8.3 + '@vue/reactivity@3.5.16': + dependencies: + '@vue/shared': 3.5.16 + + '@vue/runtime-core@3.5.16': + dependencies: + '@vue/reactivity': 3.5.16 + '@vue/shared': 3.5.16 + + '@vue/runtime-dom@3.5.16': + dependencies: + '@vue/reactivity': 3.5.16 + '@vue/runtime-core': 3.5.16 + '@vue/shared': 3.5.16 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3))': + dependencies: + '@vue/compiler-ssr': 3.5.16 + '@vue/shared': 3.5.16 + vue: 3.5.16(typescript@5.8.3) + '@vue/shared@3.5.13': {} + '@vue/shared@3.5.16': {} + '@yarnpkg/lockfile@1.1.0': {} '@yarnpkg/parsers@3.0.2': @@ -7292,6 +7528,8 @@ snapshots: ms@2.1.3: {} + muggle-string@0.3.1: {} + muggle-string@0.4.1: {} nanoid@3.3.9: {} @@ -8127,6 +8365,28 @@ snapshots: transitivePeerDependencies: - supports-color + vue-template-compiler@2.7.16: + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + vue-tsc@1.8.27(typescript@5.8.3): + dependencies: + '@volar/typescript': 1.11.1 + '@vue/language-core': 1.8.27(typescript@5.8.3) + semver: 7.7.1 + typescript: 5.8.3 + + vue@3.5.16(typescript@5.8.3): + dependencies: + '@vue/compiler-dom': 3.5.16 + '@vue/compiler-sfc': 3.5.16 + '@vue/runtime-dom': 3.5.16 + '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.8.3)) + '@vue/shared': 3.5.16 + optionalDependencies: + typescript: 5.8.3 + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0