usewagen 0.2.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/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
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";
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
@@ -19,11 +19,16 @@ interface WagenRouterOptions {
19
19
  mode?: StateMode;
20
20
  }
21
21
  type WagenParsers = Record<string, Parser<any>>;
22
- interface WagenConfig {
22
+ type MissingConfig = 'missing' extends keyof Register ? {
23
+ missing: Missing;
24
+ } : {
25
+ missing?: Missing;
26
+ };
27
+ type WagenConfig = {
23
28
  parsers?: WagenParsers;
24
29
  storage?: WagenStorageOptions;
25
30
  router?: WagenRouterOptions;
26
- }
31
+ } & MissingConfig;
27
32
  interface WagenStorage {
28
33
  readonly local: StorageInstance;
29
34
  readonly session: StorageInstance;
@@ -33,6 +38,7 @@ interface WagenStorage {
33
38
  type ResolvedWagenRouterOptions = Required<WagenRouterOptions>;
34
39
  interface Wagen {
35
40
  readonly parsers: WagenParsers;
41
+ readonly missing: Missing;
36
42
  readonly storage: WagenStorage;
37
43
  readonly router: ResolvedWagenRouterOptions;
38
44
  install: (app: App) => void;
@@ -43,4 +49,4 @@ export declare function createWagen(config?: WagenConfig): Wagen;
43
49
  export declare function getActiveWagen(): Wagen;
44
50
  export declare function useWagen(): Wagen;
45
51
  //#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 };
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-C2p8oQgu.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,25 +1,27 @@
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";
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";
3
3
  import { Ref } from "vue";
4
4
  //#region src/router/use-route-hash.d.ts
5
5
  interface RouteHashOptions {
6
6
  parser?: ParserInput;
7
+ missing?: unknown;
7
8
  history?: HistoryMode;
8
9
  clearOnDefault?: boolean;
9
10
  mode?: StateMode;
10
11
  }
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>>;
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>>>;
14
15
  //#endregion
15
16
  //#region src/router/use-route-states.d.ts
16
- type RouteStateConfig<P extends ParserInput | undefined = ParserInput | undefined> = ReactiveFields<WithParser<RouteStateOptions, P>, 'key'>;
17
+ type RouteStateConfig<P extends ParserInput | undefined = ParserInput | undefined, M = unknown> = ReactiveFields<WithParser<RouteStateOptions, P, M>, 'key' | 'missing'>;
17
18
  interface BatchOptions {
18
19
  history?: HistoryMode;
19
20
  }
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']>; };
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>>; };
23
25
  interface UseRouteStatesApi<TOptions extends readonly RouteStateConfig[]> {
24
26
  set: (patch: StatePatch<TOptions>, options?: BatchOptions) => void;
25
27
  reset: (options?: BatchOptions) => void;
@@ -36,7 +38,7 @@ export declare function routeHashOptions<const T extends RouteHashConfig>(option
36
38
  export declare function routeHashOptions<const T extends RouteHashConfig>(options: () => Exact<T, RouteHashConfig>): () => T;
37
39
  //#endregion
38
40
  //#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
+ 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>>>;
41
43
  //#endregion
42
44
  export type { BatchOptions, HistoryMode, ResolvedRouteStateOptions, RouteHashConfig, RouteHashOptions, RouteStateConfig, RouteStateOptions, RouteStateSource, StateMode, UseRouteHashOptions, UseRouteStateOptions, UseRouteStatesApi, UseRouteStatesReturn };
@@ -1,7 +1,7 @@
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-C2p8oQgu.mjs";
4
- import { a as toValueDeep, i as resolveParser, n as parseValue, r as serializeValue, t as normalizeRaw } from "../utils-EL2EiRpH.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
7
  //#region src/router/options.ts
@@ -13,36 +13,24 @@ function routeHashOptions(options) {
13
13
  }
14
14
  //#endregion
15
15
  //#region src/router/queue.ts
16
- const batches = /* @__PURE__ */ new WeakMap();
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
+ }
17
25
  function resolveBatchHistory(candidates, override) {
18
26
  if (override) return override;
19
27
  return candidates.includes("push") ? "push" : "replace";
20
28
  }
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 };
29
+ function applyChanges(base, changes, hash) {
30
+ const params = { ...base.params };
31
+ const query = { ...base.query };
44
32
  let changed = false;
45
- for (const change of batch.changes) {
33
+ for (const change of changes) {
46
34
  const target = change.source === "params" ? params : query;
47
35
  const current = target[change.urlKey];
48
36
  if (Array.isArray(current)) changed = true;
@@ -50,20 +38,70 @@ function flush(router, batch) {
50
38
  else changed ||= current !== change.serialized;
51
39
  target[change.urlKey] = change.serialized === null ? void 0 : change.serialized;
52
40
  }
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(() => {}, () => {});
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 };
61
98
  }
62
99
  //#endregion
63
100
  //#region src/router/use-route-hash.ts
64
- function toResolvedHashOptions(options, defaults) {
101
+ function toResolvedHashOptions(options, defaults, missing) {
65
102
  return {
66
103
  parser: resolveParser(options.parser),
104
+ missing: resolveMissing(options, missing),
67
105
  history: options.history ?? defaults.history,
68
106
  clearOnDefault: options.clearOnDefault ?? defaults.clearOnDefault,
69
107
  mode: options.mode ?? defaults.mode
@@ -72,9 +110,9 @@ function toResolvedHashOptions(options, defaults) {
72
110
  function useRouteHash(options = {}) {
73
111
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteHash");
74
112
  const route = useRoute();
75
- const router = useRouter();
76
- const defaults = getActiveWagen().router;
77
- const resolvedOptions = computed(() => toResolvedHashOptions(toValueDeep(options), defaults));
113
+ const queue = getRouteQueue(useRouter());
114
+ const { router: defaults, missing } = getActiveWagen();
115
+ const resolvedOptions = computed(() => toResolvedHashOptions(toValueDeep(options), defaults, missing));
78
116
  let cache = null;
79
117
  let generation = 0;
80
118
  let trigger;
@@ -86,22 +124,23 @@ function useRouteHash(options = {}) {
86
124
  return {
87
125
  get: () => {
88
126
  track();
89
- const { parser, mode } = resolvedOptions.value;
127
+ const resolved = resolvedOptions.value;
90
128
  const current = currentHash();
91
- if (mode === "source") {
129
+ if (resolved.mode === "source") {
92
130
  cache = null;
93
- return parseValue(parser, current);
131
+ return parseValue(resolved, current);
94
132
  }
95
- if (cache && cache.seen === current) return parseValue(parser, cache.raw);
133
+ if (cache && cache.seen === current) return parseValue(resolved, cache.raw);
96
134
  cache = {
97
135
  seen: current,
98
136
  raw: current
99
137
  };
100
- return parseValue(parser, current);
138
+ return parseValue(resolved, current);
101
139
  },
102
140
  set: (next) => {
103
- const { parser, clearOnDefault, history, mode } = resolvedOptions.value;
104
- const serialized = serializeValue(parser, clearOnDefault, next);
141
+ const resolved = resolvedOptions.value;
142
+ const { history, mode } = resolved;
143
+ const serialized = serializeValue(resolved, next);
105
144
  const optimistic = mode === "optimistic";
106
145
  const own = ++generation;
107
146
  if (optimistic) {
@@ -111,7 +150,7 @@ function useRouteHash(options = {}) {
111
150
  };
112
151
  trigger();
113
152
  }
114
- enqueue(router, {
153
+ queue.enqueue({
115
154
  history,
116
155
  hash: serialized
117
156
  }).then(() => {
@@ -127,7 +166,7 @@ function useRouteHash(options = {}) {
127
166
  //#region src/router/use-base-route-state.ts
128
167
  function useBaseRouteState() {
129
168
  const route = useRoute();
130
- const router = useRouter();
169
+ const queue = getRouteQueue(useRouter());
131
170
  function createRouteStateRef(options) {
132
171
  let cache = null;
133
172
  let generation = 0;
@@ -140,26 +179,25 @@ function useBaseRouteState() {
140
179
  return {
141
180
  get: () => {
142
181
  track();
143
- const { urlKey, source, parser, mode } = toValue(options);
144
- const current = normalizeRaw(rawValue(urlKey, source));
145
- if (mode === "source") {
182
+ const resolved = toValue(options);
183
+ const current = normalizeRaw(rawValue(resolved.urlKey, resolved.source));
184
+ if (resolved.mode === "source") {
146
185
  cache = null;
147
- return parseValue(parser, current);
186
+ return parseValue(resolved, current);
148
187
  }
149
- if (cache && cache.seen === current) return parseValue(parser, cache.raw);
188
+ if (cache && cache.seen === current) return parseValue(resolved, cache.raw);
150
189
  cache = {
151
190
  seen: current,
152
191
  raw: current
153
192
  };
154
- return parseValue(parser, current);
193
+ return parseValue(resolved, current);
155
194
  },
156
195
  set: (next) => {
157
- const { parser, clearOnDefault } = toValue(options);
158
- write(serializeValue(parser, clearOnDefault, next));
196
+ write(serializeValue(toValue(options), next));
159
197
  }
160
198
  };
161
199
  });
162
- function write(serialized, history) {
200
+ async function write(serialized, history) {
163
201
  const resolved = toValue(options);
164
202
  const optimistic = resolved.mode === "optimistic";
165
203
  const current = ++generation;
@@ -170,7 +208,7 @@ function useBaseRouteState() {
170
208
  };
171
209
  trigger();
172
210
  }
173
- return enqueue(router, {
211
+ return queue.enqueue({
174
212
  history: history ?? resolved.history,
175
213
  changes: [{
176
214
  urlKey: resolved.urlKey,
@@ -192,10 +230,11 @@ function useBaseRouteState() {
192
230
  }
193
231
  //#endregion
194
232
  //#region src/router/utils.ts
195
- function toResolvedOptions(input, defaults) {
233
+ function toResolvedOptions(input, defaults, missing) {
196
234
  return {
197
235
  key: input.key,
198
236
  parser: resolveParser(input.parser),
237
+ missing: resolveMissing(input, missing),
199
238
  urlKey: input.urlKey ?? input.key,
200
239
  source: input.source ?? defaults.source,
201
240
  history: input.history ?? defaults.history,
@@ -208,16 +247,16 @@ function toResolvedOptions(input, defaults) {
208
247
  function useRouteState(options) {
209
248
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteState");
210
249
  const { createRouteStateRef } = useBaseRouteState();
211
- const defaults = getActiveWagen().router;
212
- return createRouteStateRef(computed(() => toResolvedOptions(toValueDeep(options), defaults))).state;
250
+ const { router: defaults, missing } = getActiveWagen();
251
+ return createRouteStateRef(computed(() => toResolvedOptions(toValueDeep(options), defaults, missing))).state;
213
252
  }
214
253
  //#endregion
215
254
  //#region src/router/use-route-states.ts
216
255
  function useRouteStates(configs) {
217
256
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useRouteStates");
218
257
  const { createRouteStateRef } = useBaseRouteState();
219
- const defaults = getActiveWagen().router;
220
- const resolvedList = configs.map((config) => computed(() => toResolvedOptions(toValueDeep(config), defaults)));
258
+ const { router: defaults, missing } = getActiveWagen();
259
+ const resolvedList = configs.map((config) => computed(() => toResolvedOptions(toValueDeep(config), defaults, missing)));
221
260
  const handles = resolvedList.map((resolved) => createRouteStateRef(resolved));
222
261
  const refs = Object.fromEntries(resolvedList.map((resolved, index) => [resolved.value.key, handles[index].state]));
223
262
  function commit(writes, candidates, override) {
@@ -230,12 +269,12 @@ function useRouteStates(configs) {
230
269
  const writes = [];
231
270
  const candidates = [];
232
271
  resolvedList.forEach((resolved, index) => {
233
- const { key, parser, clearOnDefault, history } = resolved.value;
272
+ const { key, history } = resolved.value;
234
273
  if (!(key in patch)) return;
235
274
  const next = patch[key];
236
275
  writes.push({
237
276
  handle: handles[index],
238
- serialized: serializeValue(parser, clearOnDefault, next)
277
+ serialized: serializeValue(resolved.value, next)
239
278
  });
240
279
  candidates.push(history);
241
280
  });
@@ -245,11 +284,11 @@ function useRouteStates(configs) {
245
284
  const writes = [];
246
285
  const candidates = [];
247
286
  resolvedList.forEach((resolved, index) => {
248
- const { parser, clearOnDefault, history } = resolved.value;
287
+ const { parser, history } = resolved.value;
249
288
  const next = parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
250
289
  writes.push({
251
290
  handle: handles[index],
252
- serialized: serializeValue(parser, clearOnDefault, next)
291
+ serialized: serializeValue(resolved.value, next)
253
292
  });
254
293
  candidates.push(history);
255
294
  });
@@ -1,4 +1,4 @@
1
- import { f as WithParser, n as ReactiveOptions, o as InferInputValue, r as StateMode, s as InferInputWritable, u as ParserInput } from "../types-JtRmT3qo.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
3
  import { Ref } from "vue";
4
4
  //#region src/storage/define-storage-state.d.ts
@@ -7,10 +7,11 @@ interface StorageStateOptions {
7
7
  key: string;
8
8
  storage?: StorageSource;
9
9
  parser?: ParserInput;
10
+ missing?: unknown;
10
11
  clearOnDefault?: boolean;
11
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,7 +19,7 @@ interface StorageState<T = string | null, W = T | null | undefined> {
18
19
  remove: () => void;
19
20
  subscribe: (listener: () => void) => Unsubscribe;
20
21
  }
21
- export declare function defineStorageState<P extends ParserInput | undefined = undefined>(options: WithParser<StorageStateOptions, P>): 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>>;
22
23
  //#endregion
23
24
  //#region src/storage/presets.d.ts
24
25
  export declare function createMemoryStorage(config?: StorageConfig): StorageInstance;
@@ -26,12 +27,15 @@ export declare function createLocalStorage(config?: StorageConfig): StorageInsta
26
27
  export declare function createSessionStorage(config?: StorageConfig): StorageInstance;
27
28
  //#endregion
28
29
  //#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>;
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>>>;
32
37
  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>>;
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>>>;
36
40
  //#endregion
37
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,6 +1,6 @@
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-C2p8oQgu.mjs";
3
- import { a as toValueDeep, i as resolveParser, n as parseValue, r as serializeValue } from "../utils-EL2EiRpH.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) {
@@ -9,20 +9,16 @@ function defineStorageState(options) {
9
9
  const parser = resolveParser(options.parser);
10
10
  let cache = null;
11
11
  function resolved() {
12
- const source = options.storage;
13
- const mode = options.mode;
14
- if (mode !== void 0 && source !== void 0 && typeof source !== "string") return {
15
- instance: source,
16
- optimistic: mode === "optimistic"
17
- };
18
12
  const wagen = getActiveWagen();
13
+ const source = options.storage;
19
14
  return {
20
15
  instance: source === void 0 ? wagen.storage.default : typeof source === "string" ? wagen.storage[source] : source,
21
- optimistic: (mode ?? wagen.storage.mode) === "optimistic"
16
+ optimistic: (options.mode ?? wagen.storage.mode) === "optimistic",
17
+ missing: resolveMissing(options, wagen.missing)
22
18
  };
23
19
  }
24
- function write(serialized) {
25
- const { instance, optimistic } = resolved();
20
+ function write(target, serialized) {
21
+ const { instance, optimistic } = target;
26
22
  if (optimistic) cache = {
27
23
  seen: instance.getItem(key),
28
24
  raw: serialized
@@ -40,21 +36,32 @@ function defineStorageState(options) {
40
36
  return resolved().instance;
41
37
  },
42
38
  get: () => {
43
- const { instance, optimistic } = resolved();
39
+ const { instance, optimistic, missing } = resolved();
44
40
  const current = instance.getItem(key);
41
+ const state = {
42
+ parser,
43
+ missing
44
+ };
45
45
  if (!optimistic) {
46
46
  cache = null;
47
- return parseValue(parser, current);
47
+ return parseValue(state, current);
48
48
  }
49
- if (cache && cache.seen === current) return parseValue(parser, cache.raw);
49
+ if (cache && cache.seen === current) return parseValue(state, cache.raw);
50
50
  cache = {
51
51
  seen: current,
52
52
  raw: current
53
53
  };
54
- return parseValue(parser, current);
54
+ return parseValue(state, current);
55
+ },
56
+ set: (next) => {
57
+ const target = resolved();
58
+ write(target, serializeValue({
59
+ parser,
60
+ clearOnDefault,
61
+ missing: target.missing
62
+ }, next));
55
63
  },
56
- set: (next) => write(serializeValue(parser, clearOnDefault, next)),
57
- remove: () => write(null),
64
+ remove: () => write(resolved(), null),
58
65
  subscribe: (listener) => resolved().instance.subscribe(key, listener)
59
66
  };
60
67
  }
@@ -63,19 +70,20 @@ function defineStorageState(options) {
63
70
  function isStorageState(input) {
64
71
  return typeof input === "object" && input !== null && typeof input.get === "function";
65
72
  }
66
- function withWagenDefaults(options, wagen) {
73
+ function withDefaults(options, defaults, missing) {
67
74
  const source = options.storage;
68
- const storage = source === void 0 ? wagen.storage.default : typeof source === "string" ? wagen.storage[source] : source;
75
+ const storage = source === void 0 ? defaults.default : typeof source === "string" ? defaults[source] : source;
69
76
  return {
70
77
  ...options,
71
78
  storage,
72
- mode: options.mode ?? wagen.storage.mode
79
+ mode: options.mode ?? defaults.mode,
80
+ missing: resolveMissing(options, missing)
73
81
  };
74
82
  }
75
83
  function useStorageState(input) {
76
84
  if (isStorageState(input)) return computed(() => input);
77
- const wagen = getActiveWagen();
78
- return computed(() => defineStorageState(withWagenDefaults(toValueDeep(input), wagen)));
85
+ const { storage, missing } = getActiveWagen();
86
+ return computed(() => defineStorageState(withDefaults(toValueDeep(input), storage, missing)));
79
87
  }
80
88
  function useStorage(input) {
81
89
  if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useStorage");
@@ -1,4 +1,4 @@
1
- import { p as DefaultValue, r as StateMode, u as ParserInput } from "./types-JtRmT3qo.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,6 +12,7 @@ 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;
@@ -1,4 +1,13 @@
1
- import { MaybeRefOrGetter, Ref } from "vue";
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
2
11
  //#region src/parser/parsers.d.ts
3
12
  type ParserOptions<T> = {
4
13
  parse: (raw: string) => T | null;
@@ -50,23 +59,18 @@ type NamedParserRef = { [K in keyof KnownParsers]: {
50
59
  type ParserInput = (Parser<any> & {
51
60
  name?: never;
52
61
  }) | NamedParserRef;
53
- type UnwrapParser<P> = P extends Ref<infer U> ? U : P extends (() => infer U) ? U : P;
54
- type WithParser<T, P extends ParserInput | undefined> = Omit<T, 'parser'> & {
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'> & {
55
64
  parser?: P;
65
+ missing?: M;
56
66
  };
57
- type InferInputValue<P> = ResolveInputValue<UnwrapParser<P>>;
58
- type ResolveInputValue<P> = [P] extends [undefined] ? string | null : P extends ParserWithDefault<infer T> ? T : P extends Parser<infer T> ? T | null : P extends {
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 {
59
69
  name: infer K;
60
70
  defaultValue: any;
61
71
  } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> : string : P extends {
62
72
  name: infer K;
63
- } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> | null : string | null : string | null;
64
- type InferInputWritable<P> = InferInputValue<P> | null | undefined;
65
- //#endregion
66
- //#region src/types.d.ts
67
- type StateMode = 'optimistic' | 'source';
68
- type MaybeRefsOrGetters<T> = { [K in keyof T]: MaybeRefOrGetter<T[K]>; };
69
- type ReactiveFields<T, TStatic extends keyof T = never> = MaybeRefsOrGetters<Omit<T, TStatic>> & Pick<T, TStatic>;
70
- type ReactiveOptions<T, TStatic extends keyof T = never> = MaybeRefOrGetter<ReactiveFields<T, TStatic>>;
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;
71
75
  //#endregion
72
- export { unwrapDefault as A, parseAsInteger as C, parseAsStringEnum as D, parseAsString as E, parseAsStringLiteral as O, parseAsIndex as S, parseAsNumberLiteral as T, defineParser as _, CustomParsers as a, parseAsDate as b, InferParserValue as c, UnwrapParser as d, WithParser as f, ParserWithDefault as g, ParserOptions as h, BuiltinParsers as i, tryParse as k, KnownParsers as l, Parser as m, ReactiveOptions as n, InferInputValue as o, DefaultValue as p, StateMode as r, InferInputWritable as s, ReactiveFields as t, ParserInput as u, parseAsArrayOf as v, parseAsJson as w, parseAsFloat as x, parseAsBoolean 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-C2p8oQgu.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
  };
@@ -60,15 +45,16 @@ function resolveParser(input = { name: "parseAsString" }) {
60
45
  function normalizeRaw(raw) {
61
46
  return (Array.isArray(raw) ? raw.find((v) => v != null) : raw) ?? null;
62
47
  }
63
- function parseValue(parser, raw) {
48
+ function parseValue(options, raw) {
49
+ const { parser, missing } = options;
64
50
  const value = normalizeRaw(raw);
65
- if (value === null) return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
66
- const parsed = parser.parse(value);
51
+ const parsed = value === null ? null : parser.parse(value);
67
52
  if (parsed !== null) return parsed;
68
- return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
53
+ return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : missing;
69
54
  }
70
- function serializeValue(parser, clearOnDefault, next) {
71
- 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;
72
58
  const serialized = parser.serialize(next);
73
59
  if (clearOnDefault && parser.defaultValue !== void 0) {
74
60
  if (serialized === parser.serialize(unwrapDefault(parser.defaultValue))) return null;
@@ -76,4 +62,4 @@ function serializeValue(parser, clearOnDefault, next) {
76
62
  return serialized;
77
63
  }
78
64
  //#endregion
79
- export { toValueDeep as a, resolveParser as i, parseValue as n, serializeValue as r, normalizeRaw as t };
65
+ export { resolveParser as i, parseValue as n, serializeValue as r, normalizeRaw as t };
@@ -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) {
@@ -248,6 +259,7 @@ function createWagen(config = {}) {
248
259
  const { storage, destroy } = createStorage(config.storage ?? {});
249
260
  const wagen = markRaw({
250
261
  parsers: { ...config.parsers },
262
+ missing: resolveMissing(config, null),
251
263
  storage,
252
264
  router: {
253
265
  ...ROUTER_DEFAULTS,
@@ -275,4 +287,4 @@ function useWagen() {
275
287
  return getActiveWagen();
276
288
  }
277
289
  //#endregion
278
- 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.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Reactive state for URL and storage in Vue.",
5
5
  "keywords": [
6
6
  "composables",