usewagen 0.0.0 → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmet Tinastepe
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,31 @@
1
+ # usewagen
2
+
3
+ Reactive state for URL and storage in Vue. Type-safe, with the source always in charge.
4
+
5
+ Documentation lives at [usewagen.dev](https://usewagen.dev).
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add usewagen
11
+ ```
12
+
13
+ `vue` is a peer dependency. `vue-router` is only needed if you use the router composables.
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { parseAsInteger } from 'usewagen'
19
+ import { useRouteState } from 'usewagen/router'
20
+ import { useLocalStorage } from 'usewagen/storage'
21
+
22
+ const page = useRouteState({ key: 'page', parser: parseAsInteger.withDefault(1) })
23
+ const theme = useLocalStorage({ key: 'theme' })
24
+ ```
25
+
26
+ `page` is a `Ref<number>` over `?page=` and `theme` a `Ref<string | null>` over the `theme`
27
+ entry in `localStorage`.
28
+
29
+ ## License
30
+
31
+ [MIT](./LICENSE)
package/client.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ declare module 'virtual:usewagen/parsers' {
2
+ import type { Parser } from 'usewagen'
3
+
4
+ export const parsers: Record<string, Parser<any>>
5
+ }
@@ -0,0 +1,15 @@
1
+ import { a as parseAsFloat, i as parseAsDate, o as parseAsIndex, r as parseAsBoolean, s as parseAsInteger, u as parseAsString } from "./parsers-CA8aZuF-.mjs";
2
+ //#region src/parser/builtins.ts
3
+ const builtinParsers = {
4
+ parseAsString,
5
+ parseAsInteger,
6
+ parseAsFloat,
7
+ parseAsIndex,
8
+ parseAsBoolean,
9
+ parseAsDate
10
+ };
11
+ function isBuiltinParserName(name) {
12
+ return Object.hasOwn(builtinParsers, name);
13
+ }
14
+ //#endregion
15
+ export { isBuiltinParserName as n, builtinParsers as t };
@@ -0,0 +1,38 @@
1
+ //#region src/storage/create-storage.d.ts
2
+ type ErrorHandler = (error: unknown) => void;
3
+ type Unsubscribe = () => void;
4
+ type KeyListener = () => void;
5
+ type StorageListener = (key: string | null) => void;
6
+ interface StorageAdapter {
7
+ getItem: (key: string) => string | null;
8
+ setItem: (key: string, value: string) => void;
9
+ removeItem: (key: string) => void;
10
+ keys: () => string[];
11
+ watch?: (onChange: (key: string | null) => void) => Unsubscribe;
12
+ }
13
+ interface StorageConfig {
14
+ prefix?: string;
15
+ crossTab?: boolean;
16
+ onError?: ErrorHandler;
17
+ }
18
+ type StorageOptions = StorageAdapter & StorageConfig;
19
+ interface StorageInstance {
20
+ readonly name: string;
21
+ readonly prefix: string;
22
+ getItem: (key: string) => string | null;
23
+ setItem: (key: string, value: string) => void;
24
+ removeItem: (key: string) => void;
25
+ has: (key: string) => boolean;
26
+ keys: () => string[];
27
+ clear: (options?: {
28
+ except?: string[];
29
+ }) => void;
30
+ subscribe: {
31
+ (key: string, listener: KeyListener): Unsubscribe;
32
+ (listener: StorageListener): Unsubscribe;
33
+ };
34
+ destroy: () => void;
35
+ }
36
+ declare function createStorage(name: string, options: StorageOptions): StorageInstance;
37
+ //#endregion
38
+ export { StorageInstance as a, Unsubscribe as c, StorageConfig as i, createStorage as l, KeyListener as n, StorageListener as o, StorageAdapter as r, StorageOptions as s, ErrorHandler as t };
@@ -0,0 +1,46 @@
1
+ import { A as unwrapDefault, C as parseAsInteger, D as parseAsStringEnum, E as parseAsString, O as parseAsStringLiteral, S as parseAsIndex, T as parseAsNumberLiteral, _ as defineParser, a as CustomParsers, b as parseAsDate, c as InferParserValue, d as UnwrapParser, f as WithParser, g as ParserWithDefault, h as ParserOptions, i as BuiltinParsers, k as tryParse, l as KnownParsers, m as Parser, o as InferInputValue, p as DefaultValue, r as StateMode, s as InferInputWritable, u as ParserInput, v as parseAsArrayOf, w as parseAsJson, x as parseAsFloat, y as parseAsBoolean } from "./types-JtRmT3qo.mjs";
2
+ import { i as RouteStateSource, t as HistoryMode } from "./types-fVW6rBJ3.mjs";
3
+ import { a as StorageInstance, t as ErrorHandler } from "./create-storage-DESmB4bY.mjs";
4
+ import { App } from "vue";
5
+ //#region src/wagen.d.ts
6
+ interface WagenStorageOptions {
7
+ prefix?: string;
8
+ crossTab?: boolean;
9
+ onError?: ErrorHandler;
10
+ local?: StorageInstance;
11
+ session?: StorageInstance;
12
+ default?: 'local' | 'session';
13
+ mode?: StateMode;
14
+ }
15
+ interface WagenRouterOptions {
16
+ history?: HistoryMode;
17
+ source?: RouteStateSource;
18
+ clearOnDefault?: boolean;
19
+ mode?: StateMode;
20
+ }
21
+ type WagenParsers = Record<string, Parser<any>>;
22
+ interface WagenConfig {
23
+ parsers?: WagenParsers;
24
+ storage?: WagenStorageOptions;
25
+ router?: WagenRouterOptions;
26
+ }
27
+ interface WagenStorage {
28
+ readonly local: StorageInstance;
29
+ readonly session: StorageInstance;
30
+ readonly default: StorageInstance;
31
+ readonly mode: StateMode;
32
+ }
33
+ type ResolvedWagenRouterOptions = Required<WagenRouterOptions>;
34
+ interface Wagen {
35
+ readonly parsers: WagenParsers;
36
+ readonly storage: WagenStorage;
37
+ readonly router: ResolvedWagenRouterOptions;
38
+ install: (app: App) => void;
39
+ destroy: () => void;
40
+ }
41
+ export declare function defineWagenConfig(config: WagenConfig): WagenConfig;
42
+ export declare function createWagen(config?: WagenConfig): Wagen;
43
+ export declare function getActiveWagen(): Wagen;
44
+ export declare function useWagen(): Wagen;
45
+ //#endregion
46
+ export { type BuiltinParsers, type CustomParsers, DefaultValue, type InferInputValue, type InferInputWritable, type InferParserValue, type KnownParsers, Parser, type ParserInput, ParserOptions, ParserWithDefault, type ResolvedWagenRouterOptions, type StateMode, type UnwrapParser, type Wagen, type WagenConfig, type WagenParsers, type WagenRouterOptions, type WagenStorage, type WagenStorageOptions, type WithParser, defineParser, parseAsArrayOf, parseAsBoolean, parseAsDate, parseAsFloat, parseAsIndex, parseAsInteger, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, tryParse, unwrapDefault };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { a as parseAsFloat, c as parseAsJson, d as parseAsStringEnum, f as parseAsStringLiteral, i as parseAsDate, l as parseAsNumberLiteral, m as unwrapDefault, n as parseAsArrayOf, o as parseAsIndex, p as tryParse, r as parseAsBoolean, s as parseAsInteger, t as defineParser, u as parseAsString } from "./parsers-CA8aZuF-.mjs";
2
+ import "./parser-B6hfbAwA.mjs";
3
+ import { i as useWagen, n as defineWagenConfig, r as getActiveWagen, t as createWagen } from "./wagen-C2p8oQgu.mjs";
4
+ export { createWagen, defineParser, defineWagenConfig, getActiveWagen, parseAsArrayOf, parseAsBoolean, parseAsDate, parseAsFloat, parseAsIndex, parseAsInteger, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, tryParse, unwrapDefault, useWagen };
@@ -0,0 +1,2 @@
1
+ import "./parsers-CA8aZuF-.mjs";
2
+ export {};
@@ -0,0 +1,156 @@
1
+ //#region src/messages.ts
2
+ const PREFIX = "[usewagen]";
3
+ const ErrorCodes = {
4
+ PARSE_FAILED: 0,
5
+ UNKNOWN_PARSER_NAME: 1,
6
+ WEB_STORAGE_UNAVAILABLE: 2,
7
+ RESERVED_PARSER_NAME: 3,
8
+ DUPLICATE_PARSER_NAME: 4,
9
+ NO_INJECTION_CONTEXT: 5,
10
+ NO_EFFECT_SCOPE: 6
11
+ };
12
+ const errorMessages = {
13
+ [ErrorCodes.PARSE_FAILED]: "Failed to parse the input:",
14
+ [ErrorCodes.UNKNOWN_PARSER_NAME]: "No parser is registered under this name; falling back to `parseAsString`. Pass it to `createWagen({ parsers })`:",
15
+ [ErrorCodes.WEB_STORAGE_UNAVAILABLE]: "The storage area is not accessible; falling back to in-memory storage. Values will not persist:",
16
+ [ErrorCodes.RESERVED_PARSER_NAME]: "Skipping a parser whose name is reserved by a built-in:",
17
+ [ErrorCodes.DUPLICATE_PARSER_NAME]: "A parser with this name was already found in another file — the last one wins:",
18
+ [ErrorCodes.NO_INJECTION_CONTEXT]: "Composables should be called inside `setup()` or a function that runs in an injection context; falling back to the active instance. Outside one, use `getActiveWagen()` or the `define*` APIs:",
19
+ [ErrorCodes.NO_EFFECT_SCOPE]: "Composables should be called inside `setup()` or a running effect scope; outside one the effects they create are never released, which leaks:"
20
+ };
21
+ function getMessage(code) {
22
+ return `${PREFIX} ${errorMessages[code]}`;
23
+ }
24
+ function warn(code, ...details) {
25
+ console.warn(getMessage(code), ...details);
26
+ }
27
+ function warnDev(code, ...details) {
28
+ if (process.env.NODE_ENV !== "production") warn(code, ...details);
29
+ }
30
+ //#endregion
31
+ //#region src/parser/parsers.ts
32
+ function tryParse(fn, input) {
33
+ try {
34
+ return fn(input);
35
+ } catch (error) {
36
+ warn(ErrorCodes.PARSE_FAILED, input, error);
37
+ return null;
38
+ }
39
+ }
40
+ function defineParser(options) {
41
+ const parse = options.parse;
42
+ const serialize = options.serialize ?? String;
43
+ return {
44
+ parse,
45
+ serialize,
46
+ withDefault(value) {
47
+ const parser = defineParser({
48
+ parse,
49
+ serialize
50
+ });
51
+ Object.defineProperty(parser, "defaultValue", {
52
+ value,
53
+ enumerable: true
54
+ });
55
+ return parser;
56
+ }
57
+ };
58
+ }
59
+ function unwrapDefault(value) {
60
+ return typeof value === "function" ? value() : value;
61
+ }
62
+ const parseAsString = defineParser({
63
+ parse: (v) => v,
64
+ serialize: String
65
+ });
66
+ const parseAsInteger = defineParser({
67
+ parse: (v) => {
68
+ const n = Number.parseInt(v, 10);
69
+ return Number.isNaN(n) ? null : n;
70
+ },
71
+ serialize: (v) => Math.trunc(v).toString()
72
+ });
73
+ const parseAsFloat = defineParser({
74
+ parse: (v) => {
75
+ const n = Number.parseFloat(v);
76
+ return Number.isNaN(n) ? null : n;
77
+ },
78
+ serialize: (v) => v.toString()
79
+ });
80
+ const parseAsIndex = defineParser({
81
+ parse: (v) => {
82
+ const n = Number.parseInt(v, 10);
83
+ return Number.isNaN(n) ? null : n - 1;
84
+ },
85
+ serialize: (v) => (Math.trunc(v) + 1).toString()
86
+ });
87
+ const parseAsBoolean = defineParser({
88
+ parse: (v) => v === "true" ? true : v === "false" ? false : null,
89
+ serialize: (v) => v ? "true" : "false"
90
+ });
91
+ function parseAsStringLiteral(values) {
92
+ return defineParser({
93
+ parse: (v) => values.includes(v) ? v : null,
94
+ serialize: (v) => v.toString()
95
+ });
96
+ }
97
+ function parseAsNumberLiteral(values) {
98
+ return defineParser({
99
+ parse: (v) => {
100
+ const n = Number.parseFloat(v);
101
+ return values.includes(n) ? n : null;
102
+ },
103
+ serialize: (v) => v.toString()
104
+ });
105
+ }
106
+ function parseAsStringEnum(values) {
107
+ return parseAsStringLiteral(values);
108
+ }
109
+ const parseAsDate = Object.assign(defineParser({
110
+ parse: (v) => {
111
+ const d = new Date(v.includes("T") ? v : `${v}T00:00:00.000Z`);
112
+ return Number.isNaN(d.getTime()) ? null : d;
113
+ },
114
+ serialize: (v) => v.toISOString().slice(0, 10)
115
+ }), {
116
+ iso: () => defineParser({
117
+ parse: (v) => {
118
+ const d = new Date(v);
119
+ return Number.isNaN(d.getTime()) ? null : d;
120
+ },
121
+ serialize: (v) => v.toISOString()
122
+ }),
123
+ timestamp: () => defineParser({
124
+ parse: (v) => {
125
+ const n = Number.parseInt(v, 10);
126
+ if (Number.isNaN(n)) return null;
127
+ const d = new Date(n);
128
+ return Number.isNaN(d.getTime()) ? null : d;
129
+ },
130
+ serialize: (v) => v.getTime().toString()
131
+ })
132
+ });
133
+ function parseAsArrayOf(itemParser, separator = ",") {
134
+ const encodedSeparator = encodeURIComponent(separator);
135
+ return defineParser({
136
+ parse: (raw) => {
137
+ if (raw === "") return [];
138
+ return raw.split(separator).map((item) => tryParse(itemParser.parse, item.replaceAll(encodedSeparator, separator))).filter((value) => value !== null);
139
+ },
140
+ serialize: (values) => values.map((value) => itemParser.serialize(value).replaceAll(separator, encodedSeparator)).join(separator)
141
+ });
142
+ }
143
+ function parseAsJson() {
144
+ return defineParser({
145
+ parse: (v) => {
146
+ try {
147
+ return JSON.parse(v);
148
+ } catch {
149
+ return null;
150
+ }
151
+ },
152
+ serialize: (v) => JSON.stringify(v)
153
+ });
154
+ }
155
+ //#endregion
156
+ export { warnDev as _, parseAsFloat as a, parseAsJson as c, parseAsStringEnum as d, parseAsStringLiteral as f, warn as g, ErrorCodes as h, parseAsDate as i, parseAsNumberLiteral as l, unwrapDefault as m, parseAsArrayOf as n, parseAsIndex as o, tryParse as p, parseAsBoolean as r, parseAsInteger as s, defineParser as t, parseAsString as u };
@@ -0,0 +1,42 @@
1
+ import { f as WithParser, n as ReactiveOptions, o as InferInputValue, r as StateMode, s as InferInputWritable, t as ReactiveFields, u as ParserInput } from "../types-JtRmT3qo.mjs";
2
+ import { i as RouteStateSource, n as ResolvedRouteStateOptions, r as RouteStateOptions, t as HistoryMode } from "../types-fVW6rBJ3.mjs";
3
+ import { Ref } from "vue";
4
+ //#region src/router/use-route-hash.d.ts
5
+ interface RouteHashOptions {
6
+ parser?: ParserInput;
7
+ history?: HistoryMode;
8
+ clearOnDefault?: boolean;
9
+ mode?: StateMode;
10
+ }
11
+ type UseRouteHashOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<WithParser<RouteHashOptions, P>>;
12
+ type RouteHashConfig = ReactiveFields<RouteHashOptions>;
13
+ export declare function useRouteHash<P extends ParserInput | undefined = undefined>(options?: UseRouteHashOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
14
+ //#endregion
15
+ //#region src/router/use-route-states.d.ts
16
+ type RouteStateConfig<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveFields<WithParser<RouteStateOptions, P>, 'key'>;
17
+ interface BatchOptions {
18
+ history?: HistoryMode;
19
+ }
20
+ type Refs<TOptions extends readonly RouteStateConfig[]> = { [TOption in TOptions[number] as TOption['key']]: Ref<InferInputValue<TOption['parser']>, InferInputWritable<TOption['parser']>>; };
21
+ type StatePatch<TOptions extends readonly RouteStateConfig[]> = Partial<{ [TOption in TOptions[number] as TOption['key']]: InferInputWritable<TOption['parser']>; }>;
22
+ type StateSnapshot<TOptions extends readonly RouteStateConfig[]> = { [TOption in TOptions[number] as TOption['key']]: InferInputValue<TOption['parser']>; };
23
+ interface UseRouteStatesApi<TOptions extends readonly RouteStateConfig[]> {
24
+ set: (patch: StatePatch<TOptions>, options?: BatchOptions) => void;
25
+ reset: (options?: BatchOptions) => void;
26
+ toObject: () => StateSnapshot<TOptions>;
27
+ }
28
+ type UseRouteStatesReturn<TOptions extends readonly RouteStateConfig[]> = Refs<TOptions> & UseRouteStatesApi<TOptions>;
29
+ export declare function useRouteStates<const TOptions extends readonly RouteStateConfig[]>(configs: TOptions): UseRouteStatesReturn<TOptions>;
30
+ //#endregion
31
+ //#region src/router/options.d.ts
32
+ type Exact<T, TShape> = T & Record<Exclude<keyof T, keyof TShape>, never>;
33
+ export declare function routeStateOptions<const T extends RouteStateConfig>(options: Exact<T, RouteStateConfig>): T;
34
+ export declare function routeStateOptions<const T extends RouteStateConfig>(options: () => Exact<T, RouteStateConfig>): () => T;
35
+ export declare function routeHashOptions<const T extends RouteHashConfig>(options: Exact<T, RouteHashConfig>): T;
36
+ export declare function routeHashOptions<const T extends RouteHashConfig>(options: () => Exact<T, RouteHashConfig>): () => T;
37
+ //#endregion
38
+ //#region src/router/use-route-state.d.ts
39
+ type UseRouteStateOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<WithParser<RouteStateOptions, P>>;
40
+ export declare function useRouteState<P extends ParserInput | undefined = undefined>(options: UseRouteStateOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
41
+ //#endregion
42
+ export type { BatchOptions, HistoryMode, ResolvedRouteStateOptions, RouteHashConfig, RouteHashOptions, RouteStateConfig, RouteStateOptions, RouteStateSource, StateMode, UseRouteHashOptions, UseRouteStateOptions, UseRouteStatesApi, UseRouteStatesReturn };
@@ -0,0 +1,273 @@
1
+ import { _ as warnDev, h as ErrorCodes, m as unwrapDefault } from "../parsers-CA8aZuF-.mjs";
2
+ import "../parser-B6hfbAwA.mjs";
3
+ import { r as getActiveWagen } from "../wagen-C2p8oQgu.mjs";
4
+ import { a as toValueDeep, i as resolveParser, n as parseValue, r as serializeValue, t as normalizeRaw } from "../utils-EL2EiRpH.mjs";
5
+ import { computed, customRef, getCurrentScope, nextTick, toValue } from "vue";
6
+ import { useRoute, useRouter } from "vue-router";
7
+ //#region src/router/options.ts
8
+ function routeStateOptions(options) {
9
+ return options;
10
+ }
11
+ function routeHashOptions(options) {
12
+ return options;
13
+ }
14
+ //#endregion
15
+ //#region src/router/queue.ts
16
+ const batches = /* @__PURE__ */ new WeakMap();
17
+ function resolveBatchHistory(candidates, override) {
18
+ if (override) return override;
19
+ return candidates.includes("push") ? "push" : "replace";
20
+ }
21
+ function enqueue(router, write) {
22
+ let batch = batches.get(router);
23
+ if (!batch) {
24
+ const created = {
25
+ changes: [],
26
+ histories: [],
27
+ hash: void 0,
28
+ settled: Promise.resolve()
29
+ };
30
+ created.settled = nextTick().then(() => flush(router, created));
31
+ batches.set(router, created);
32
+ batch = created;
33
+ }
34
+ if (write.changes) batch.changes.push(...write.changes);
35
+ if (write.hash !== void 0) batch.hash = write.hash;
36
+ batch.histories.push(write.history);
37
+ return batch.settled;
38
+ }
39
+ function flush(router, batch) {
40
+ if (batches.get(router) === batch) batches.delete(router);
41
+ const route = router.currentRoute.value;
42
+ const params = { ...route.params };
43
+ const query = { ...route.query };
44
+ let changed = false;
45
+ for (const change of batch.changes) {
46
+ const target = change.source === "params" ? params : query;
47
+ const current = target[change.urlKey];
48
+ if (Array.isArray(current)) changed = true;
49
+ else if (change.serialized === null) changed ||= current !== void 0;
50
+ else changed ||= current !== change.serialized;
51
+ target[change.urlKey] = change.serialized === null ? void 0 : change.serialized;
52
+ }
53
+ const hash = batch.hash === void 0 ? route.hash : batch.hash ?? "";
54
+ changed ||= hash !== route.hash;
55
+ if (!changed) return Promise.resolve();
56
+ return router[resolveBatchHistory(batch.histories)]({
57
+ params,
58
+ query,
59
+ hash: hash === "" ? void 0 : hash
60
+ }).then(() => {}, () => {});
61
+ }
62
+ //#endregion
63
+ //#region src/router/use-route-hash.ts
64
+ function toResolvedHashOptions(options, defaults) {
65
+ return {
66
+ parser: resolveParser(options.parser),
67
+ history: options.history ?? defaults.history,
68
+ clearOnDefault: options.clearOnDefault ?? defaults.clearOnDefault,
69
+ mode: options.mode ?? defaults.mode
70
+ };
71
+ }
72
+ function useRouteHash(options = {}) {
73
+ if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteHash");
74
+ const route = useRoute();
75
+ const router = useRouter();
76
+ const defaults = getActiveWagen().router;
77
+ const resolvedOptions = computed(() => toResolvedHashOptions(toValueDeep(options), defaults));
78
+ let cache = null;
79
+ let generation = 0;
80
+ let trigger;
81
+ function currentHash() {
82
+ return route.hash === "" ? null : route.hash;
83
+ }
84
+ return customRef((track, triggerRef) => {
85
+ trigger = triggerRef;
86
+ return {
87
+ get: () => {
88
+ track();
89
+ const { parser, mode } = resolvedOptions.value;
90
+ const current = currentHash();
91
+ if (mode === "source") {
92
+ cache = null;
93
+ return parseValue(parser, current);
94
+ }
95
+ if (cache && cache.seen === current) return parseValue(parser, cache.raw);
96
+ cache = {
97
+ seen: current,
98
+ raw: current
99
+ };
100
+ return parseValue(parser, current);
101
+ },
102
+ set: (next) => {
103
+ const { parser, clearOnDefault, history, mode } = resolvedOptions.value;
104
+ const serialized = serializeValue(parser, clearOnDefault, next);
105
+ const optimistic = mode === "optimistic";
106
+ const own = ++generation;
107
+ if (optimistic) {
108
+ cache = {
109
+ seen: currentHash(),
110
+ raw: serialized
111
+ };
112
+ trigger();
113
+ }
114
+ enqueue(router, {
115
+ history,
116
+ hash: serialized
117
+ }).then(() => {
118
+ if (!optimistic || generation !== own) return;
119
+ cache = null;
120
+ trigger();
121
+ });
122
+ }
123
+ };
124
+ });
125
+ }
126
+ //#endregion
127
+ //#region src/router/use-base-route-state.ts
128
+ function useBaseRouteState() {
129
+ const route = useRoute();
130
+ const router = useRouter();
131
+ function createRouteStateRef(options) {
132
+ let cache = null;
133
+ let generation = 0;
134
+ let trigger;
135
+ function rawValue(urlKey, source) {
136
+ return source === "params" ? route.params[urlKey] : route.query[urlKey];
137
+ }
138
+ const state = customRef((track, triggerRef) => {
139
+ trigger = triggerRef;
140
+ return {
141
+ get: () => {
142
+ track();
143
+ const { urlKey, source, parser, mode } = toValue(options);
144
+ const current = normalizeRaw(rawValue(urlKey, source));
145
+ if (mode === "source") {
146
+ cache = null;
147
+ return parseValue(parser, current);
148
+ }
149
+ if (cache && cache.seen === current) return parseValue(parser, cache.raw);
150
+ cache = {
151
+ seen: current,
152
+ raw: current
153
+ };
154
+ return parseValue(parser, current);
155
+ },
156
+ set: (next) => {
157
+ const { parser, clearOnDefault } = toValue(options);
158
+ write(serializeValue(parser, clearOnDefault, next));
159
+ }
160
+ };
161
+ });
162
+ function write(serialized, history) {
163
+ const resolved = toValue(options);
164
+ const optimistic = resolved.mode === "optimistic";
165
+ const current = ++generation;
166
+ if (optimistic) {
167
+ cache = {
168
+ seen: normalizeRaw(rawValue(resolved.urlKey, resolved.source)),
169
+ raw: serialized
170
+ };
171
+ trigger();
172
+ }
173
+ return enqueue(router, {
174
+ history: history ?? resolved.history,
175
+ changes: [{
176
+ urlKey: resolved.urlKey,
177
+ source: resolved.source,
178
+ serialized
179
+ }]
180
+ }).then(() => {
181
+ if (!optimistic || generation !== current) return;
182
+ cache = null;
183
+ trigger();
184
+ });
185
+ }
186
+ return {
187
+ state,
188
+ write
189
+ };
190
+ }
191
+ return { createRouteStateRef };
192
+ }
193
+ //#endregion
194
+ //#region src/router/utils.ts
195
+ function toResolvedOptions(input, defaults) {
196
+ return {
197
+ key: input.key,
198
+ parser: resolveParser(input.parser),
199
+ urlKey: input.urlKey ?? input.key,
200
+ source: input.source ?? defaults.source,
201
+ history: input.history ?? defaults.history,
202
+ clearOnDefault: input.clearOnDefault ?? defaults.clearOnDefault,
203
+ mode: input.mode ?? defaults.mode
204
+ };
205
+ }
206
+ //#endregion
207
+ //#region src/router/use-route-state.ts
208
+ function useRouteState(options) {
209
+ if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteState");
210
+ const { createRouteStateRef } = useBaseRouteState();
211
+ const defaults = getActiveWagen().router;
212
+ return createRouteStateRef(computed(() => toResolvedOptions(toValueDeep(options), defaults))).state;
213
+ }
214
+ //#endregion
215
+ //#region src/router/use-route-states.ts
216
+ function useRouteStates(configs) {
217
+ if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteStates");
218
+ const { createRouteStateRef } = useBaseRouteState();
219
+ const defaults = getActiveWagen().router;
220
+ const resolvedList = configs.map((config) => computed(() => toResolvedOptions(toValueDeep(config), defaults)));
221
+ const handles = resolvedList.map((resolved) => createRouteStateRef(resolved));
222
+ const refs = Object.fromEntries(resolvedList.map((resolved, index) => [resolved.value.key, handles[index].state]));
223
+ function commit(writes, candidates, override) {
224
+ if (writes.length === 0) return;
225
+ const history = resolveBatchHistory(candidates, override);
226
+ for (const { handle, serialized } of writes) handle.write(serialized, history);
227
+ }
228
+ const api = {
229
+ set(patch, options) {
230
+ const writes = [];
231
+ const candidates = [];
232
+ resolvedList.forEach((resolved, index) => {
233
+ const { key, parser, clearOnDefault, history } = resolved.value;
234
+ if (!(key in patch)) return;
235
+ const next = patch[key];
236
+ writes.push({
237
+ handle: handles[index],
238
+ serialized: serializeValue(parser, clearOnDefault, next)
239
+ });
240
+ candidates.push(history);
241
+ });
242
+ commit(writes, candidates, options?.history);
243
+ },
244
+ reset(options) {
245
+ const writes = [];
246
+ const candidates = [];
247
+ resolvedList.forEach((resolved, index) => {
248
+ const { parser, clearOnDefault, history } = resolved.value;
249
+ const next = parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
250
+ writes.push({
251
+ handle: handles[index],
252
+ serialized: serializeValue(parser, clearOnDefault, next)
253
+ });
254
+ candidates.push(history);
255
+ });
256
+ commit(writes, candidates, options?.history);
257
+ },
258
+ toObject() {
259
+ const snapshot = {};
260
+ for (const resolved of resolvedList) {
261
+ const { key } = resolved.value;
262
+ snapshot[key] = refs[key].value;
263
+ }
264
+ return snapshot;
265
+ }
266
+ };
267
+ return {
268
+ ...refs,
269
+ ...api
270
+ };
271
+ }
272
+ //#endregion
273
+ export { routeHashOptions, routeStateOptions, useRouteHash, useRouteState, useRouteStates };
@@ -0,0 +1,37 @@
1
+ import { f as WithParser, n as ReactiveOptions, o as InferInputValue, r as StateMode, s as InferInputWritable, u as ParserInput } from "../types-JtRmT3qo.mjs";
2
+ import { a as StorageInstance, c as Unsubscribe, i as StorageConfig, l as createStorage, n as KeyListener, o as StorageListener, r as StorageAdapter, s as StorageOptions, t as ErrorHandler } from "../create-storage-DESmB4bY.mjs";
3
+ import { Ref } from "vue";
4
+ //#region src/storage/define-storage-state.d.ts
5
+ type StorageSource = StorageInstance | 'local' | 'session';
6
+ interface StorageStateOptions {
7
+ key: string;
8
+ storage?: StorageSource;
9
+ parser?: ParserInput;
10
+ clearOnDefault?: boolean;
11
+ mode?: StateMode;
12
+ }
13
+ interface StorageState<T = string | null, W = T | null | undefined> {
14
+ readonly key: string;
15
+ readonly storage: StorageInstance;
16
+ get: () => T;
17
+ set: (value: W) => void;
18
+ remove: () => void;
19
+ subscribe: (listener: () => void) => Unsubscribe;
20
+ }
21
+ export declare function defineStorageState<P extends ParserInput | undefined = undefined>(options: WithParser<StorageStateOptions, P>): StorageState<InferInputValue<P>, InferInputWritable<P>>;
22
+ //#endregion
23
+ //#region src/storage/presets.d.ts
24
+ export declare function createMemoryStorage(config?: StorageConfig): StorageInstance;
25
+ export declare function createLocalStorage(config?: StorageConfig): StorageInstance;
26
+ export declare function createSessionStorage(config?: StorageConfig): StorageInstance;
27
+ //#endregion
28
+ //#region src/storage/use-storage.d.ts
29
+ type UseStorageOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<WithParser<StorageStateOptions, P>>;
30
+ type UseLocalStorageOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<WithParser<Omit<StorageStateOptions, 'storage'>, P>>;
31
+ type UseSessionStorageOptions<P extends ParserInput | undefined = ParserInput | undefined> = UseLocalStorageOptions<P>;
32
+ export declare function useStorage<T, W>(state: StorageState<T, W>): Ref<T, W>;
33
+ export declare function useStorage<P extends ParserInput | undefined = undefined>(options: UseStorageOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
34
+ export declare function useLocalStorage<P extends ParserInput | undefined = undefined>(options: UseLocalStorageOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
35
+ export declare function useSessionStorage<P extends ParserInput | undefined = undefined>(options: UseSessionStorageOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
36
+ //#endregion
37
+ export { type ErrorHandler, type KeyListener, type StateMode, type StorageAdapter, type StorageConfig, type StorageInstance, type StorageListener, type StorageOptions, type StorageSource, type StorageState, type StorageStateOptions, type Unsubscribe, type UseLocalStorageOptions, type UseSessionStorageOptions, type UseStorageOptions, createStorage };