vue-context-storage 0.1.41 → 0.1.43
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 +111 -50
- package/dist/index.d.ts +49 -20
- package/dist/index.js +148 -6
- package/package.json +15 -8
- package/dist/zod.d.ts +0 -59
- package/dist/zod.js +0 -68
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# vue-context-storage
|
|
2
2
|
|
|
3
|
-
Vue 3
|
|
3
|
+
Vue 3 reactive state management — sync state with the URL query, localStorage, and sessionStorage through a single type-safe composable.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/vue-context-storage)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
@@ -13,15 +13,29 @@ Vue 3 context storage system with URL query, localStorage, and sessionStorage sy
|
|
|
13
13
|
[](https://codecov.io/gh/lviobio/vue-context-storage)
|
|
14
14
|
[](https://lviobio.github.io/vue-context-storage/)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Key features:
|
|
17
17
|
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **Type-safe** TypeScript support
|
|
18
|
+
- **Automatic URL query sync** — painless type coercion (numbers, booleans, arrays) and nested objects
|
|
19
|
+
- **localStorage & sessionStorage** for persistent and session-scoped state — same API as the query handler
|
|
20
|
+
- **Context prefixing** to avoid key collisions when the same key is reused across instances
|
|
21
|
+
- **Type-safe** with optional Zod schema validation
|
|
23
22
|
- **Tree-shakeable** and lightweight
|
|
24
23
|
|
|
24
|
+
```ts
|
|
25
|
+
const filters = reactive({ search: '', page: 1 })
|
|
26
|
+
|
|
27
|
+
// reactive state ⇄ URL query — kept in sync automatically, both directions
|
|
28
|
+
useContextStorage('query', filters) // URL: /products?search=shoes&page=2
|
|
29
|
+
useContextStorage('query', filters, { key: 'filters' }) // URL: /products?filters[search]=shoes&filters[page]=2
|
|
30
|
+
// zod schema support with type coercion, 'page' will be converted to number
|
|
31
|
+
useContextStorage('query', filters, { schema: z.object({ search: z.string(), page: z.number() }) }) // URL: /products?filters[search]=shoes&filters[page]=2
|
|
32
|
+
// transform function support with type coercion, 'page' will be converted to number
|
|
33
|
+
useContextStorage('query', filters, {
|
|
34
|
+
transform: (value) => ({ search: value.search, page: Number(value.page) }),
|
|
35
|
+
}) // URL: /products?filters[search]=shoes&filters[page]=2
|
|
36
|
+
// And a lot of other features... (onlyChanges option; createEmptyZodObject helper for zod schemas; additional default values; passing 'key' via ContextStoragePrefix wrapper)
|
|
37
|
+
```
|
|
38
|
+
|
|
25
39
|
## Live Demo
|
|
26
40
|
|
|
27
41
|
🚀 **[Try the interactive playground](https://lviobio.github.io/vue-context-storage)**
|
|
@@ -147,12 +161,14 @@ useContextStorage('query', filters, {
|
|
|
147
161
|
})
|
|
148
162
|
|
|
149
163
|
// Option 2: Transform function
|
|
164
|
+
import { transform } from 'vue-context-storage'
|
|
165
|
+
|
|
150
166
|
useContextStorage('query', filters, {
|
|
151
167
|
key: 'filters',
|
|
152
168
|
transform: (deserialized, initial) => ({
|
|
153
|
-
page: asNumber(deserialized.page, { fallback: initial.page }),
|
|
154
|
-
search: asString(deserialized.search, { fallback: initial.search }),
|
|
155
|
-
status: asString(deserialized.status, { fallback: initial.status }),
|
|
169
|
+
page: transform.asNumber(deserialized.page, { fallback: initial.page }),
|
|
170
|
+
search: transform.asString(deserialized.search, { fallback: initial.search }),
|
|
171
|
+
status: transform.asString(deserialized.status, { fallback: initial.status }),
|
|
156
172
|
}),
|
|
157
173
|
})
|
|
158
174
|
```
|
|
@@ -280,7 +296,7 @@ The query handler supports arrays of objects. They are serialized as indexed que
|
|
|
280
296
|
?items[0][product]=Apple&items[0][quantity]=5&items[1][product]=Banana&items[1][quantity]=10
|
|
281
297
|
```
|
|
282
298
|
|
|
283
|
-
After deserialization, URL parameters produce indexed objects (`{ '0': {...}, '1': {...} }`) rather than arrays.
|
|
299
|
+
After deserialization, URL parameters produce indexed objects (`{ '0': {...}, '1': {...} }`) rather than arrays. With a Zod schema, plain `z.array(...)` works out of the box — the library converts indexed objects back to sorted arrays automatically. With the manual approach, use `transform.asObjectArray`.
|
|
284
300
|
|
|
285
301
|
**With transform helpers:**
|
|
286
302
|
|
|
@@ -310,7 +326,6 @@ useContextStorage('query', data, {
|
|
|
310
326
|
|
|
311
327
|
```typescript
|
|
312
328
|
import { z } from 'zod'
|
|
313
|
-
import { zObjectArray } from 'vue-context-storage/zod'
|
|
314
329
|
|
|
315
330
|
const ItemSchema = z.object({
|
|
316
331
|
product: z.string().default(''),
|
|
@@ -319,13 +334,13 @@ const ItemSchema = z.object({
|
|
|
319
334
|
|
|
320
335
|
const DataSchema = z.object({
|
|
321
336
|
title: z.string().default(''),
|
|
322
|
-
items:
|
|
337
|
+
items: z.array(ItemSchema),
|
|
323
338
|
})
|
|
324
339
|
|
|
325
340
|
useContextStorage('query', data, { schema: DataSchema })
|
|
326
341
|
```
|
|
327
342
|
|
|
328
|
-
See [
|
|
343
|
+
See [Automatic Type Coercion](#automatic-type-coercion) for details on how indexed objects are converted.
|
|
329
344
|
|
|
330
345
|
### Preserve Empty State
|
|
331
346
|
|
|
@@ -360,6 +375,55 @@ useContextStorage('query', data, {
|
|
|
360
375
|
// page=2 → appears in query as ?filters[page]=2
|
|
361
376
|
```
|
|
362
377
|
|
|
378
|
+
When using Zod schemas, you can also specify `additionalDefaultData` per-field via `.meta()` instead of (or in addition to) the option:
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
const Schema = z.object({
|
|
382
|
+
page: z.coerce.number().default(1).meta({ additionalDefaultData: 3 }),
|
|
383
|
+
search: z.string().default(''),
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
const data = reactive({ page: undefined as number | undefined, search: '' })
|
|
387
|
+
|
|
388
|
+
useContextStorage('query', data, {
|
|
389
|
+
key: 'filters',
|
|
390
|
+
schema: Schema,
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
// page=1 → not in query (matches default from schema)
|
|
394
|
+
// page=2 → appears in query as ?filters[page]=2
|
|
395
|
+
// page=3 → not in query (matches additionalDefaultData from schema meta)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
A field's schema `.default(...)` value is itself treated as a default baseline: a value equal to it is omitted from the URL, exactly like the initial snapshot and `additionalDefaultData`. So both the `.default()` value and any `.meta({ additionalDefaultData })` value are excluded, while every other value appears in the query.
|
|
399
|
+
|
|
400
|
+
Each source of defaults is an **independent baseline** — the initial snapshot, the option-level `additionalDefaultData`, the schema `.meta({ additionalDefaultData })`, and the schema `.default()`. A value is omitted from the URL if it matches **any** of them, so they never overwrite each other (e.g. a field can have a `.default()` of `1`, a meta value of `5`, and an option value of `3`, and all three are excluded).
|
|
401
|
+
|
|
402
|
+
Nested objects work the same way. Following the documented best practice of declaring `.default()` at the object level, a value matching the nested default is omitted too:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
const Schema = z.object({
|
|
406
|
+
filters: z
|
|
407
|
+
.object({
|
|
408
|
+
page: z.coerce.number(),
|
|
409
|
+
sort: z.string(),
|
|
410
|
+
})
|
|
411
|
+
.default({ page: 1, sort: 'asc' }),
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
const data = reactive({
|
|
415
|
+
filters: { page: undefined as number | undefined, sort: undefined as string | undefined },
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
useContextStorage('query', data, { key: 'q', schema: Schema })
|
|
419
|
+
|
|
420
|
+
// filters = { page: 1, sort: 'asc' } → not in query (matches the nested default)
|
|
421
|
+
// filters = { page: 2, sort: 'asc' } → ?q[filters][page]=2 (sort matches default, omitted)
|
|
422
|
+
// filters = { page: 2, sort: 'desc' } → ?q[filters][page]=2&q[filters][sort]=desc
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Field-level defaults inside a nested object take priority over the object-level default and fill in any fields the object-level default omits.
|
|
426
|
+
|
|
363
427
|
### Configure Query Handler
|
|
364
428
|
|
|
365
429
|
Customize behavior by passing options to the factory:
|
|
@@ -685,33 +749,14 @@ transform.asNumber(value, {
|
|
|
685
749
|
})
|
|
686
750
|
```
|
|
687
751
|
|
|
688
|
-
## Zod Helpers
|
|
752
|
+
## Zod Helpers
|
|
689
753
|
|
|
690
|
-
|
|
754
|
+
Zod-specific helpers are exported from the main entry point. `zod` remains an optional peer dependency — the helpers are implemented via duck-typed schema introspection, so the main bundle never imports Zod at runtime.
|
|
691
755
|
|
|
692
756
|
```bash
|
|
693
757
|
npm install zod
|
|
694
758
|
```
|
|
695
759
|
|
|
696
|
-
### `zObjectArray(itemSchema)`
|
|
697
|
-
|
|
698
|
-
Creates a Zod schema for arrays of objects serialized as indexed query parameters. Wraps `z.record()` + `.transform()` to convert indexed objects back to sorted arrays.
|
|
699
|
-
|
|
700
|
-
```typescript
|
|
701
|
-
import { z } from 'zod'
|
|
702
|
-
import { zObjectArray } from 'vue-context-storage/zod'
|
|
703
|
-
|
|
704
|
-
const ItemSchema = z.object({
|
|
705
|
-
product: z.string().default(''),
|
|
706
|
-
quantity: z.coerce.number().default(0),
|
|
707
|
-
})
|
|
708
|
-
|
|
709
|
-
const DataSchema = z.object({
|
|
710
|
-
title: z.string().default(''),
|
|
711
|
-
items: zObjectArray(ItemSchema),
|
|
712
|
-
})
|
|
713
|
-
```
|
|
714
|
-
|
|
715
760
|
### Automatic Type Coercion
|
|
716
761
|
|
|
717
762
|
When a `schema` is provided, the library automatically coerces URL query parameter values to match the expected Zod types before validation. This handles two common URL serialization quirks:
|
|
@@ -740,6 +785,22 @@ const Schema = z.object({
|
|
|
740
785
|
// (no tags param) → { tags: [], ... }
|
|
741
786
|
```
|
|
742
787
|
|
|
788
|
+
Arrays of objects are also handled automatically. They deserialize from indexed query parameters (`?items[0][product]=Apple`) into indexed objects (`{ '0': { product: 'Apple' } }`), and the library converts them back to arrays sorted by numeric key wherever the schema expects `.array()` — coercion is applied recursively inside array elements as well:
|
|
789
|
+
|
|
790
|
+
```typescript
|
|
791
|
+
const ItemSchema = z.object({
|
|
792
|
+
product: z.string().default(''),
|
|
793
|
+
quantity: z.coerce.number().default(0),
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
const Schema = z.object({
|
|
797
|
+
items: z.array(ItemSchema),
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
// ?items[0][product]=Apple&items[0][quantity]=5
|
|
801
|
+
// → { items: [{ product: 'Apple', quantity: 5 }] }
|
|
802
|
+
```
|
|
803
|
+
|
|
743
804
|
#### Boolean Coercion
|
|
744
805
|
|
|
745
806
|
The query handler serializes booleans as `'1'`/`'0'` strings in URL parameters (e.g. `?active=1`). Standard `z.coerce.boolean()` cannot be used because `Boolean('0')` is `true` in JavaScript. The library automatically converts `'1'` → `true` and `'0'` → `false` when the schema expects a boolean field. **No special helpers are needed** — plain `z.boolean()` works out of the box:
|
|
@@ -754,13 +815,13 @@ const Schema = z.object({
|
|
|
754
815
|
// ?active=0 → { active: false, enabled: true }
|
|
755
816
|
```
|
|
756
817
|
|
|
757
|
-
### `
|
|
818
|
+
### `createEmptyZodObject(schema, options?)`
|
|
758
819
|
|
|
759
820
|
Creates a plain object with empty/default values based on a Zod schema. Useful for initializing reactive data from a schema definition.
|
|
760
821
|
|
|
761
822
|
```typescript
|
|
762
823
|
import { z } from 'zod'
|
|
763
|
-
import {
|
|
824
|
+
import { createEmptyZodObject } from 'vue-context-storage'
|
|
764
825
|
|
|
765
826
|
const FiltersSchema = z.object({
|
|
766
827
|
search: z.string().default(''),
|
|
@@ -769,7 +830,7 @@ const FiltersSchema = z.object({
|
|
|
769
830
|
score: z.number().nullable(),
|
|
770
831
|
})
|
|
771
832
|
|
|
772
|
-
const filters = reactive(
|
|
833
|
+
const filters = reactive(createEmptyZodObject(FiltersSchema))
|
|
773
834
|
// Result: { search: '', page: 1, active: false, score: null }
|
|
774
835
|
```
|
|
775
836
|
|
|
@@ -779,24 +840,24 @@ const filters = reactive(createSchemaObject(FiltersSchema))
|
|
|
779
840
|
- `withSchema` (default: `false`) — When `true`, attaches the schema to the result object via `SCHEMA_SYMBOL` (wrapped with `markRaw`). Nested objects also receive their respective schemas.
|
|
780
841
|
|
|
781
842
|
```typescript
|
|
782
|
-
import {
|
|
843
|
+
import { createEmptyZodObject, SCHEMA_SYMBOL } from 'vue-context-storage'
|
|
783
844
|
|
|
784
|
-
const data =
|
|
845
|
+
const data = createEmptyZodObject(FiltersSchema, { withSchema: true })
|
|
785
846
|
data[SCHEMA_SYMBOL] // → FiltersSchema
|
|
786
847
|
```
|
|
787
848
|
|
|
788
849
|
**Type-based defaults** (when `useDefaults: false` or no `.default()` is set):
|
|
789
850
|
|
|
790
|
-
| Zod type | Default value
|
|
791
|
-
| ------------- |
|
|
792
|
-
| `z.string()` | `''`
|
|
793
|
-
| `z.number()` | `0` (respects `.min()` / `.positive()`)
|
|
794
|
-
| `z.boolean()` | `false`
|
|
795
|
-
| `z.array()` | `[]`
|
|
796
|
-
| `z.object()` | Recursively created via `
|
|
797
|
-
| `z.date()` | `null`
|
|
798
|
-
| `.nullable()` | `null`
|
|
799
|
-
| `.optional()` | `undefined`
|
|
851
|
+
| Zod type | Default value |
|
|
852
|
+
| ------------- | ---------------------------------------------- |
|
|
853
|
+
| `z.string()` | `''` |
|
|
854
|
+
| `z.number()` | `0` (respects `.min()` / `.positive()`) |
|
|
855
|
+
| `z.boolean()` | `false` |
|
|
856
|
+
| `z.array()` | `[]` |
|
|
857
|
+
| `z.object()` | Recursively created via `createEmptyZodObject` |
|
|
858
|
+
| `z.date()` | `null` |
|
|
859
|
+
| `.nullable()` | `null` |
|
|
860
|
+
| `.optional()` | `undefined` |
|
|
800
861
|
|
|
801
862
|
## TypeScript Support
|
|
802
863
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as vue0 from "vue";
|
|
2
2
|
import { ComputedRef, InjectionKey, MaybeRefOrGetter, Plugin, PropType, UnwrapNestedRefs } from "vue";
|
|
3
3
|
import { LocationQuery, LocationQueryValue } from "vue-router";
|
|
4
|
+
import { ZodObject, ZodRawShape, z } from "zod";
|
|
4
5
|
|
|
5
6
|
//#region src/components/ContextStorageActivator.vue.d.ts
|
|
6
7
|
declare const _default$1: typeof __VLS_export$4;
|
|
7
|
-
declare const __VLS_export$4:
|
|
8
|
+
declare const __VLS_export$4: vue0.DefineComponent<{}, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
|
|
8
9
|
[key: string]: any;
|
|
9
|
-
}>, {}, {}, {},
|
|
10
|
+
}>, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
|
|
10
11
|
//#endregion
|
|
11
12
|
//#region src/handlers/types.d.ts
|
|
12
13
|
interface HandlerSchema<T> {
|
|
@@ -65,7 +66,7 @@ interface ContextStorageHandler<T, O> {
|
|
|
65
66
|
//#endregion
|
|
66
67
|
//#region src/components/ContextStorageCollection.vue.d.ts
|
|
67
68
|
declare const _default$2: typeof __VLS_export$3;
|
|
68
|
-
declare const __VLS_export$3:
|
|
69
|
+
declare const __VLS_export$3: vue0.DefineComponent<vue0.ExtractPropTypes<{
|
|
69
70
|
handlers: {
|
|
70
71
|
type: PropType<ContextStorageHandlerFactory[]>;
|
|
71
72
|
default: () => ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
@@ -81,9 +82,9 @@ declare const __VLS_export$3: vue9.DefineComponent<vue9.ExtractPropTypes<{
|
|
|
81
82
|
type: PropType<ContextStorageHandlerFactory[]>;
|
|
82
83
|
default: undefined;
|
|
83
84
|
};
|
|
84
|
-
}>, () =>
|
|
85
|
+
}>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
|
|
85
86
|
[key: string]: any;
|
|
86
|
-
}>[] | undefined, {}, {}, {},
|
|
87
|
+
}>[] | undefined, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<vue0.ExtractPropTypes<{
|
|
87
88
|
handlers: {
|
|
88
89
|
type: PropType<ContextStorageHandlerFactory[]>;
|
|
89
90
|
default: () => ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
@@ -102,27 +103,27 @@ declare const __VLS_export$3: vue9.DefineComponent<vue9.ExtractPropTypes<{
|
|
|
102
103
|
}>> & Readonly<{}>, {
|
|
103
104
|
handlers: ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
104
105
|
additionalHandlers: ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
105
|
-
}, {}, {}, {}, string,
|
|
106
|
+
}, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
|
|
106
107
|
//#endregion
|
|
107
108
|
//#region src/components/ContextStorageProvider.vue.d.ts
|
|
108
109
|
declare const _default$4: typeof __VLS_export$2;
|
|
109
|
-
declare const __VLS_export$2:
|
|
110
|
+
declare const __VLS_export$2: vue0.DefineComponent<vue0.ExtractPropTypes<{
|
|
110
111
|
itemKey: {
|
|
111
112
|
type: StringConstructor;
|
|
112
113
|
required: true;
|
|
113
114
|
};
|
|
114
|
-
}>, () =>
|
|
115
|
+
}>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
|
|
115
116
|
[key: string]: any;
|
|
116
|
-
}>[] | undefined, {}, {}, {},
|
|
117
|
+
}>[] | undefined, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<vue0.ExtractPropTypes<{
|
|
117
118
|
itemKey: {
|
|
118
119
|
type: StringConstructor;
|
|
119
120
|
required: true;
|
|
120
121
|
};
|
|
121
|
-
}>> & Readonly<{}>, {}, {}, {}, {}, string,
|
|
122
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
|
|
122
123
|
//#endregion
|
|
123
124
|
//#region src/components/ContextStorage.vue.d.ts
|
|
124
125
|
declare const _default: typeof __VLS_export$1;
|
|
125
|
-
declare const __VLS_export$1:
|
|
126
|
+
declare const __VLS_export$1: vue0.DefineComponent<vue0.ExtractPropTypes<{
|
|
126
127
|
handlers: {
|
|
127
128
|
type: PropType<ContextStorageHandlerFactory[]>;
|
|
128
129
|
default: () => ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
@@ -138,9 +139,9 @@ declare const __VLS_export$1: vue9.DefineComponent<vue9.ExtractPropTypes<{
|
|
|
138
139
|
type: PropType<ContextStorageHandlerFactory[]>;
|
|
139
140
|
default: undefined;
|
|
140
141
|
};
|
|
141
|
-
}>, () =>
|
|
142
|
+
}>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
|
|
142
143
|
[key: string]: any;
|
|
143
|
-
}>[] | undefined, {}, {}, {},
|
|
144
|
+
}>[] | undefined, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<vue0.ExtractPropTypes<{
|
|
144
145
|
handlers: {
|
|
145
146
|
type: PropType<ContextStorageHandlerFactory[]>;
|
|
146
147
|
default: () => ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
@@ -159,7 +160,7 @@ declare const __VLS_export$1: vue9.DefineComponent<vue9.ExtractPropTypes<{
|
|
|
159
160
|
}>> & Readonly<{}>, {
|
|
160
161
|
handlers: ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
161
162
|
additionalHandlers: ContextStorageHandlerFactory<{}, RegisterOptions<{}>>[];
|
|
162
|
-
}, {}, {}, {}, string,
|
|
163
|
+
}, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
|
|
163
164
|
//#endregion
|
|
164
165
|
//#region src/prefix.d.ts
|
|
165
166
|
/**
|
|
@@ -181,19 +182,19 @@ declare const contextStoragePrefixSegmentsInjectKey: InjectionKey<MaybeRefOrGett
|
|
|
181
182
|
//#endregion
|
|
182
183
|
//#region src/components/ContextStoragePrefix.vue.d.ts
|
|
183
184
|
declare const _default$3: typeof __VLS_export;
|
|
184
|
-
declare const __VLS_export:
|
|
185
|
+
declare const __VLS_export: vue0.DefineComponent<vue0.ExtractPropTypes<{
|
|
185
186
|
name: {
|
|
186
187
|
type: PropType<ContextStoragePrefixSegment>;
|
|
187
188
|
required: true;
|
|
188
189
|
};
|
|
189
|
-
}>, () =>
|
|
190
|
+
}>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
|
|
190
191
|
[key: string]: any;
|
|
191
|
-
}>, {}, {}, {},
|
|
192
|
+
}>, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<vue0.ExtractPropTypes<{
|
|
192
193
|
name: {
|
|
193
194
|
type: PropType<ContextStoragePrefixSegment>;
|
|
194
195
|
required: true;
|
|
195
196
|
};
|
|
196
|
-
}>> & Readonly<{}>, {}, {}, {}, {}, string,
|
|
197
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
|
|
197
198
|
//#endregion
|
|
198
199
|
//#region src/plugin.d.ts
|
|
199
200
|
declare const VueContextStoragePlugin: Plugin;
|
|
@@ -456,6 +457,34 @@ declare function createLocalStorageHandler(customOptions?: WebStorageHandlerBase
|
|
|
456
457
|
//#region src/handlers/session-storage/index.d.ts
|
|
457
458
|
declare function createSessionStorageHandler(customOptions?: WebStorageHandlerBaseOptions): ContextStorageHandlerFactory;
|
|
458
459
|
//#endregion
|
|
460
|
+
//#region src/zod.d.ts
|
|
461
|
+
declare const SCHEMA_SYMBOL: unique symbol;
|
|
462
|
+
/**
|
|
463
|
+
* This type is "Maybe", but always has a schema attached to it.
|
|
464
|
+
* It's used to avoid any type errors with transformations.
|
|
465
|
+
*/
|
|
466
|
+
type MaybeWithSchema<T extends ZodRawShape> = z.infer<ZodObject<T>> & {
|
|
467
|
+
[SCHEMA_SYMBOL]?: ZodObject<T>;
|
|
468
|
+
};
|
|
469
|
+
/**
|
|
470
|
+
* Creates an object with empty values based on a Zod schema.
|
|
471
|
+
* Useful for initializing forms.
|
|
472
|
+
*
|
|
473
|
+
* When `withSchema` is `true`, attaches the schema to the result via `SCHEMA_SYMBOL`
|
|
474
|
+
* (wrapped with `markRaw` to prevent Vue reactivity on the schema instance).
|
|
475
|
+
*
|
|
476
|
+
* Implemented via duck-typed introspection (`_zod.def.type`) — `zod` is only
|
|
477
|
+
* imported as types, so the main bundle never depends on it at runtime.
|
|
478
|
+
*/
|
|
479
|
+
declare function createEmptyZodObject<T extends ZodRawShape>(schema: ZodObject<T>, options: {
|
|
480
|
+
useDefaults?: boolean;
|
|
481
|
+
withSchema: true;
|
|
482
|
+
}): MaybeWithSchema<T>;
|
|
483
|
+
declare function createEmptyZodObject<T extends ZodRawShape>(schema: ZodObject<T>, options?: {
|
|
484
|
+
useDefaults?: boolean;
|
|
485
|
+
withSchema?: false;
|
|
486
|
+
}): z.infer<ZodObject<T>>;
|
|
487
|
+
//#endregion
|
|
459
488
|
//#region src/handlers/query/transform-helpers.d.ts
|
|
460
489
|
declare function asNumber(value: QueryValue | number | undefined): number;
|
|
461
490
|
declare function asNumber(value: QueryValue | number | undefined, options: {
|
|
@@ -660,4 +689,4 @@ declare const contextStorageSessionStorageHandlerInjectKey: InjectionKey<Context
|
|
|
660
689
|
//#region src/constants.d.ts
|
|
661
690
|
declare const defaultHandlers: ContextStorageHandlerFactory[];
|
|
662
691
|
//#endregion
|
|
663
|
-
export { type CollectionManager, type CollectionManagerItem, _default as ContextStorage, _default$1 as ContextStorageActivator, _default$2 as ContextStorageCollection, type ContextStorageHandler, type ContextStorageHandlerFactory, type ContextStorageHandlerMap, _default$3 as ContextStoragePrefix, type ContextStoragePrefixSegment, _default$4 as ContextStorageProvider, type WebStorageHandlerBaseOptions as LocalStorageHandlerBaseOptions, type QueryValue, type RegisterBaseOptions, type RegisterWebStorageHandlerBaseOptions as RegisterLocalStorageHandlerBaseOptions, VueContextStoragePlugin, asArray, asBoolean, asNumber, asNumberArray, asObjectArray, asString, contextStorageCollectionInjectKey, contextStorageCollectionItemInjectKey, contextStorageHandlersInjectKey, contextStorageLocalStorageHandlerInjectKey, contextStoragePrefixSegmentsInjectKey, contextStorageQueryHandlerInjectKey, contextStorageSessionStorageHandlerInjectKey, createCollectionManager, createLocalStorageHandler, createQueryHandler, createSessionStorageHandler, defaultHandlers, defineContextStorageHandler, deserializeParams as deserializeQueryParams, resolveHandlerInjectionKey, serializeParams as serializeQueryParams, transform, useContextStorage, useContextStorageActivator, useContextStorageCollection, useContextStorageProvider };
|
|
692
|
+
export { type CollectionManager, type CollectionManagerItem, _default as ContextStorage, _default$1 as ContextStorageActivator, _default$2 as ContextStorageCollection, type ContextStorageHandler, type ContextStorageHandlerFactory, type ContextStorageHandlerMap, _default$3 as ContextStoragePrefix, type ContextStoragePrefixSegment, _default$4 as ContextStorageProvider, type WebStorageHandlerBaseOptions as LocalStorageHandlerBaseOptions, type MaybeWithSchema, type QueryValue, type RegisterBaseOptions, type RegisterWebStorageHandlerBaseOptions as RegisterLocalStorageHandlerBaseOptions, SCHEMA_SYMBOL, VueContextStoragePlugin, asArray, asBoolean, asNumber, asNumberArray, asObjectArray, asString, contextStorageCollectionInjectKey, contextStorageCollectionItemInjectKey, contextStorageHandlersInjectKey, contextStorageLocalStorageHandlerInjectKey, contextStoragePrefixSegmentsInjectKey, contextStorageQueryHandlerInjectKey, contextStorageSessionStorageHandlerInjectKey, createCollectionManager, createEmptyZodObject, createLocalStorageHandler, createQueryHandler, createSessionStorageHandler, defaultHandlers, defineContextStorageHandler, deserializeParams as deserializeQueryParams, resolveHandlerInjectionKey, serializeParams as serializeQueryParams, transform, useContextStorage, useContextStorageActivator, useContextStorageCollection, useContextStorageProvider };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, defineComponent, getCurrentInstance, h, inject, onBeforeUnmount, onUnmounted, provide, toValue, watch } from "vue";
|
|
1
|
+
import { computed, defineComponent, getCurrentInstance, h, inject, markRaw, onBeforeUnmount, onUnmounted, provide, toValue, watch } from "vue";
|
|
2
2
|
import { useRoute, useRouter } from "vue-router";
|
|
3
3
|
import { cloneDeep, isEqual, merge, omit, pick } from "lodash";
|
|
4
4
|
|
|
@@ -130,6 +130,78 @@ function unwrapZodField(field) {
|
|
|
130
130
|
}
|
|
131
131
|
return t;
|
|
132
132
|
}
|
|
133
|
+
function readZodFieldMeta(field) {
|
|
134
|
+
let t = field;
|
|
135
|
+
while (true) {
|
|
136
|
+
const m = typeof t?.meta === "function" ? t.meta() : void 0;
|
|
137
|
+
if (m !== void 0) return m;
|
|
138
|
+
const type = zodDefType(t);
|
|
139
|
+
if (type === "default" || type === "optional" || type === "nullable") t = t.unwrap();
|
|
140
|
+
else break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function extractAdditionalDefaultDataFromSchema(schema) {
|
|
144
|
+
const shape = schema?.shape;
|
|
145
|
+
if (!shape || typeof shape !== "object") return void 0;
|
|
146
|
+
let result;
|
|
147
|
+
for (const key of Object.keys(shape)) {
|
|
148
|
+
const meta = readZodFieldMeta(shape[key]);
|
|
149
|
+
if (meta && "additionalDefaultData" in meta) {
|
|
150
|
+
if (!result) result = {};
|
|
151
|
+
result[key] = meta.additionalDefaultData;
|
|
152
|
+
} else {
|
|
153
|
+
const base = unwrapZodField(shape[key]);
|
|
154
|
+
if (zodDefType(base) === "object" && base.shape) {
|
|
155
|
+
const nested = extractAdditionalDefaultDataFromSchema(base);
|
|
156
|
+
if (nested) {
|
|
157
|
+
if (!result) result = {};
|
|
158
|
+
result[key] = nested;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
function readZodFieldDefault(field) {
|
|
166
|
+
let t = field;
|
|
167
|
+
while (true) {
|
|
168
|
+
const type = zodDefType(t);
|
|
169
|
+
if (type === "default") return {
|
|
170
|
+
hasDefault: true,
|
|
171
|
+
value: t._zod.def.defaultValue
|
|
172
|
+
};
|
|
173
|
+
if (type === "optional" || type === "nullable") t = t.unwrap();
|
|
174
|
+
else break;
|
|
175
|
+
}
|
|
176
|
+
return { hasDefault: false };
|
|
177
|
+
}
|
|
178
|
+
function extractDefaultsFromSchema(schema) {
|
|
179
|
+
const shape = schema?.shape;
|
|
180
|
+
if (!shape || typeof shape !== "object") return void 0;
|
|
181
|
+
let result;
|
|
182
|
+
for (const key of Object.keys(shape)) {
|
|
183
|
+
const base = unwrapZodField(shape[key]);
|
|
184
|
+
if (zodDefType(base) === "object" && base.shape) {
|
|
185
|
+
const { hasDefault: hasDefault2, value: objectDefault } = readZodFieldDefault(shape[key]);
|
|
186
|
+
const nested = extractDefaultsFromSchema(base);
|
|
187
|
+
let combined;
|
|
188
|
+
if (hasDefault2 && nested) combined = merge({}, objectDefault, nested);
|
|
189
|
+
else if (hasDefault2) combined = objectDefault;
|
|
190
|
+
else combined = nested;
|
|
191
|
+
if (combined !== void 0) {
|
|
192
|
+
if (!result) result = {};
|
|
193
|
+
result[key] = combined;
|
|
194
|
+
}
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const { hasDefault, value } = readZodFieldDefault(shape[key]);
|
|
198
|
+
if (hasDefault) {
|
|
199
|
+
if (!result) result = {};
|
|
200
|
+
result[key] = value;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
133
205
|
function coerceDataForSchema(data, schema) {
|
|
134
206
|
const shape = schema?.shape;
|
|
135
207
|
if (!shape || typeof shape !== "object") return data;
|
|
@@ -144,7 +216,15 @@ function coerceDataForSchema(data, schema) {
|
|
|
144
216
|
else if (value === "0") result[key] = false;
|
|
145
217
|
} else if (baseType === "array") {
|
|
146
218
|
const value = result[key];
|
|
147
|
-
if (value !== void 0 && value !== null
|
|
219
|
+
if (value !== void 0 && value !== null) {
|
|
220
|
+
let arr;
|
|
221
|
+
if (Array.isArray(value)) arr = value;
|
|
222
|
+
else if (typeof value === "object") arr = Object.entries(value).sort(([a], [b]) => Number(a) - Number(b)).map(([, v]) => v);
|
|
223
|
+
else arr = [value];
|
|
224
|
+
const elementBase = unwrapZodField(base.element ?? base._zod?.def?.element);
|
|
225
|
+
if (zodDefType(elementBase) === "object" && elementBase.shape) arr = arr.map((v) => v && typeof v === "object" && !Array.isArray(v) ? coerceDataForSchema(v, elementBase) : v);
|
|
226
|
+
result[key] = arr;
|
|
227
|
+
}
|
|
148
228
|
} else if (baseType === "object" && base.shape) {
|
|
149
229
|
const nested = result[key];
|
|
150
230
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) result[key] = coerceDataForSchema(nested, base);
|
|
@@ -258,7 +338,7 @@ function buildQuery(input) {
|
|
|
258
338
|
warnings.push("[vue-context-storage] preserveEmptyState is not supported with onlyChanges");
|
|
259
339
|
}
|
|
260
340
|
Object.keys(patch).forEach((key2) => {
|
|
261
|
-
if (isEqual(patch[key2], item.initialQueryData[key2]) || item.additionalDefaultQueryData && isEqual(patch[key2], item.additionalDefaultQueryData[key2])) delete patch[key2];
|
|
341
|
+
if (isEqual(patch[key2], item.initialQueryData[key2]) || item.additionalDefaultQueryData && isEqual(patch[key2], item.additionalDefaultQueryData[key2]) || item.schemaMetaDefaultQueryData && isEqual(patch[key2], item.schemaMetaDefaultQueryData[key2]) || item.schemaDefaultQueryData && isEqual(patch[key2], item.schemaDefaultQueryData[key2])) delete patch[key2];
|
|
262
342
|
});
|
|
263
343
|
}
|
|
264
344
|
const patchKeys = Object.keys(patch);
|
|
@@ -457,11 +537,18 @@ function createQueryHandler(baseOptions) {
|
|
|
457
537
|
scheduleSyncToQuery();
|
|
458
538
|
}, { deep: true });
|
|
459
539
|
const initialData = cloneDeep(resolvedData);
|
|
540
|
+
const initialQueryData = serializeParams(initialData, { key: registerOptions.key });
|
|
541
|
+
const additionalDefaultQueryData = registerOptions.additionalDefaultData ? serializeParams(registerOptions.additionalDefaultData, { key: registerOptions.key }) : void 0;
|
|
542
|
+
const schemaMetaDefaults = registerOptions.schema ? extractAdditionalDefaultDataFromSchema(registerOptions.schema) : void 0;
|
|
543
|
+
const schemaMetaDefaultQueryData = schemaMetaDefaults ? serializeParams(schemaMetaDefaults, { key: registerOptions.key }) : void 0;
|
|
544
|
+
const schemaDefaults = registerOptions.schema ? extractDefaultsFromSchema(registerOptions.schema) : void 0;
|
|
460
545
|
const item = {
|
|
461
546
|
data,
|
|
462
547
|
initialData,
|
|
463
|
-
initialQueryData
|
|
464
|
-
additionalDefaultQueryData
|
|
548
|
+
initialQueryData,
|
|
549
|
+
additionalDefaultQueryData,
|
|
550
|
+
schemaMetaDefaultQueryData,
|
|
551
|
+
schemaDefaultQueryData: schemaDefaults ? serializeParams(schemaDefaults, { key: registerOptions.key }) : void 0,
|
|
465
552
|
options: registerOptions,
|
|
466
553
|
watchHandle
|
|
467
554
|
};
|
|
@@ -501,6 +588,8 @@ function createQueryHandler(baseOptions) {
|
|
|
501
588
|
data: toValue(item.data),
|
|
502
589
|
initialQueryData: item.initialQueryData,
|
|
503
590
|
additionalDefaultQueryData: item.additionalDefaultQueryData,
|
|
591
|
+
schemaMetaDefaultQueryData: item.schemaMetaDefaultQueryData,
|
|
592
|
+
schemaDefaultQueryData: item.schemaDefaultQueryData,
|
|
504
593
|
key: item.options?.key,
|
|
505
594
|
onlyChanges: item.options?.onlyChanges,
|
|
506
595
|
preserveEmptyState: item.options?.preserveEmptyState,
|
|
@@ -929,6 +1018,59 @@ function useContextStorage(type, data, options) {
|
|
|
929
1018
|
return buildContextStorageHandler(handler, data, options);
|
|
930
1019
|
}
|
|
931
1020
|
|
|
1021
|
+
//#endregion
|
|
1022
|
+
//#region src/zod.ts
|
|
1023
|
+
const SCHEMA_SYMBOL = /* @__PURE__ */ Symbol("schema");
|
|
1024
|
+
function createEmptyZodObject(schema, options) {
|
|
1025
|
+
const shape = schema.shape;
|
|
1026
|
+
const result = {};
|
|
1027
|
+
const { useDefaults = true } = options || {};
|
|
1028
|
+
for (const key in shape) {
|
|
1029
|
+
const field = shape[key];
|
|
1030
|
+
let hasDefault = false;
|
|
1031
|
+
let isNullable = false;
|
|
1032
|
+
let isOptional = false;
|
|
1033
|
+
let base = field;
|
|
1034
|
+
while (true) {
|
|
1035
|
+
const type = zodDefType(base);
|
|
1036
|
+
if (type === "default") hasDefault = true;
|
|
1037
|
+
else if (type === "nullable") isNullable = true;
|
|
1038
|
+
else if (type === "optional") isOptional = true;
|
|
1039
|
+
else break;
|
|
1040
|
+
base = base.unwrap();
|
|
1041
|
+
}
|
|
1042
|
+
if (useDefaults && hasDefault) {
|
|
1043
|
+
result[key] = field.parse(void 0);
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (isNullable) {
|
|
1047
|
+
result[key] = null;
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
if (isOptional) {
|
|
1051
|
+
result[key] = void 0;
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
const baseType = zodDefType(base);
|
|
1055
|
+
if (baseType === "string") result[key] = "";
|
|
1056
|
+
else if (baseType === "number") {
|
|
1057
|
+
const minValue = base.minValue;
|
|
1058
|
+
let defaultValue = minValue !== null && minValue !== void 0 && isFinite(minValue) ? minValue : 0;
|
|
1059
|
+
if (!base.safeParse(defaultValue).success) defaultValue += 1;
|
|
1060
|
+
result[key] = defaultValue;
|
|
1061
|
+
} else if (baseType === "boolean") result[key] = false;
|
|
1062
|
+
else if (baseType === "array") result[key] = [];
|
|
1063
|
+
else if (baseType === "object" && base.shape) result[key] = createEmptyZodObject(base, {
|
|
1064
|
+
useDefaults: options?.useDefaults,
|
|
1065
|
+
withSchema: options?.withSchema
|
|
1066
|
+
});
|
|
1067
|
+
else if (baseType === "date") result[key] = null;
|
|
1068
|
+
else result[key] = void 0;
|
|
1069
|
+
}
|
|
1070
|
+
if (options?.withSchema) result[SCHEMA_SYMBOL] = markRaw(schema);
|
|
1071
|
+
return result;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
932
1074
|
//#endregion
|
|
933
1075
|
//#region src/handlers/query/transform-helpers.ts
|
|
934
1076
|
function asNumber(value, options) {
|
|
@@ -1006,4 +1148,4 @@ const transform = {
|
|
|
1006
1148
|
};
|
|
1007
1149
|
|
|
1008
1150
|
//#endregion
|
|
1009
|
-
export { ContextStorage_default as ContextStorage, ContextStorageActivator_default as ContextStorageActivator, ContextStorageCollection_default as ContextStorageCollection, ContextStoragePrefix_default as ContextStoragePrefix, ContextStorageProvider_default as ContextStorageProvider, VueContextStoragePlugin, asArray, asBoolean, asNumber, asNumberArray, asObjectArray, asString, contextStorageCollectionInjectKey, contextStorageCollectionItemInjectKey, contextStorageHandlersInjectKey, contextStorageLocalStorageHandlerInjectKey, contextStoragePrefixSegmentsInjectKey, contextStorageQueryHandlerInjectKey, contextStorageSessionStorageHandlerInjectKey, createCollectionManager, createLocalStorageHandler, createQueryHandler, createSessionStorageHandler, defaultHandlers, defineContextStorageHandler, deserializeParams as deserializeQueryParams, resolveHandlerInjectionKey, serializeParams as serializeQueryParams, transform, useContextStorage, useContextStorageActivator, useContextStorageCollection, useContextStorageProvider };
|
|
1151
|
+
export { ContextStorage_default as ContextStorage, ContextStorageActivator_default as ContextStorageActivator, ContextStorageCollection_default as ContextStorageCollection, ContextStoragePrefix_default as ContextStoragePrefix, ContextStorageProvider_default as ContextStorageProvider, SCHEMA_SYMBOL, VueContextStoragePlugin, asArray, asBoolean, asNumber, asNumberArray, asObjectArray, asString, contextStorageCollectionInjectKey, contextStorageCollectionItemInjectKey, contextStorageHandlersInjectKey, contextStorageLocalStorageHandlerInjectKey, contextStoragePrefixSegmentsInjectKey, contextStorageQueryHandlerInjectKey, contextStorageSessionStorageHandlerInjectKey, createCollectionManager, createEmptyZodObject, createLocalStorageHandler, createQueryHandler, createSessionStorageHandler, defaultHandlers, defineContextStorageHandler, deserializeParams as deserializeQueryParams, resolveHandlerInjectionKey, serializeParams as serializeQueryParams, transform, useContextStorage, useContextStorageActivator, useContextStorageCollection, useContextStorageProvider };
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-context-storage",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
5
|
-
"description": "Vue 3
|
|
4
|
+
"version": "0.1.43",
|
|
5
|
+
"description": "Vue 3 reactive state management — sync state with the URL query, localStorage, and sessionStorage",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"homepage": "https://github.com/lviobio/vue-context-storage#readme",
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
},
|
|
16
16
|
"exports": {
|
|
17
17
|
".": "./dist/index.js",
|
|
18
|
-
"./zod": "./dist/zod.js",
|
|
19
18
|
"./package.json": "./package.json"
|
|
20
19
|
},
|
|
21
20
|
"main": "./dist/index.js",
|
|
@@ -32,16 +31,23 @@
|
|
|
32
31
|
"dev": "tsdown --watch",
|
|
33
32
|
"check": "npm run ts:check && npm run lint:check && npm run format:check && npm run dependency-cruiser:check",
|
|
34
33
|
"ts:check": "vue-tsc --noEmit",
|
|
35
|
-
"format": "prettier --write src/ playground/src",
|
|
36
|
-
"format:check": "prettier --check src/ playground/src",
|
|
34
|
+
"format": "prettier --write src/ playground/src playground-e2e/src",
|
|
35
|
+
"format:check": "prettier --check src/ playground/src playground-e2e/src",
|
|
37
36
|
"lint": "eslint . --fix",
|
|
38
37
|
"lint:check": "eslint .",
|
|
39
38
|
"dependency-cruiser:check": "depcruise --config .dependency-cruiser.cjs src playground/src",
|
|
40
|
-
"play": "vite",
|
|
39
|
+
"play": "vite --port 5173",
|
|
40
|
+
"play:e2e": "vite --port 5174 --config vite.e2e.config.ts",
|
|
41
41
|
"build:playground": "vite build",
|
|
42
42
|
"preview:playground": "vite preview --outDir playground/dist",
|
|
43
|
-
"test": "
|
|
44
|
-
"
|
|
43
|
+
"test": "npm run test:unit && npm run test:e2e",
|
|
44
|
+
"test:unit": "vitest run",
|
|
45
|
+
"test:e2e": "playwright test",
|
|
46
|
+
"test:e2e:ui": "playwright test --ui",
|
|
47
|
+
"test:e2e:headed": "playwright test --headed",
|
|
48
|
+
"test:e2e:debug": "playwright test --debug",
|
|
49
|
+
"test:e2e:report": "playwright show-report",
|
|
50
|
+
"release": "npm run check && npm run test && bumpp && npm publish",
|
|
45
51
|
"prepublishOnly": "npm run check && npm run test && npm run build"
|
|
46
52
|
},
|
|
47
53
|
"peerDependencies": {
|
|
@@ -55,6 +61,7 @@
|
|
|
55
61
|
}
|
|
56
62
|
},
|
|
57
63
|
"devDependencies": {
|
|
64
|
+
"@playwright/test": "^1.60.0",
|
|
58
65
|
"@tailwindcss/vite": "^4.1.18",
|
|
59
66
|
"@types/lodash": "^4.17.21",
|
|
60
67
|
"@types/node": "^25.0.3",
|
package/dist/zod.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { ZodObject, ZodRawShape, z } from "zod";
|
|
2
|
-
|
|
3
|
-
//#region src/zod.d.ts
|
|
4
|
-
declare const SCHEMA_SYMBOL: unique symbol;
|
|
5
|
-
/**
|
|
6
|
-
* This type is "Maybe", but always has a schema attached to it.
|
|
7
|
-
* It's used to avoid any type errors with transformations.
|
|
8
|
-
*/
|
|
9
|
-
type MaybeWithSchema<T extends ZodRawShape> = z.infer<ZodObject<T>> & {
|
|
10
|
-
[SCHEMA_SYMBOL]?: ZodObject<T>;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Creates a Zod schema for arrays of objects serialized as indexed query parameters.
|
|
14
|
-
*
|
|
15
|
-
* URL query parameters serialize arrays of objects as indexed keys:
|
|
16
|
-
* `items[0][product]=Apple&items[0][quantity]=5&items[1][product]=Banana&items[1][quantity]=10`
|
|
17
|
-
*
|
|
18
|
-
* After deserialization, these become indexed objects:
|
|
19
|
-
* `{ '0': { product: 'Apple', quantity: '5' }, '1': { product: 'Banana', quantity: '10' } }`
|
|
20
|
-
*
|
|
21
|
-
* This helper wraps `z.record()` + `.transform()` to convert them back to a typed array,
|
|
22
|
-
* so you don't have to write the boilerplate yourself.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* import { z } from 'zod'
|
|
27
|
-
* import { zObjectArray } from 'vue-context-storage/zod'
|
|
28
|
-
*
|
|
29
|
-
* const ItemSchema = z.object({
|
|
30
|
-
* product: z.string().default(''),
|
|
31
|
-
* quantity: z.coerce.number().default(0),
|
|
32
|
-
* })
|
|
33
|
-
*
|
|
34
|
-
* const DataSchema = z.object({
|
|
35
|
-
* title: z.string().default(''),
|
|
36
|
-
* items: zObjectArray(ItemSchema),
|
|
37
|
-
* })
|
|
38
|
-
*
|
|
39
|
-
* useContextStorage('query', data, { schema: DataSchema })
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
declare function zObjectArray<T extends z.ZodTypeAny>(itemSchema: T): z.ZodPipe<z.ZodDefault<z.ZodRecord<z.ZodString, T>>, z.ZodTransform<z.core.output<T>[], Record<string, z.core.output<T>>>>;
|
|
43
|
-
/**
|
|
44
|
-
* Creates an object with empty values based on a Zod schema.
|
|
45
|
-
* Useful for initializing forms.
|
|
46
|
-
*
|
|
47
|
-
* When `withSchema` is `true`, attaches the schema to the result via `SCHEMA_SYMBOL`
|
|
48
|
-
* (wrapped with `markRaw` to prevent Vue reactivity on the schema instance).
|
|
49
|
-
*/
|
|
50
|
-
declare function createEmptyObject<T extends ZodRawShape>(schema: ZodObject<T>, options: {
|
|
51
|
-
useDefaults?: boolean;
|
|
52
|
-
withSchema: true;
|
|
53
|
-
}): MaybeWithSchema<T>;
|
|
54
|
-
declare function createEmptyObject<T extends ZodRawShape>(schema: ZodObject<T>, options?: {
|
|
55
|
-
useDefaults?: boolean;
|
|
56
|
-
withSchema?: false;
|
|
57
|
-
}): z.infer<ZodObject<T>>;
|
|
58
|
-
//#endregion
|
|
59
|
-
export { MaybeWithSchema, SCHEMA_SYMBOL, createEmptyObject, zObjectArray };
|
package/dist/zod.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { markRaw } from "vue";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
//#region src/zod.ts
|
|
5
|
-
const SCHEMA_SYMBOL = /* @__PURE__ */ Symbol("schema");
|
|
6
|
-
function zObjectArray(itemSchema) {
|
|
7
|
-
return z.record(z.string(), itemSchema).default({}).transform((record) => Object.entries(record).sort(([a], [b]) => Number(a) - Number(b)).map(([, v]) => v));
|
|
8
|
-
}
|
|
9
|
-
function createEmptyObject(schema, options) {
|
|
10
|
-
const shape = schema.shape;
|
|
11
|
-
const result = {};
|
|
12
|
-
const { useDefaults = true } = options || {};
|
|
13
|
-
for (const key in shape) {
|
|
14
|
-
const field = shape[key];
|
|
15
|
-
if (useDefaults) {
|
|
16
|
-
let hasDefault = false;
|
|
17
|
-
let currentType = field;
|
|
18
|
-
while (currentType instanceof z.ZodOptional || currentType instanceof z.ZodNullable || currentType instanceof z.ZodDefault) {
|
|
19
|
-
if (currentType instanceof z.ZodDefault) {
|
|
20
|
-
hasDefault = true;
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
currentType = currentType.unwrap();
|
|
24
|
-
}
|
|
25
|
-
if (hasDefault) {
|
|
26
|
-
result[key] = field.parse(void 0);
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
let isNullable = false;
|
|
31
|
-
let isOptional = false;
|
|
32
|
-
let checkType = field;
|
|
33
|
-
while (checkType instanceof z.ZodOptional || checkType instanceof z.ZodNullable || checkType instanceof z.ZodDefault) {
|
|
34
|
-
if (checkType instanceof z.ZodNullable) isNullable = true;
|
|
35
|
-
if (checkType instanceof z.ZodOptional) isOptional = true;
|
|
36
|
-
checkType = checkType.unwrap();
|
|
37
|
-
}
|
|
38
|
-
if (isNullable) {
|
|
39
|
-
result[key] = null;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (isOptional) {
|
|
43
|
-
result[key] = void 0;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
let baseType = field;
|
|
47
|
-
while (baseType instanceof z.ZodOptional || baseType instanceof z.ZodNullable || baseType instanceof z.ZodDefault) baseType = baseType.unwrap();
|
|
48
|
-
if (baseType instanceof z.ZodString) result[key] = "";
|
|
49
|
-
else if (baseType instanceof z.ZodNumber) {
|
|
50
|
-
let defaultValue = baseType.minValue !== null && baseType.minValue !== void 0 && isFinite(baseType.minValue) ? baseType.minValue : 0;
|
|
51
|
-
if (!baseType.safeParse(defaultValue).success) defaultValue += 1;
|
|
52
|
-
result[key] = defaultValue;
|
|
53
|
-
} else if (baseType instanceof z.ZodBoolean) result[key] = false;
|
|
54
|
-
else if (baseType instanceof z.ZodArray) result[key] = [];
|
|
55
|
-
else if (baseType instanceof z.ZodObject) result[key] = createEmptyObject(baseType, {
|
|
56
|
-
useDefaults: options?.useDefaults,
|
|
57
|
-
withSchema: options?.withSchema
|
|
58
|
-
});
|
|
59
|
-
else if (baseType instanceof z.ZodDate) result[key] = null;
|
|
60
|
-
else if (field instanceof z.ZodOptional || field instanceof z.ZodNullable) result[key] = void 0;
|
|
61
|
-
else result[key] = void 0;
|
|
62
|
-
}
|
|
63
|
-
if (options?.withSchema) result[SCHEMA_SYMBOL] = markRaw(schema);
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//#endregion
|
|
68
|
-
export { SCHEMA_SYMBOL, createEmptyObject, zObjectArray };
|