usewagen 0.1.0 → 0.3.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # usewagen
2
2
 
3
- Reactive state for URL and storage in Vue. Type-safe, with the value in exactly one place.
3
+ Reactive state for URL and storage in Vue. Type-safe, with the source always in charge.
4
4
 
5
5
  Documentation lives at [usewagen.dev](https://usewagen.dev).
6
6
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as parseAsStringLiteral, S as parseAsStringEnum, T as unwrapDefault, _ as parseAsIndex, a as InferParserValue, b as parseAsNumberLiteral, c as DefaultValue, d as ParserWithDefault, f as defineParser, g as parseAsFloat, h as parseAsDate, i as InferInputWritable, l as Parser, m as parseAsBoolean, n as CustomParsers, o as KnownParsers, p as parseAsArrayOf, r as InferInputValue, s as ParserInput, t as BuiltinParsers, u as ParserOptions, v as parseAsInteger, w as tryParse, x as parseAsString, y as parseAsJson } from "./types-CNt2lcP1.mjs";
2
- import { i as RouteStateSource, t as HistoryMode } from "./types-CuwASQTi.mjs";
1
+ import { C as parseAsString, D as unwrapDefault, E as tryParse, M as StateMode, O as Missing, S as parseAsNumberLiteral, T as parseAsStringLiteral, _ as parseAsDate, a as InferParserValue, b as parseAsInteger, c as UnwrapOption, d as Parser, f as ParserOptions, g as parseAsBoolean, h as parseAsArrayOf, i as InferInputWritable, j as Register, l as WithParser, m as defineParser, n as CustomParsers, o as KnownParsers, p as ParserWithDefault, r as InferInputValue, s as ParserInput, t as BuiltinParsers, u as DefaultValue, v as parseAsFloat, w as parseAsStringEnum, x as parseAsJson, y as parseAsIndex } from "./types-DFspmU7A.mjs";
2
+ import { i as RouteStateSource, t as HistoryMode } from "./types-Bv0pKeGI.mjs";
3
3
  import { a as StorageInstance, t as ErrorHandler } from "./create-storage-DESmB4bY.mjs";
4
4
  import { App } from "vue";
5
5
  //#region src/wagen.d.ts
@@ -10,34 +10,43 @@ interface WagenStorageOptions {
10
10
  local?: StorageInstance;
11
11
  session?: StorageInstance;
12
12
  default?: 'local' | 'session';
13
+ mode?: StateMode;
13
14
  }
14
15
  interface WagenRouterOptions {
15
16
  history?: HistoryMode;
16
17
  source?: RouteStateSource;
17
18
  clearOnDefault?: boolean;
19
+ mode?: StateMode;
18
20
  }
19
21
  type WagenParsers = Record<string, Parser<any>>;
20
- interface WagenConfig {
22
+ type MissingConfig = 'missing' extends keyof Register ? {
23
+ missing: Missing;
24
+ } : {
25
+ missing?: Missing;
26
+ };
27
+ type WagenConfig = {
21
28
  parsers?: WagenParsers;
22
29
  storage?: WagenStorageOptions;
23
30
  router?: WagenRouterOptions;
24
- }
31
+ } & MissingConfig;
25
32
  interface WagenStorage {
26
33
  readonly local: StorageInstance;
27
34
  readonly session: StorageInstance;
28
35
  readonly default: StorageInstance;
36
+ readonly mode: StateMode;
29
37
  }
30
38
  type ResolvedWagenRouterOptions = Required<WagenRouterOptions>;
31
39
  interface Wagen {
32
40
  readonly parsers: WagenParsers;
41
+ readonly missing: Missing;
33
42
  readonly storage: WagenStorage;
34
43
  readonly router: ResolvedWagenRouterOptions;
35
44
  install: (app: App) => void;
36
45
  destroy: () => void;
37
46
  }
38
- declare function defineWagenConfig(config: WagenConfig): WagenConfig;
39
- declare function createWagen(config?: WagenConfig): Wagen;
40
- declare function getActiveWagen(): Wagen;
41
- declare function useWagen(): Wagen;
47
+ export declare function defineWagenConfig(config: WagenConfig): WagenConfig;
48
+ export declare function createWagen(config?: WagenConfig): Wagen;
49
+ export declare function getActiveWagen(): Wagen;
50
+ export declare function useWagen(): Wagen;
42
51
  //#endregion
43
- export { type BuiltinParsers, type CustomParsers, DefaultValue, type InferInputValue, type InferInputWritable, type InferParserValue, type KnownParsers, Parser, type ParserInput, ParserOptions, ParserWithDefault, type ResolvedWagenRouterOptions, type Wagen, type WagenConfig, type WagenParsers, type WagenRouterOptions, type WagenStorage, type WagenStorageOptions, createWagen, defineParser, defineWagenConfig, getActiveWagen, parseAsArrayOf, parseAsBoolean, parseAsDate, parseAsFloat, parseAsIndex, parseAsInteger, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, tryParse, unwrapDefault, useWagen };
52
+ export { type BuiltinParsers, type CustomParsers, DefaultValue, type InferInputValue, type InferInputWritable, type InferParserValue, type KnownParsers, type Missing, Parser, type ParserInput, ParserOptions, ParserWithDefault, type Register, type ResolvedWagenRouterOptions, type StateMode, type UnwrapOption, 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 CHANGED
@@ -1,4 +1,4 @@
1
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
2
  import "./parser-B6hfbAwA.mjs";
3
- import { i as useWagen, n as defineWagenConfig, r as getActiveWagen, t as createWagen } from "./wagen-C9_CPUdF.mjs";
3
+ import { i as useWagen, n as defineWagenConfig, r as getActiveWagen, t as createWagen } from "./wagen-FM0F79xP.mjs";
4
4
  export { createWagen, defineParser, defineWagenConfig, getActiveWagen, parseAsArrayOf, parseAsBoolean, parseAsDate, parseAsFloat, parseAsIndex, parseAsInteger, parseAsJson, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, tryParse, unwrapDefault, useWagen };
@@ -1,40 +1,44 @@
1
- import { i as InferInputWritable, r as InferInputValue, s as ParserInput } from "../types-CNt2lcP1.mjs";
2
- import { i as RouteStateSource, n as ResolvedRouteStateOptions, r as RouteStateOptions, t as HistoryMode } from "../types-CuwASQTi.mjs";
3
- import { n as ReactiveOptions, t as ReactiveFields } from "../options-BKybciek.mjs";
1
+ import { A as ReactiveOptions, M as StateMode, O as Missing, c as UnwrapOption, i as InferInputWritable, k as ReactiveFields, l as WithParser, r as InferInputValue, s as ParserInput } from "../types-DFspmU7A.mjs";
2
+ import { i as RouteStateSource, n as ResolvedRouteStateOptions, r as RouteStateOptions, t as HistoryMode } from "../types-Bv0pKeGI.mjs";
4
3
  import { Ref } from "vue";
5
4
  //#region src/router/use-route-hash.d.ts
6
5
  interface RouteHashOptions {
7
6
  parser?: ParserInput;
7
+ missing?: unknown;
8
8
  history?: HistoryMode;
9
9
  clearOnDefault?: boolean;
10
+ mode?: StateMode;
10
11
  }
11
- type UseRouteHashOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<Omit<RouteHashOptions, 'parser'> & {
12
- parser?: P;
13
- }, 'parser'>;
14
- declare function useRouteHash<P extends ParserInput | undefined = undefined>(options?: UseRouteHashOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
15
- //#endregion
16
- //#region src/router/use-route-state.d.ts
17
- type UseRouteStateOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<Omit<RouteStateOptions, 'parser'> & {
18
- parser?: P;
19
- }, 'parser'>;
20
- declare function useRouteState<P extends ParserInput | undefined = undefined>(options: UseRouteStateOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
12
+ type UseRouteHashOptions<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = ReactiveOptions<WithParser<RouteHashOptions, P, M>, 'missing'>;
13
+ type RouteHashConfig = ReactiveFields<RouteHashOptions, 'missing'>;
14
+ export declare function useRouteHash<P extends ParserInput | undefined = undefined, const M = Missing>(options?: UseRouteHashOptions<P, M>): Ref<InferInputValue<P, UnwrapOption<M>>, InferInputWritable<P, UnwrapOption<M>>>;
21
15
  //#endregion
22
16
  //#region src/router/use-route-states.d.ts
23
- type RouteStateConfig<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveFields<Omit<RouteStateOptions, 'parser'> & {
24
- parser?: P;
25
- }, 'key' | 'parser'>;
17
+ type RouteStateConfig<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = ReactiveFields<WithParser<RouteStateOptions, P, M>, 'key' | 'missing'>;
26
18
  interface BatchOptions {
27
19
  history?: HistoryMode;
28
20
  }
29
- type Refs<TOptions extends readonly RouteStateConfig[]> = { [TOption in TOptions[number] as TOption['key']]: Ref<InferInputValue<TOption['parser']>, InferInputWritable<TOption['parser']>>; };
30
- type StatePatch<TOptions extends readonly RouteStateConfig[]> = Partial<{ [TOption in TOptions[number] as TOption['key']]: InferInputWritable<TOption['parser']>; }>;
31
- type StateSnapshot<TOptions extends readonly RouteStateConfig[]> = { [TOption in TOptions[number] as TOption['key']]: InferInputValue<TOption['parser']>; };
21
+ type ConfigMissing<TConfig> = 'missing' extends keyof TConfig ? UnwrapOption<TConfig['missing']> : Missing;
22
+ type Refs<TOptions extends readonly RouteStateConfig[]> = { [TOption in TOptions[number] as TOption['key']]: Ref<InferInputValue<TOption['parser'], ConfigMissing<TOption>>, InferInputWritable<TOption['parser'], ConfigMissing<TOption>>>; };
23
+ type StatePatch<TOptions extends readonly RouteStateConfig[]> = Partial<{ [TOption in TOptions[number] as TOption['key']]: InferInputWritable<TOption['parser'], ConfigMissing<TOption>>; }>;
24
+ type StateSnapshot<TOptions extends readonly RouteStateConfig[]> = { [TOption in TOptions[number] as TOption['key']]: InferInputValue<TOption['parser'], ConfigMissing<TOption>>; };
32
25
  interface UseRouteStatesApi<TOptions extends readonly RouteStateConfig[]> {
33
26
  set: (patch: StatePatch<TOptions>, options?: BatchOptions) => void;
34
27
  reset: (options?: BatchOptions) => void;
35
28
  toObject: () => StateSnapshot<TOptions>;
36
29
  }
37
30
  type UseRouteStatesReturn<TOptions extends readonly RouteStateConfig[]> = Refs<TOptions> & UseRouteStatesApi<TOptions>;
38
- declare function useRouteStates<const TOptions extends readonly RouteStateConfig[]>(configs: TOptions): UseRouteStatesReturn<TOptions>;
31
+ export declare function useRouteStates<const TOptions extends readonly RouteStateConfig[]>(configs: TOptions): UseRouteStatesReturn<TOptions>;
32
+ //#endregion
33
+ //#region src/router/options.d.ts
34
+ type Exact<T, TShape> = T & Record<Exclude<keyof T, keyof TShape>, never>;
35
+ export declare function routeStateOptions<const T extends RouteStateConfig>(options: Exact<T, RouteStateConfig>): T;
36
+ export declare function routeStateOptions<const T extends RouteStateConfig>(options: () => Exact<T, RouteStateConfig>): () => T;
37
+ export declare function routeHashOptions<const T extends RouteHashConfig>(options: Exact<T, RouteHashConfig>): T;
38
+ export declare function routeHashOptions<const T extends RouteHashConfig>(options: () => Exact<T, RouteHashConfig>): () => T;
39
+ //#endregion
40
+ //#region src/router/use-route-state.d.ts
41
+ type UseRouteStateOptions<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = ReactiveOptions<WithParser<RouteStateOptions, P, M>, 'missing'>;
42
+ export declare function useRouteState<P extends ParserInput | undefined = undefined, const M = Missing>(options: UseRouteStateOptions<P, M>): Ref<InferInputValue<P, UnwrapOption<M>>, InferInputWritable<P, UnwrapOption<M>>>;
39
43
  //#endregion
40
- export { type BatchOptions, type HistoryMode, type ResolvedRouteStateOptions, type RouteHashOptions, type RouteStateConfig, type RouteStateOptions, type RouteStateSource, type UseRouteHashOptions, type UseRouteStateOptions, type UseRouteStatesApi, type UseRouteStatesReturn, useRouteHash, useRouteState, useRouteStates };
44
+ export type { BatchOptions, HistoryMode, ResolvedRouteStateOptions, RouteHashConfig, RouteHashOptions, RouteStateConfig, RouteStateOptions, RouteStateSource, StateMode, UseRouteHashOptions, UseRouteStateOptions, UseRouteStatesApi, UseRouteStatesReturn };
@@ -1,105 +1,245 @@
1
1
  import { _ as warnDev, h as ErrorCodes, m as unwrapDefault } from "../parsers-CA8aZuF-.mjs";
2
2
  import "../parser-B6hfbAwA.mjs";
3
- import { r as getActiveWagen } from "../wagen-C9_CPUdF.mjs";
4
- import { i as toValueDeep, n as serializeValue, r as resolveParser, t as parseValue } from "../utils-B-885gDp.mjs";
3
+ import { l as resolveMissing, r as getActiveWagen, u as toValueDeep } from "../wagen-FM0F79xP.mjs";
4
+ import { i as resolveParser, n as parseValue, r as serializeValue, t as normalizeRaw } from "../utils-B_xKZZeR.mjs";
5
5
  import { computed, customRef, getCurrentScope, nextTick, toValue } from "vue";
6
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 queues = /* @__PURE__ */ new WeakMap();
17
+ function getRouteQueue(router) {
18
+ let queue = queues.get(router);
19
+ if (!queue) {
20
+ queue = createRouteQueue(router);
21
+ queues.set(router, queue);
22
+ }
23
+ return queue;
24
+ }
25
+ function resolveBatchHistory(candidates, override) {
26
+ if (override) return override;
27
+ return candidates.includes("push") ? "push" : "replace";
28
+ }
29
+ function applyChanges(base, changes, hash) {
30
+ const params = { ...base.params };
31
+ const query = { ...base.query };
32
+ let changed = false;
33
+ for (const change of changes) {
34
+ const target = change.source === "params" ? params : query;
35
+ const current = target[change.urlKey];
36
+ if (Array.isArray(current)) changed = true;
37
+ else if (change.serialized === null) changed ||= current !== void 0;
38
+ else changed ||= current !== change.serialized;
39
+ target[change.urlKey] = change.serialized === null ? void 0 : change.serialized;
40
+ }
41
+ const nextHash = hash === void 0 ? base.hash : hash ?? "";
42
+ changed ||= nextHash !== base.hash;
43
+ return {
44
+ target: {
45
+ params,
46
+ query,
47
+ hash: nextHash
48
+ },
49
+ changed
50
+ };
51
+ }
52
+ function createRouteQueue(router) {
53
+ let batch = null;
54
+ let pending = null;
55
+ function open() {
56
+ const created = {
57
+ changes: [],
58
+ histories: [],
59
+ hash: void 0,
60
+ settled: nextTick().then(() => flush(created))
61
+ };
62
+ return created;
63
+ }
64
+ function flush(current) {
65
+ if (batch === current) batch = null;
66
+ const route = router.currentRoute.value;
67
+ const ahead = pending && pending.from === route ? pending : null;
68
+ const { target, changed } = applyChanges(ahead ?? route, current.changes, current.hash);
69
+ if (!changed) return ahead ? ahead.settled : Promise.resolve();
70
+ const history = resolveBatchHistory(ahead ? [ahead.history, ...current.histories] : current.histories);
71
+ const navigation = {
72
+ ...target,
73
+ from: route,
74
+ history,
75
+ successor: null,
76
+ settled: router[history]({
77
+ params: target.params,
78
+ query: target.query,
79
+ hash: target.hash === "" ? void 0 : target.hash
80
+ }).then(settle, settle)
81
+ };
82
+ if (ahead) ahead.successor = navigation;
83
+ pending = navigation;
84
+ return navigation.settled;
85
+ function settle() {
86
+ if (pending === navigation) pending = null;
87
+ return navigation.successor?.settled;
88
+ }
89
+ }
90
+ function enqueue(write) {
91
+ batch ??= open();
92
+ if (write.changes) batch.changes.push(...write.changes);
93
+ if (write.hash !== void 0) batch.hash = write.hash;
94
+ batch.histories.push(write.history);
95
+ return batch.settled;
96
+ }
97
+ return { enqueue };
98
+ }
99
+ //#endregion
7
100
  //#region src/router/use-route-hash.ts
8
- function toResolvedHashOptions(options, defaults) {
101
+ function toResolvedHashOptions(options, defaults, missing) {
9
102
  return {
10
103
  parser: resolveParser(options.parser),
104
+ missing: resolveMissing(options, missing),
11
105
  history: options.history ?? defaults.history,
12
- clearOnDefault: options.clearOnDefault ?? defaults.clearOnDefault
106
+ clearOnDefault: options.clearOnDefault ?? defaults.clearOnDefault,
107
+ mode: options.mode ?? defaults.mode
13
108
  };
14
109
  }
15
110
  function useRouteHash(options = {}) {
16
111
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteHash");
17
112
  const route = useRoute();
18
- const router = useRouter();
19
- const defaults = getActiveWagen().router;
20
- const resolvedOptions = computed(() => toResolvedHashOptions(toValueDeep(options), defaults));
21
- return customRef((track) => ({
22
- get: () => {
23
- track();
24
- const raw = route.hash === "" ? void 0 : route.hash;
25
- return parseValue(resolvedOptions.value.parser, raw);
26
- },
27
- set: (next) => {
28
- const { parser, history, clearOnDefault } = resolvedOptions.value;
29
- const serialized = serializeValue(parser, clearOnDefault, next);
30
- nextTick(() => {
31
- if ((route.hash === "" ? null : route.hash) === serialized) return;
32
- router[history]({
33
- query: route.query,
34
- params: route.params,
35
- hash: serialized === null ? void 0 : serialized
113
+ const queue = getRouteQueue(useRouter());
114
+ const { router: defaults, missing } = getActiveWagen();
115
+ const resolvedOptions = computed(() => toResolvedHashOptions(toValueDeep(options), defaults, missing));
116
+ let cache = null;
117
+ let generation = 0;
118
+ let trigger;
119
+ function currentHash() {
120
+ return route.hash === "" ? null : route.hash;
121
+ }
122
+ return customRef((track, triggerRef) => {
123
+ trigger = triggerRef;
124
+ return {
125
+ get: () => {
126
+ track();
127
+ const resolved = resolvedOptions.value;
128
+ const current = currentHash();
129
+ if (resolved.mode === "source") {
130
+ cache = null;
131
+ return parseValue(resolved, current);
132
+ }
133
+ if (cache && cache.seen === current) return parseValue(resolved, cache.raw);
134
+ cache = {
135
+ seen: current,
136
+ raw: current
137
+ };
138
+ return parseValue(resolved, current);
139
+ },
140
+ set: (next) => {
141
+ const resolved = resolvedOptions.value;
142
+ const { history, mode } = resolved;
143
+ const serialized = serializeValue(resolved, next);
144
+ const optimistic = mode === "optimistic";
145
+ const own = ++generation;
146
+ if (optimistic) {
147
+ cache = {
148
+ seen: currentHash(),
149
+ raw: serialized
150
+ };
151
+ trigger();
152
+ }
153
+ queue.enqueue({
154
+ history,
155
+ hash: serialized
156
+ }).then(() => {
157
+ if (!optimistic || generation !== own) return;
158
+ cache = null;
159
+ trigger();
36
160
  });
37
- });
38
- }
39
- }));
161
+ }
162
+ };
163
+ });
40
164
  }
41
165
  //#endregion
42
166
  //#region src/router/use-base-route-state.ts
43
167
  function useBaseRouteState() {
44
168
  const route = useRoute();
45
- const router = useRouter();
46
- function hasChanged(change) {
47
- const current = change.source === "params" ? route.params[change.urlKey] : route.query[change.urlKey];
48
- if (Array.isArray(current)) return true;
49
- if (change.serialized === null) return current !== void 0;
50
- return current !== change.serialized;
51
- }
52
- function navigate(changes, history) {
53
- const changed = changes.filter(hasChanged);
54
- if (changed.length === 0) return;
55
- const params = { ...route.params };
56
- const query = { ...route.query };
57
- for (const change of changed) {
58
- const target = change.source === "params" ? params : query;
59
- target[change.urlKey] = change.serialized === null ? void 0 : change.serialized;
169
+ const queue = getRouteQueue(useRouter());
170
+ function createRouteStateRef(options) {
171
+ let cache = null;
172
+ let generation = 0;
173
+ let trigger;
174
+ function rawValue(urlKey, source) {
175
+ return source === "params" ? route.params[urlKey] : route.query[urlKey];
60
176
  }
61
- router[history]({
62
- params,
63
- query,
64
- hash: route.hash
177
+ const state = customRef((track, triggerRef) => {
178
+ trigger = triggerRef;
179
+ return {
180
+ get: () => {
181
+ track();
182
+ const resolved = toValue(options);
183
+ const current = normalizeRaw(rawValue(resolved.urlKey, resolved.source));
184
+ if (resolved.mode === "source") {
185
+ cache = null;
186
+ return parseValue(resolved, current);
187
+ }
188
+ if (cache && cache.seen === current) return parseValue(resolved, cache.raw);
189
+ cache = {
190
+ seen: current,
191
+ raw: current
192
+ };
193
+ return parseValue(resolved, current);
194
+ },
195
+ set: (next) => {
196
+ write(serializeValue(toValue(options), next));
197
+ }
198
+ };
65
199
  });
66
- }
67
- function createRouteStateRef(options) {
68
- return customRef((track) => ({
69
- get: () => {
70
- track();
71
- const { urlKey, source, parser } = toValue(options);
72
- const raw = source === "params" ? route.params[urlKey] : route.query[urlKey];
73
- return parseValue(parser, raw);
74
- },
75
- set: (next) => {
76
- const { urlKey, source, parser, history, clearOnDefault } = toValue(options);
77
- const serialized = serializeValue(parser, clearOnDefault, next);
78
- nextTick(() => {
79
- navigate([{
80
- urlKey,
81
- source,
82
- serialized
83
- }], history);
84
- });
200
+ async function write(serialized, history) {
201
+ const resolved = toValue(options);
202
+ const optimistic = resolved.mode === "optimistic";
203
+ const current = ++generation;
204
+ if (optimistic) {
205
+ cache = {
206
+ seen: normalizeRaw(rawValue(resolved.urlKey, resolved.source)),
207
+ raw: serialized
208
+ };
209
+ trigger();
85
210
  }
86
- }));
211
+ return queue.enqueue({
212
+ history: history ?? resolved.history,
213
+ changes: [{
214
+ urlKey: resolved.urlKey,
215
+ source: resolved.source,
216
+ serialized
217
+ }]
218
+ }).then(() => {
219
+ if (!optimistic || generation !== current) return;
220
+ cache = null;
221
+ trigger();
222
+ });
223
+ }
224
+ return {
225
+ state,
226
+ write
227
+ };
87
228
  }
88
- return {
89
- createRouteStateRef,
90
- navigate
91
- };
229
+ return { createRouteStateRef };
92
230
  }
93
231
  //#endregion
94
232
  //#region src/router/utils.ts
95
- function toResolvedOptions(input, defaults) {
233
+ function toResolvedOptions(input, defaults, missing) {
96
234
  return {
97
235
  key: input.key,
98
236
  parser: resolveParser(input.parser),
237
+ missing: resolveMissing(input, missing),
99
238
  urlKey: input.urlKey ?? input.key,
100
239
  source: input.source ?? defaults.source,
101
240
  history: input.history ?? defaults.history,
102
- clearOnDefault: input.clearOnDefault ?? defaults.clearOnDefault
241
+ clearOnDefault: input.clearOnDefault ?? defaults.clearOnDefault,
242
+ mode: input.mode ?? defaults.mode
103
243
  };
104
244
  }
105
245
  //#endregion
@@ -107,59 +247,52 @@ function toResolvedOptions(input, defaults) {
107
247
  function useRouteState(options) {
108
248
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteState");
109
249
  const { createRouteStateRef } = useBaseRouteState();
110
- const defaults = getActiveWagen().router;
111
- return createRouteStateRef(computed(() => toResolvedOptions(toValueDeep(options), defaults)));
250
+ const { router: defaults, missing } = getActiveWagen();
251
+ return createRouteStateRef(computed(() => toResolvedOptions(toValueDeep(options), defaults, missing))).state;
112
252
  }
113
253
  //#endregion
114
254
  //#region src/router/use-route-states.ts
115
- function resolveBatchHistory(candidates, override) {
116
- if (override) return override;
117
- return candidates.includes("push") ? "push" : "replace";
118
- }
119
255
  function useRouteStates(configs) {
120
256
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteStates");
121
- const { createRouteStateRef, navigate } = useBaseRouteState();
122
- const defaults = getActiveWagen().router;
123
- const resolvedList = configs.map((config) => computed(() => toResolvedOptions(toValueDeep(config), defaults)));
124
- const refs = Object.fromEntries(resolvedList.map((resolved) => [resolved.value.key, createRouteStateRef(resolved)]));
257
+ const { createRouteStateRef } = useBaseRouteState();
258
+ const { router: defaults, missing } = getActiveWagen();
259
+ const resolvedList = configs.map((config) => computed(() => toResolvedOptions(toValueDeep(config), defaults, missing)));
260
+ const handles = resolvedList.map((resolved) => createRouteStateRef(resolved));
261
+ const refs = Object.fromEntries(resolvedList.map((resolved, index) => [resolved.value.key, handles[index].state]));
262
+ function commit(writes, candidates, override) {
263
+ if (writes.length === 0) return;
264
+ const history = resolveBatchHistory(candidates, override);
265
+ for (const { handle, serialized } of writes) handle.write(serialized, history);
266
+ }
125
267
  const api = {
126
268
  set(patch, options) {
127
- const changes = [];
128
- const historyCandidates = [];
129
- for (const resolved of resolvedList) {
130
- const { key, urlKey, source, parser, clearOnDefault, history } = resolved.value;
131
- if (!(key in patch)) continue;
269
+ const writes = [];
270
+ const candidates = [];
271
+ resolvedList.forEach((resolved, index) => {
272
+ const { key, history } = resolved.value;
273
+ if (!(key in patch)) return;
132
274
  const next = patch[key];
133
- changes.push({
134
- urlKey,
135
- source,
136
- serialized: serializeValue(parser, clearOnDefault, next)
275
+ writes.push({
276
+ handle: handles[index],
277
+ serialized: serializeValue(resolved.value, next)
137
278
  });
138
- historyCandidates.push(history);
139
- }
140
- if (changes.length === 0) return;
141
- const history = resolveBatchHistory(historyCandidates, options?.history);
142
- nextTick(() => {
143
- navigate(changes, history);
279
+ candidates.push(history);
144
280
  });
281
+ commit(writes, candidates, options?.history);
145
282
  },
146
283
  reset(options) {
147
- const changes = [];
148
- const historyCandidates = [];
149
- for (const resolved of resolvedList) {
150
- const { urlKey, source, parser, clearOnDefault, history } = resolved.value;
284
+ const writes = [];
285
+ const candidates = [];
286
+ resolvedList.forEach((resolved, index) => {
287
+ const { parser, history } = resolved.value;
151
288
  const next = parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
152
- changes.push({
153
- urlKey,
154
- source,
155
- serialized: serializeValue(parser, clearOnDefault, next)
289
+ writes.push({
290
+ handle: handles[index],
291
+ serialized: serializeValue(resolved.value, next)
156
292
  });
157
- historyCandidates.push(history);
158
- }
159
- const history = resolveBatchHistory(historyCandidates, options?.history);
160
- nextTick(() => {
161
- navigate(changes, history);
293
+ candidates.push(history);
162
294
  });
295
+ commit(writes, candidates, options?.history);
163
296
  },
164
297
  toObject() {
165
298
  const snapshot = {};
@@ -176,4 +309,4 @@ function useRouteStates(configs) {
176
309
  };
177
310
  }
178
311
  //#endregion
179
- export { useRouteHash, useRouteState, useRouteStates };
312
+ export { routeHashOptions, routeStateOptions, useRouteHash, useRouteState, useRouteStates };
@@ -1,6 +1,5 @@
1
- import { i as InferInputWritable, r as InferInputValue, s as ParserInput } from "../types-CNt2lcP1.mjs";
1
+ import { A as ReactiveOptions, M as StateMode, O as Missing, c as UnwrapOption, i as InferInputWritable, l as WithParser, r as InferInputValue, s as ParserInput } from "../types-DFspmU7A.mjs";
2
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 { n as ReactiveOptions } from "../options-BKybciek.mjs";
4
3
  import { Ref } from "vue";
5
4
  //#region src/storage/define-storage-state.d.ts
6
5
  type StorageSource = StorageInstance | 'local' | 'session';
@@ -8,9 +7,11 @@ interface StorageStateOptions {
8
7
  key: string;
9
8
  storage?: StorageSource;
10
9
  parser?: ParserInput;
10
+ missing?: unknown;
11
11
  clearOnDefault?: boolean;
12
+ mode?: StateMode;
12
13
  }
13
- interface StorageState<T = string | null, W = T | null | undefined> {
14
+ interface StorageState<T = string | Missing, W = T | Missing | undefined> {
14
15
  readonly key: string;
15
16
  readonly storage: StorageInstance;
16
17
  get: () => T;
@@ -18,26 +19,23 @@ interface StorageState<T = string | null, W = T | null | undefined> {
18
19
  remove: () => void;
19
20
  subscribe: (listener: () => void) => Unsubscribe;
20
21
  }
21
- declare function defineStorageState<P extends ParserInput | undefined = undefined>(options: Omit<StorageStateOptions, 'parser'> & {
22
- parser?: P;
23
- }): StorageState<InferInputValue<P>, InferInputWritable<P>>;
22
+ export declare function defineStorageState<P extends ParserInput | undefined = undefined, const M = Missing>(options: WithParser<StorageStateOptions, P, M>): StorageState<InferInputValue<P, M>, InferInputWritable<P, M>>;
24
23
  //#endregion
25
24
  //#region src/storage/presets.d.ts
26
- declare function createMemoryStorage(config?: StorageConfig): StorageInstance;
27
- declare function createLocalStorage(config?: StorageConfig): StorageInstance;
28
- declare function createSessionStorage(config?: StorageConfig): StorageInstance;
25
+ export declare function createMemoryStorage(config?: StorageConfig): StorageInstance;
26
+ export declare function createLocalStorage(config?: StorageConfig): StorageInstance;
27
+ export declare function createSessionStorage(config?: StorageConfig): StorageInstance;
29
28
  //#endregion
30
29
  //#region src/storage/use-storage.d.ts
31
- type UseStorageOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<Omit<StorageStateOptions, 'parser'> & {
32
- parser?: P;
33
- }, 'parser'>;
34
- type UseLocalStorageOptions<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveOptions<Omit<StorageStateOptions, 'parser' | 'storage'> & {
35
- parser?: P;
36
- }, 'parser'>;
37
- type UseSessionStorageOptions<P extends ParserInput | undefined = ParserInput | undefined> = UseLocalStorageOptions<P>;
38
- declare function useStorage<T, W>(state: StorageState<T, W>): Ref<T, W>;
39
- declare function useStorage<P extends ParserInput | undefined = undefined>(options: UseStorageOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
40
- declare function useLocalStorage<P extends ParserInput | undefined = undefined>(options: UseLocalStorageOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
41
- declare function useSessionStorage<P extends ParserInput | undefined = undefined>(options: UseSessionStorageOptions<P>): Ref<InferInputValue<P>, InferInputWritable<P>>;
30
+ type UseStorageOptions<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = ReactiveOptions<WithParser<StorageStateOptions, P, M>, 'missing'>;
31
+ type UseLocalStorageOptions<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = ReactiveOptions<WithParser<Omit<StorageStateOptions, 'storage'>, P, M>, 'missing'>;
32
+ type UseSessionStorageOptions<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = UseLocalStorageOptions<P, M>;
33
+ type NotStorageState = {
34
+ get?: never;
35
+ };
36
+ export declare function useStorage<P extends ParserInput | undefined = undefined, const M = Missing>(options: UseStorageOptions<P, M> & NotStorageState): Ref<InferInputValue<P, UnwrapOption<M>>, InferInputWritable<P, UnwrapOption<M>>>;
37
+ export declare function useStorage<T, W>(state: StorageState<T, W>): Ref<T, W>;
38
+ export declare function useLocalStorage<P extends ParserInput | undefined = undefined, const M = Missing>(options: UseLocalStorageOptions<P, M>): Ref<InferInputValue<P, UnwrapOption<M>>, InferInputWritable<P, UnwrapOption<M>>>;
39
+ export declare function useSessionStorage<P extends ParserInput | undefined = undefined, const M = Missing>(options: UseSessionStorageOptions<P, M>): Ref<InferInputValue<P, UnwrapOption<M>>, InferInputWritable<P, UnwrapOption<M>>>;
42
40
  //#endregion
43
- export { type ErrorHandler, type KeyListener, type StorageAdapter, type StorageConfig, type StorageInstance, type StorageListener, type StorageOptions, type StorageSource, type StorageState, type StorageStateOptions, type Unsubscribe, type UseLocalStorageOptions, type UseSessionStorageOptions, type UseStorageOptions, createLocalStorage, createMemoryStorage, createSessionStorage, createStorage, defineStorageState, useLocalStorage, useSessionStorage, useStorage };
41
+ 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 };
@@ -1,31 +1,68 @@
1
1
  import { _ as warnDev, h as ErrorCodes } from "../parsers-CA8aZuF-.mjs";
2
- import { a as createLocalStorage, c as createStorage, o as createMemoryStorage, r as getActiveWagen, s as createSessionStorage } from "../wagen-C9_CPUdF.mjs";
3
- import { i as toValueDeep, n as serializeValue, r as resolveParser, t as parseValue } from "../utils-B-885gDp.mjs";
2
+ import { a as createLocalStorage, c as createStorage, l as resolveMissing, o as createMemoryStorage, r as getActiveWagen, s as createSessionStorage, u as toValueDeep } from "../wagen-FM0F79xP.mjs";
3
+ import { i as resolveParser, n as parseValue, r as serializeValue } from "../utils-B_xKZZeR.mjs";
4
4
  import { computed, customRef, getCurrentScope, onWatcherCleanup, toValue, watch } from "vue";
5
5
  //#region src/storage/define-storage-state.ts
6
6
  function defineStorageState(options) {
7
7
  const { key } = options;
8
8
  const clearOnDefault = options.clearOnDefault ?? true;
9
9
  const parser = resolveParser(options.parser);
10
- function storage() {
10
+ let cache = null;
11
+ function resolved() {
12
+ const wagen = getActiveWagen();
11
13
  const source = options.storage;
12
- if (source && typeof source !== "string") return source;
13
- const { storage } = getActiveWagen();
14
- return source ? storage[source] : storage.default;
14
+ return {
15
+ instance: source === void 0 ? wagen.storage.default : typeof source === "string" ? wagen.storage[source] : source,
16
+ optimistic: (options.mode ?? wagen.storage.mode) === "optimistic",
17
+ missing: resolveMissing(options, wagen.missing)
18
+ };
19
+ }
20
+ function write(target, serialized) {
21
+ const { instance, optimistic } = target;
22
+ if (optimistic) cache = {
23
+ seen: instance.getItem(key),
24
+ raw: serialized
25
+ };
26
+ if (serialized === null) instance.removeItem(key);
27
+ else instance.setItem(key, serialized);
28
+ if (optimistic) cache = {
29
+ seen: instance.getItem(key),
30
+ raw: serialized
31
+ };
15
32
  }
16
33
  return {
17
34
  key,
18
35
  get storage() {
19
- return storage();
36
+ return resolved().instance;
37
+ },
38
+ get: () => {
39
+ const { instance, optimistic, missing } = resolved();
40
+ const current = instance.getItem(key);
41
+ const state = {
42
+ parser,
43
+ missing
44
+ };
45
+ if (!optimistic) {
46
+ cache = null;
47
+ return parseValue(state, current);
48
+ }
49
+ if (cache && cache.seen === current) return parseValue(state, cache.raw);
50
+ cache = {
51
+ seen: current,
52
+ raw: current
53
+ };
54
+ return parseValue(state, current);
20
55
  },
21
- get: () => parseValue(parser, storage().getItem(key)),
22
56
  set: (next) => {
23
- const serialized = serializeValue(parser, clearOnDefault, next);
24
- if (serialized === null) storage().removeItem(key);
25
- else storage().setItem(key, serialized);
57
+ const target = resolved();
58
+ write(target, serializeValue({
59
+ parser,
60
+ clearOnDefault,
61
+ missing: target.missing
62
+ }, next));
26
63
  },
27
- remove: () => storage().removeItem(key),
28
- subscribe: (listener) => storage().subscribe(key, listener)
64
+ remove: () => write(resolved(), null),
65
+ subscribe: (listener) => resolved().instance.subscribe(key, listener)
29
66
  };
30
67
  }
31
68
  //#endregion
@@ -33,18 +70,20 @@ function defineStorageState(options) {
33
70
  function isStorageState(input) {
34
71
  return typeof input === "object" && input !== null && typeof input.get === "function";
35
72
  }
36
- function withResolvedStorage(options, wagen) {
73
+ function withDefaults(options, defaults, missing) {
37
74
  const source = options.storage;
38
- if (source && typeof source !== "string") return options;
75
+ const storage = source === void 0 ? defaults.default : typeof source === "string" ? defaults[source] : source;
39
76
  return {
40
77
  ...options,
41
- storage: source ? wagen.storage[source] : wagen.storage.default
78
+ storage,
79
+ mode: options.mode ?? defaults.mode,
80
+ missing: resolveMissing(options, missing)
42
81
  };
43
82
  }
44
83
  function useStorageState(input) {
45
84
  if (isStorageState(input)) return computed(() => input);
46
- const wagen = getActiveWagen();
47
- return computed(() => defineStorageState(withResolvedStorage(toValueDeep(input), wagen)));
85
+ const { storage, missing } = getActiveWagen();
86
+ return computed(() => defineStorageState(withDefaults(toValueDeep(input), storage, missing)));
48
87
  }
49
88
  function useStorage(input) {
50
89
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useStorage");
@@ -1,4 +1,4 @@
1
- import { c as DefaultValue, s as ParserInput } from "./types-CNt2lcP1.mjs";
1
+ import { M as StateMode, s as ParserInput, u as DefaultValue } from "./types-DFspmU7A.mjs";
2
2
  //#region src/parser/resolve.d.ts
3
3
  type ResolvedParser<T> = {
4
4
  parse: (raw: string) => T | null;
@@ -12,10 +12,12 @@ type HistoryMode = 'push' | 'replace';
12
12
  interface RouteStateOptions {
13
13
  key: string;
14
14
  parser?: ParserInput;
15
+ missing?: unknown;
15
16
  urlKey?: string;
16
17
  source?: RouteStateSource;
17
18
  history?: HistoryMode;
18
19
  clearOnDefault?: boolean;
20
+ mode?: StateMode;
19
21
  }
20
22
  type ResolvedRouteStateOptions = Omit<Required<RouteStateOptions>, 'parser'> & {
21
23
  parser: ResolvedParser<any>;
@@ -1,3 +1,13 @@
1
+ import { ComputedRef, MaybeRef, MaybeRefOrGetter, Ref } from "vue";
2
+ //#region src/types.d.ts
3
+ type StateMode = 'optimistic' | 'source';
4
+ interface Register {}
5
+ type Registered<K extends string, TFallback> = K extends keyof Register ? Register[K] : TFallback;
6
+ type Missing = Registered<'missing', null>;
7
+ type MaybeRefOrComputed<T> = MaybeRef<T> | ComputedRef<T>;
8
+ type ReactiveFields<T, TRaw extends keyof T = never> = { [K in keyof T]: K extends TRaw ? T[K] : K extends 'parser' ? MaybeRefOrComputed<T[K]> : MaybeRefOrGetter<T[K]>; };
9
+ type ReactiveOptions<T, TRaw extends keyof T = never> = MaybeRefOrGetter<ReactiveFields<T, TRaw>>;
10
+ //#endregion
1
11
  //#region src/parser/parsers.d.ts
2
12
  type ParserOptions<T> = {
3
13
  parse: (raw: string) => T | null;
@@ -49,12 +59,18 @@ type NamedParserRef = { [K in keyof KnownParsers]: {
49
59
  type ParserInput = (Parser<any> & {
50
60
  name?: never;
51
61
  }) | NamedParserRef;
52
- type InferInputValue<P> = [P] extends [undefined] ? string | null : P extends ParserWithDefault<infer T> ? T : P extends Parser<infer T> ? T | null : P extends {
62
+ type UnwrapOption<T> = T extends Ref<infer U> ? U : T extends (() => infer U) ? U : T;
63
+ type WithParser<T, P extends ParserInput | undefined, M = unknown> = Omit<T, 'parser' | 'missing'> & {
64
+ parser?: P;
65
+ missing?: M;
66
+ };
67
+ type InferInputValue<P, M = Missing> = ResolveInputValue<UnwrapOption<P>, M>;
68
+ type ResolveInputValue<P, M> = [P] extends [undefined] ? string | M : P extends ParserWithDefault<infer T> ? T : P extends Parser<infer T> ? T | M : P extends {
53
69
  name: infer K;
54
70
  defaultValue: any;
55
71
  } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> : string : P extends {
56
72
  name: infer K;
57
- } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> | null : string | null : string | null;
58
- type InferInputWritable<P> = InferInputValue<P> | null | undefined;
73
+ } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> | M : string | M : string | M;
74
+ type InferInputWritable<P, M = Missing> = InferInputValue<P, M> | M | null | undefined;
59
75
  //#endregion
60
- export { parseAsStringLiteral as C, parseAsStringEnum as S, unwrapDefault as T, parseAsIndex as _, InferParserValue as a, parseAsNumberLiteral as b, DefaultValue as c, ParserWithDefault as d, defineParser as f, parseAsFloat as g, parseAsDate as h, InferInputWritable as i, Parser as l, parseAsBoolean as m, CustomParsers as n, KnownParsers as o, parseAsArrayOf as p, InferInputValue as r, ParserInput as s, BuiltinParsers as t, ParserOptions as u, parseAsInteger as v, tryParse as w, parseAsString as x, parseAsJson as y };
76
+ export { ReactiveOptions as A, parseAsString as C, unwrapDefault as D, tryParse as E, StateMode as M, Missing as O, parseAsNumberLiteral as S, parseAsStringLiteral as T, parseAsDate as _, InferParserValue as a, parseAsInteger as b, UnwrapOption as c, Parser as d, ParserOptions as f, parseAsBoolean as g, parseAsArrayOf as h, InferInputWritable as i, Register as j, ReactiveFields as k, WithParser as l, defineParser as m, CustomParsers as n, KnownParsers as o, ParserWithDefault as p, InferInputValue as r, ParserInput as s, BuiltinParsers as t, DefaultValue as u, parseAsFloat as v, parseAsStringEnum as w, parseAsJson as x, parseAsIndex as y };
@@ -1,15 +1,6 @@
1
1
  import { g as warn, h as ErrorCodes, m as unwrapDefault, u as parseAsString } from "./parsers-CA8aZuF-.mjs";
2
- import { r as getActiveWagen } from "./wagen-C9_CPUdF.mjs";
2
+ import { r as getActiveWagen } from "./wagen-FM0F79xP.mjs";
3
3
  import { n as isBuiltinParserName, t as builtinParsers } from "./builtins-DiCjLgsG.mjs";
4
- import { toValue } from "vue";
5
- //#region src/options.ts
6
- function toValueDeep(input) {
7
- const source = toValue(input);
8
- const result = {};
9
- for (const key in source) result[key] = toValue(source[key]);
10
- return result;
11
- }
12
- //#endregion
13
4
  //#region src/parser/resolve.ts
14
5
  function getParser(name) {
15
6
  if (isBuiltinParserName(name)) return builtinParsers[name];
@@ -35,17 +26,11 @@ function fromName(name, override) {
35
26
  }
36
27
  return parseAsString;
37
28
  }
38
- const base = {
39
- parse: (raw) => get().parse(raw),
40
- serialize: (value) => get().serialize(value)
41
- };
42
- if (override) return {
43
- ...base,
44
- defaultValue: override.defaultValue
45
- };
46
29
  return {
47
- ...base,
30
+ parse: (raw) => get().parse(raw),
31
+ serialize: (value) => get().serialize(value),
48
32
  get defaultValue() {
33
+ if (override) return override.defaultValue;
49
34
  return get().defaultValue;
50
35
  }
51
36
  };
@@ -57,15 +42,19 @@ function resolveParser(input = { name: "parseAsString" }) {
57
42
  }
58
43
  //#endregion
59
44
  //#region src/parser/utils.ts
60
- function parseValue(parser, raw) {
61
- const value = Array.isArray(raw) ? raw.find((v) => v != null) : raw;
62
- if (value == null) return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
63
- const parsed = parser.parse(value);
45
+ function normalizeRaw(raw) {
46
+ return (Array.isArray(raw) ? raw.find((v) => v != null) : raw) ?? null;
47
+ }
48
+ function parseValue(options, raw) {
49
+ const { parser, missing } = options;
50
+ const value = normalizeRaw(raw);
51
+ const parsed = value === null ? null : parser.parse(value);
64
52
  if (parsed !== null) return parsed;
65
- return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
53
+ return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : missing;
66
54
  }
67
- function serializeValue(parser, clearOnDefault, next) {
68
- if (next == null) return null;
55
+ function serializeValue(options, next) {
56
+ const { parser, clearOnDefault, missing } = options;
57
+ if (next == null || Object.is(next, missing)) return null;
69
58
  const serialized = parser.serialize(next);
70
59
  if (clearOnDefault && parser.defaultValue !== void 0) {
71
60
  if (serialized === parser.serialize(unwrapDefault(parser.defaultValue))) return null;
@@ -73,4 +62,4 @@ function serializeValue(parser, clearOnDefault, next) {
73
62
  return serialized;
74
63
  }
75
64
  //#endregion
76
- export { toValueDeep as i, serializeValue as n, resolveParser as r, parseValue as t };
65
+ export { resolveParser as i, parseValue as n, serializeValue as r, normalizeRaw as t };
@@ -1,8 +1,8 @@
1
1
  import { Plugin } from "vite";
2
2
  //#region src/vite/index.d.ts
3
- declare const PARSERS_ID = "virtual:usewagen/parsers";
4
- declare const RESOLVED_PARSERS_ID = "\0virtual:usewagen/parsers";
5
- interface WagenPluginOptions {
3
+ export declare const PARSERS_ID = "virtual:usewagen/parsers";
4
+ export declare const RESOLVED_PARSERS_ID = "\0virtual:usewagen/parsers";
5
+ export interface WagenPluginOptions {
6
6
  /**
7
7
  * Directory or directories to scan for parser files.
8
8
  * @default 'src/parsers'
@@ -14,6 +14,6 @@ interface WagenPluginOptions {
14
14
  */
15
15
  dts?: string | false;
16
16
  }
17
- declare function usewagen(options?: WagenPluginOptions): Plugin;
17
+ export declare function usewagen(options?: WagenPluginOptions): Plugin;
18
18
  //#endregion
19
- export { PARSERS_ID, RESOLVED_PARSERS_ID, WagenPluginOptions, usewagen as default, usewagen };
19
+ export { usewagen as default };
@@ -1,5 +1,16 @@
1
1
  import { _ as warnDev, g as warn, h as ErrorCodes } from "./parsers-CA8aZuF-.mjs";
2
- import { hasInjectionContext, inject, markRaw } from "vue";
2
+ import { hasInjectionContext, inject, markRaw, toValue } from "vue";
3
+ //#region src/options.ts
4
+ function toValueDeep(input) {
5
+ const source = toValue(input);
6
+ const result = {};
7
+ for (const key in source) result[key] = toValue(source[key]);
8
+ return result;
9
+ }
10
+ function resolveMissing(options, fallback) {
11
+ return "missing" in options ? options.missing : fallback;
12
+ }
13
+ //#endregion
3
14
  //#region src/storage/create-storage.ts
4
15
  const ALL_KEYS = Symbol("all-keys");
5
16
  function createStorage$1(name, options) {
@@ -197,7 +208,8 @@ const WAGEN_KEY = Symbol.for("usewagen");
197
208
  const ROUTER_DEFAULTS = {
198
209
  history: "replace",
199
210
  source: "query",
200
- clearOnDefault: true
211
+ clearOnDefault: true,
212
+ mode: "optimistic"
201
213
  };
202
214
  let activeWagen = null;
203
215
  function createStorage(options) {
@@ -228,7 +240,8 @@ function createStorage(options) {
228
240
  },
229
241
  get default() {
230
242
  return options.default === "session" ? storage.session : storage.local;
231
- }
243
+ },
244
+ mode: options.mode ?? "optimistic"
232
245
  };
233
246
  function destroy() {
234
247
  for (const instance of created) instance.destroy();
@@ -246,6 +259,7 @@ function createWagen(config = {}) {
246
259
  const { storage, destroy } = createStorage(config.storage ?? {});
247
260
  const wagen = markRaw({
248
261
  parsers: { ...config.parsers },
262
+ missing: resolveMissing(config, null),
249
263
  storage,
250
264
  router: {
251
265
  ...ROUTER_DEFAULTS,
@@ -273,4 +287,4 @@ function useWagen() {
273
287
  return getActiveWagen();
274
288
  }
275
289
  //#endregion
276
- export { createLocalStorage as a, createStorage$1 as c, useWagen as i, defineWagenConfig as n, createMemoryStorage as o, getActiveWagen as r, createSessionStorage as s, createWagen as t };
290
+ export { createLocalStorage as a, createStorage$1 as c, useWagen as i, resolveMissing as l, defineWagenConfig as n, createMemoryStorage as o, getActiveWagen as r, createSessionStorage as s, createWagen as t, toValueDeep as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usewagen",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Reactive state for URL and storage in Vue.",
5
5
  "keywords": [
6
6
  "composables",
@@ -64,7 +64,7 @@
64
64
  "devEngines": {
65
65
  "packageManager": {
66
66
  "name": "pnpm",
67
- "version": "11.24.0",
67
+ "version": "11.25.0",
68
68
  "onFail": "download"
69
69
  }
70
70
  },
@@ -1,7 +0,0 @@
1
- import { MaybeRefOrGetter } from "vue";
2
- //#region src/options.d.ts
3
- type MaybeRefsOrGetters<T> = { [K in keyof T]: MaybeRefOrGetter<T[K]>; };
4
- type ReactiveFields<T, TStatic extends keyof T = never> = MaybeRefsOrGetters<Omit<T, TStatic>> & Pick<T, TStatic>;
5
- type ReactiveOptions<T, TStatic extends keyof T = never> = MaybeRefOrGetter<ReactiveFields<T, TStatic>>;
6
- //#endregion
7
- export { ReactiveOptions as n, ReactiveFields as t };