@andabove/vuqs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-05-18
4
+
5
+ Initial public release.
6
+
7
+ ### Added
8
+
9
+ - `useQueryState(key, parser)` — binds a typed Vue `Ref` to a URL query parameter via Vue Router. Supports `Ref<T>` (with default) and `Ref<T | null>` (without).
10
+ - Built-in parsers: `parseAsString`, `parseAsInteger`, `parseAsFloat`, `parseAsBoolean`
11
+ - Collection parsers: `parseAsArrayOf(itemParser, separator?)`
12
+ - Enum / literal parsers: `parseAsStringLiteral(values)`, `parseAsStringEnum(values)`
13
+ - JSON parser: `parseAsJson<T>(parseFn)` — validator-agnostic; pass any `(raw: unknown) => T | null` function (Zod, Valibot, etc.)
14
+ - `createParser(config)` — public factory for building custom parsers with the full builder API
15
+ - Builder pattern: `.withDefault(value)` and `.withOptions({ mode })` on all parsers
16
+ - History mode option: `mode: "replace" | "push"` (defaults to `"replace"`)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 andabove
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @andabove/vuqs
2
+
3
+ Type-safe URL query state for Vue 3 — like `ref`, but stored in the URL.
4
+
5
+ Documentation lives in the [repository root README](https://github.com/andabove/vuqs#readme).
@@ -0,0 +1,154 @@
1
+ import { Ref } from 'vue';
2
+
3
+ /** JSON primitive values. */
4
+ type JsonPrimitive = string | number | boolean | null;
5
+ /** JSON object values (recursive). */
6
+ interface JsonObject {
7
+ [key: string]: JsonValue;
8
+ }
9
+ /** JSON array values (recursive). */
10
+ type JsonArray = JsonValue[];
11
+ /** Any value that `JSON.stringify` can round-trip without data loss. */
12
+ type JsonValue = JsonPrimitive | JsonObject | JsonArray;
13
+ /**
14
+ * Controls how the router updates the URL when the query param changes.
15
+ * Defaults to "replace" to avoid polluting browser history with every keystroke.
16
+ */
17
+ interface QueryStateOptions {
18
+ mode?: "replace" | "push";
19
+ }
20
+ /**
21
+ * A parser without a default value. The ref it produces is `T | null`,
22
+ * where null means the query param is absent from the URL.
23
+ *
24
+ * Use `.withDefault(value)` to narrow the type to `T` and get a clean URL
25
+ * when the value equals the default.
26
+ */
27
+ interface Parser<T> {
28
+ parse(value: string): T | null;
29
+ serialize(value: T): string;
30
+ eq(a: T, b: T): boolean;
31
+ withDefault(defaultValue: T): ParserWithDefault<T>;
32
+ withOptions(options: QueryStateOptions): Parser<T>;
33
+ readonly defaultValue: undefined;
34
+ readonly options: QueryStateOptions;
35
+ }
36
+ /**
37
+ * A parser with a known default value. The ref it produces is `Ref<T>` —
38
+ * never null. When the value equals the default, the query param is removed
39
+ * from the URL, keeping URLs clean.
40
+ */
41
+ interface ParserWithDefault<T> {
42
+ parse(value: string): T | null;
43
+ serialize(value: T): string;
44
+ eq(a: T, b: T): boolean;
45
+ withDefault(defaultValue: T): ParserWithDefault<T>;
46
+ withOptions(options: QueryStateOptions): ParserWithDefault<T>;
47
+ readonly defaultValue: T;
48
+ readonly options: QueryStateOptions;
49
+ }
50
+
51
+ interface ParserConfig<T> {
52
+ parse: (value: string) => T | null;
53
+ serialize: (value: T) => string;
54
+ eq?: (a: T, b: T) => boolean;
55
+ }
56
+ /**
57
+ * Creates a typed parser you can pass to `useQueryState`.
58
+ * Exported so consumers can build custom parsers.
59
+ */
60
+ declare function createParser<T>(config: ParserConfig<T>, opts?: QueryStateOptions): Parser<T>;
61
+ /** Accepts any string as-is. A noop for serialization. */
62
+ declare const parseAsString: Parser<string>;
63
+ /** Parses with `parseInt` (base 10). Returns null for non-numeric strings. */
64
+ declare const parseAsInteger: Parser<number>;
65
+ /** Parses with `parseFloat`. Returns null for non-numeric strings. */
66
+ declare const parseAsFloat: Parser<number>;
67
+ /**
68
+ * Parses "true" and "false" only. Any other string (e.g. "1", "yes")
69
+ * returns null rather than coercing, keeping the URL as the source of truth.
70
+ */
71
+ declare const parseAsBoolean: Parser<boolean>;
72
+ /**
73
+ * Validates the query value against a readonly list of string literals.
74
+ * Returns null if the value is not in the list.
75
+ *
76
+ * @example
77
+ * const sort = useQueryState('sort', parseAsStringLiteral(['asc', 'desc'] as const))
78
+ */
79
+ declare function parseAsStringLiteral<const Literal extends string>(validValues: readonly Literal[]): Parser<Literal>;
80
+ /**
81
+ * Validates the query value against a TypeScript string enum.
82
+ * Returns null if the value is not a member of the enum.
83
+ *
84
+ * @example
85
+ * enum Status { Active = 'active', Inactive = 'inactive' }
86
+ * const status = useQueryState('status', parseAsStringEnum<Status>(Object.values(Status)))
87
+ */
88
+ declare function parseAsStringEnum<Enum extends string>(validValues: Enum[]): Parser<Enum>;
89
+ /**
90
+ * Serializes/deserializes a JSON value in the query string.
91
+ *
92
+ * Pass any `(raw: unknown) => T | null` function to validate the parsed JSON.
93
+ * This is validator-agnostic — works with Zod, Valibot, or any custom logic.
94
+ * Returns null if the string is not valid JSON or the parse function returns null.
95
+ *
96
+ * Uses deep equality (`JSON.stringify` comparison) for the `eq` check so
97
+ * that setting to the default value still cleans the URL correctly.
98
+ *
99
+ * `T` must be JSON-serialisable (`JsonValue`). The `parseFn` must return only
100
+ * values that `JSON.stringify` can round-trip (no `Date`, `BigInt`, `Map`, etc.).
101
+ *
102
+ * @example
103
+ * // with Valibot
104
+ * const schema = v.object({ q: v.string() })
105
+ * const filter = useQueryState('filter', parseAsJson((raw) => {
106
+ * const r = v.safeParse(schema, raw)
107
+ * return r.success ? r.output : null
108
+ * }))
109
+ *
110
+ * @example
111
+ * // with Zod
112
+ * const schema = z.object({ q: z.string() })
113
+ * const filter = useQueryState('filter', parseAsJson((raw) => {
114
+ * const r = schema.safeParse(raw)
115
+ * return r.success ? r.data : null
116
+ * }))
117
+ */
118
+ declare function parseAsJson<T extends JsonValue>(parseFn: (raw: unknown) => T | null): Parser<T>;
119
+ /**
120
+ * Encodes an array as a comma-separated query string value.
121
+ * The separator character is URI-encoded inside item values so it is safe
122
+ * to use in items that contain the separator character.
123
+ *
124
+ * An empty array serializes to an empty string, and an empty string parses
125
+ * back to an empty array.
126
+ *
127
+ * @example
128
+ * const tags = useQueryState('tags', parseAsArrayOf(parseAsString))
129
+ * // ?tags=a,b,c → ['a', 'b', 'c']
130
+ */
131
+ declare function parseAsArrayOf<ItemType>(itemParser: Parser<ItemType> | ParserWithDefault<ItemType>, separator?: string): Parser<ItemType[]>;
132
+
133
+ /**
134
+ * Binds a typed ref to a URL query parameter.
135
+ *
136
+ * When a parser with a default is provided the ref type is `Ref<T>` and the
137
+ * query param is removed from the URL whenever the value equals the default,
138
+ * keeping URLs clean (e.g. `/products` instead of `/products?filter=all`).
139
+ *
140
+ * When no default is provided the ref type is `Ref<T | null>`, where null
141
+ * means the param is absent from the URL.
142
+ *
143
+ * @example
144
+ * // Ref<string | null> — null when ?q is absent
145
+ * const search = useQueryState('q', parseAsString)
146
+ *
147
+ * // Ref<number> — URL is clean when page === 1
148
+ * const page = useQueryState('page', parseAsInteger.withDefault(1))
149
+ */
150
+ declare function useQueryState<T>(name: string, parser: ParserWithDefault<T>): Ref<T>;
151
+ declare function useQueryState<T>(name: string, parser: Parser<T>): Ref<T | null>;
152
+
153
+ export { createParser, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsInteger, parseAsJson, parseAsString, parseAsStringEnum, parseAsStringLiteral, useQueryState };
154
+ export type { JsonArray, JsonObject, JsonPrimitive, JsonValue, Parser, ParserWithDefault, QueryStateOptions };
@@ -0,0 +1,154 @@
1
+ import { Ref } from 'vue';
2
+
3
+ /** JSON primitive values. */
4
+ type JsonPrimitive = string | number | boolean | null;
5
+ /** JSON object values (recursive). */
6
+ interface JsonObject {
7
+ [key: string]: JsonValue;
8
+ }
9
+ /** JSON array values (recursive). */
10
+ type JsonArray = JsonValue[];
11
+ /** Any value that `JSON.stringify` can round-trip without data loss. */
12
+ type JsonValue = JsonPrimitive | JsonObject | JsonArray;
13
+ /**
14
+ * Controls how the router updates the URL when the query param changes.
15
+ * Defaults to "replace" to avoid polluting browser history with every keystroke.
16
+ */
17
+ interface QueryStateOptions {
18
+ mode?: "replace" | "push";
19
+ }
20
+ /**
21
+ * A parser without a default value. The ref it produces is `T | null`,
22
+ * where null means the query param is absent from the URL.
23
+ *
24
+ * Use `.withDefault(value)` to narrow the type to `T` and get a clean URL
25
+ * when the value equals the default.
26
+ */
27
+ interface Parser<T> {
28
+ parse(value: string): T | null;
29
+ serialize(value: T): string;
30
+ eq(a: T, b: T): boolean;
31
+ withDefault(defaultValue: T): ParserWithDefault<T>;
32
+ withOptions(options: QueryStateOptions): Parser<T>;
33
+ readonly defaultValue: undefined;
34
+ readonly options: QueryStateOptions;
35
+ }
36
+ /**
37
+ * A parser with a known default value. The ref it produces is `Ref<T>` —
38
+ * never null. When the value equals the default, the query param is removed
39
+ * from the URL, keeping URLs clean.
40
+ */
41
+ interface ParserWithDefault<T> {
42
+ parse(value: string): T | null;
43
+ serialize(value: T): string;
44
+ eq(a: T, b: T): boolean;
45
+ withDefault(defaultValue: T): ParserWithDefault<T>;
46
+ withOptions(options: QueryStateOptions): ParserWithDefault<T>;
47
+ readonly defaultValue: T;
48
+ readonly options: QueryStateOptions;
49
+ }
50
+
51
+ interface ParserConfig<T> {
52
+ parse: (value: string) => T | null;
53
+ serialize: (value: T) => string;
54
+ eq?: (a: T, b: T) => boolean;
55
+ }
56
+ /**
57
+ * Creates a typed parser you can pass to `useQueryState`.
58
+ * Exported so consumers can build custom parsers.
59
+ */
60
+ declare function createParser<T>(config: ParserConfig<T>, opts?: QueryStateOptions): Parser<T>;
61
+ /** Accepts any string as-is. A noop for serialization. */
62
+ declare const parseAsString: Parser<string>;
63
+ /** Parses with `parseInt` (base 10). Returns null for non-numeric strings. */
64
+ declare const parseAsInteger: Parser<number>;
65
+ /** Parses with `parseFloat`. Returns null for non-numeric strings. */
66
+ declare const parseAsFloat: Parser<number>;
67
+ /**
68
+ * Parses "true" and "false" only. Any other string (e.g. "1", "yes")
69
+ * returns null rather than coercing, keeping the URL as the source of truth.
70
+ */
71
+ declare const parseAsBoolean: Parser<boolean>;
72
+ /**
73
+ * Validates the query value against a readonly list of string literals.
74
+ * Returns null if the value is not in the list.
75
+ *
76
+ * @example
77
+ * const sort = useQueryState('sort', parseAsStringLiteral(['asc', 'desc'] as const))
78
+ */
79
+ declare function parseAsStringLiteral<const Literal extends string>(validValues: readonly Literal[]): Parser<Literal>;
80
+ /**
81
+ * Validates the query value against a TypeScript string enum.
82
+ * Returns null if the value is not a member of the enum.
83
+ *
84
+ * @example
85
+ * enum Status { Active = 'active', Inactive = 'inactive' }
86
+ * const status = useQueryState('status', parseAsStringEnum<Status>(Object.values(Status)))
87
+ */
88
+ declare function parseAsStringEnum<Enum extends string>(validValues: Enum[]): Parser<Enum>;
89
+ /**
90
+ * Serializes/deserializes a JSON value in the query string.
91
+ *
92
+ * Pass any `(raw: unknown) => T | null` function to validate the parsed JSON.
93
+ * This is validator-agnostic — works with Zod, Valibot, or any custom logic.
94
+ * Returns null if the string is not valid JSON or the parse function returns null.
95
+ *
96
+ * Uses deep equality (`JSON.stringify` comparison) for the `eq` check so
97
+ * that setting to the default value still cleans the URL correctly.
98
+ *
99
+ * `T` must be JSON-serialisable (`JsonValue`). The `parseFn` must return only
100
+ * values that `JSON.stringify` can round-trip (no `Date`, `BigInt`, `Map`, etc.).
101
+ *
102
+ * @example
103
+ * // with Valibot
104
+ * const schema = v.object({ q: v.string() })
105
+ * const filter = useQueryState('filter', parseAsJson((raw) => {
106
+ * const r = v.safeParse(schema, raw)
107
+ * return r.success ? r.output : null
108
+ * }))
109
+ *
110
+ * @example
111
+ * // with Zod
112
+ * const schema = z.object({ q: z.string() })
113
+ * const filter = useQueryState('filter', parseAsJson((raw) => {
114
+ * const r = schema.safeParse(raw)
115
+ * return r.success ? r.data : null
116
+ * }))
117
+ */
118
+ declare function parseAsJson<T extends JsonValue>(parseFn: (raw: unknown) => T | null): Parser<T>;
119
+ /**
120
+ * Encodes an array as a comma-separated query string value.
121
+ * The separator character is URI-encoded inside item values so it is safe
122
+ * to use in items that contain the separator character.
123
+ *
124
+ * An empty array serializes to an empty string, and an empty string parses
125
+ * back to an empty array.
126
+ *
127
+ * @example
128
+ * const tags = useQueryState('tags', parseAsArrayOf(parseAsString))
129
+ * // ?tags=a,b,c → ['a', 'b', 'c']
130
+ */
131
+ declare function parseAsArrayOf<ItemType>(itemParser: Parser<ItemType> | ParserWithDefault<ItemType>, separator?: string): Parser<ItemType[]>;
132
+
133
+ /**
134
+ * Binds a typed ref to a URL query parameter.
135
+ *
136
+ * When a parser with a default is provided the ref type is `Ref<T>` and the
137
+ * query param is removed from the URL whenever the value equals the default,
138
+ * keeping URLs clean (e.g. `/products` instead of `/products?filter=all`).
139
+ *
140
+ * When no default is provided the ref type is `Ref<T | null>`, where null
141
+ * means the param is absent from the URL.
142
+ *
143
+ * @example
144
+ * // Ref<string | null> — null when ?q is absent
145
+ * const search = useQueryState('q', parseAsString)
146
+ *
147
+ * // Ref<number> — URL is clean when page === 1
148
+ * const page = useQueryState('page', parseAsInteger.withDefault(1))
149
+ */
150
+ declare function useQueryState<T>(name: string, parser: ParserWithDefault<T>): Ref<T>;
151
+ declare function useQueryState<T>(name: string, parser: Parser<T>): Ref<T | null>;
152
+
153
+ export { createParser, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsInteger, parseAsJson, parseAsString, parseAsStringEnum, parseAsStringLiteral, useQueryState };
154
+ export type { JsonArray, JsonObject, JsonPrimitive, JsonValue, Parser, ParserWithDefault, QueryStateOptions };
package/dist/index.mjs ADDED
@@ -0,0 +1,132 @@
1
+ import { useRouteQuery } from '@vueuse/router';
2
+
3
+ function createParser(config, opts = {}) {
4
+ const eq = config.eq ?? ((a, b) => a === b);
5
+ return {
6
+ parse: config.parse,
7
+ serialize: config.serialize,
8
+ eq,
9
+ defaultValue: void 0,
10
+ options: opts,
11
+ withDefault(defaultValue) {
12
+ return createParserWithDefault(config, defaultValue, opts);
13
+ },
14
+ withOptions(options) {
15
+ return createParser(config, { ...opts, ...options });
16
+ }
17
+ };
18
+ }
19
+ function createParserWithDefault(config, defaultVal, opts = {}) {
20
+ const eq = config.eq ?? ((a, b) => a === b);
21
+ return {
22
+ parse: config.parse,
23
+ serialize: config.serialize,
24
+ eq,
25
+ defaultValue: defaultVal,
26
+ options: opts,
27
+ withDefault(defaultValue) {
28
+ return createParserWithDefault(config, defaultValue, opts);
29
+ },
30
+ withOptions(options) {
31
+ return createParserWithDefault(config, defaultVal, { ...opts, ...options });
32
+ }
33
+ };
34
+ }
35
+ const parseAsString = createParser({
36
+ parse: (value) => value,
37
+ serialize: (value) => value
38
+ });
39
+ const parseAsInteger = createParser({
40
+ parse: (value) => {
41
+ const n = parseInt(value, 10);
42
+ return isNaN(n) ? null : n;
43
+ },
44
+ serialize: (value) => String(Math.round(value))
45
+ });
46
+ const parseAsFloat = createParser({
47
+ parse: (value) => {
48
+ const n = parseFloat(value);
49
+ return isNaN(n) ? null : n;
50
+ },
51
+ serialize: (value) => String(value)
52
+ });
53
+ const parseAsBoolean = createParser({
54
+ parse: (value) => {
55
+ if (value === "true") return true;
56
+ if (value === "false") return false;
57
+ return null;
58
+ },
59
+ serialize: (value) => String(value)
60
+ });
61
+ function parseAsStringLiteral(validValues) {
62
+ return createParser({
63
+ parse: (query) => validValues.includes(query) ? query : null,
64
+ serialize: (value) => value
65
+ });
66
+ }
67
+ function parseAsStringEnum(validValues) {
68
+ return parseAsStringLiteral(validValues);
69
+ }
70
+ function parseAsJson(parseFn) {
71
+ return createParser({
72
+ parse: (str) => {
73
+ try {
74
+ return parseFn(JSON.parse(str));
75
+ } catch {
76
+ return null;
77
+ }
78
+ },
79
+ serialize: (value) => JSON.stringify(value),
80
+ eq: (a, b) => a === b || JSON.stringify(a) === JSON.stringify(b)
81
+ });
82
+ }
83
+ function parseAsArrayOf(itemParser, separator = ",") {
84
+ return createParser({
85
+ parse: (query) => {
86
+ if (query === "") return [];
87
+ return query.split(separator).map((item) => {
88
+ try {
89
+ return itemParser.parse(decodeURIComponent(item));
90
+ } catch {
91
+ return null;
92
+ }
93
+ }).filter((value) => value !== null);
94
+ },
95
+ serialize: (values) => values.map((value) => encodeURIComponent(itemParser.serialize(value))).join(separator),
96
+ eq: (a, b) => {
97
+ if (a === b) return true;
98
+ if (a.length !== b.length) return false;
99
+ const itemEq = itemParser.eq.bind(itemParser);
100
+ return a.every((value, index) => itemEq(value, b[index]));
101
+ }
102
+ });
103
+ }
104
+
105
+ function resolveRawString(v) {
106
+ if (v === null || v === void 0) return null;
107
+ if (Array.isArray(v)) return v[0] ?? null;
108
+ return v;
109
+ }
110
+ function useQueryState(name, parser) {
111
+ const hasDefault = parser.defaultValue !== void 0;
112
+ const defaultValue = hasDefault ? parser.defaultValue : null;
113
+ const mode = parser.options.mode ?? "replace";
114
+ return useRouteQuery(name, null, {
115
+ mode,
116
+ transform: {
117
+ get(v) {
118
+ const str = resolveRawString(v);
119
+ if (str === null) return defaultValue;
120
+ const parsed = parser.parse(str);
121
+ return parsed ?? defaultValue;
122
+ },
123
+ set(v) {
124
+ if (v === null) return null;
125
+ if (hasDefault && parser.eq(v, defaultValue)) return null;
126
+ return parser.serialize(v);
127
+ }
128
+ }
129
+ });
130
+ }
131
+
132
+ export { createParser, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsInteger, parseAsJson, parseAsString, parseAsStringEnum, parseAsStringLiteral, useQueryState };
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@andabove/vuqs",
3
+ "version": "0.1.0",
4
+ "description": "Type-safe URL query state for Vue 3 — like ref, but stored in the URL",
5
+ "keywords": [
6
+ "composable",
7
+ "query-state",
8
+ "search-params",
9
+ "type-safe",
10
+ "url",
11
+ "vue",
12
+ "vue-router",
13
+ "vue3"
14
+ ],
15
+ "homepage": "https://github.com/andabove/vuqs#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/andabove/vuqs/issues"
18
+ },
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/andabove/vuqs",
23
+ "directory": "packages/vuqs"
24
+ },
25
+ "files": [
26
+ "CHANGELOG.md",
27
+ "dist",
28
+ "LICENSE",
29
+ "README.md"
30
+ ],
31
+ "type": "module",
32
+ "sideEffects": false,
33
+ "main": "./dist/index.mjs",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.mjs"
39
+ }
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "devDependencies": {
45
+ "@vueuse/router": "^14.3.0",
46
+ "happy-dom": "^20.9.0",
47
+ "typescript": "^5.9.2",
48
+ "unbuild": "^3.6.1",
49
+ "valibot": "^1.4.0",
50
+ "vitest": "^4.1.7",
51
+ "vue": "^3.5.34",
52
+ "vue-router": "^4",
53
+ "vue-tsc": "^3.3.1"
54
+ },
55
+ "peerDependencies": {
56
+ "@vueuse/router": "^14.0.0",
57
+ "vue": ">=3.5",
58
+ "vue-router": "^4"
59
+ },
60
+ "engines": {
61
+ "node": ">=18"
62
+ },
63
+ "scripts": {
64
+ "build": "unbuild",
65
+ "test": "vitest",
66
+ "test:run": "vitest run",
67
+ "typecheck": "vue-tsc --noEmit -p tsconfig.json"
68
+ }
69
+ }