vue-context-storage 0.1.39 → 0.1.41
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 +23 -33
- package/dist/index.js +36 -2
- package/dist/zod.d.ts +1 -81
- package/dist/zod.js +1 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -712,56 +712,46 @@ const DataSchema = z.object({
|
|
|
712
712
|
})
|
|
713
713
|
```
|
|
714
714
|
|
|
715
|
-
###
|
|
715
|
+
### Automatic Type Coercion
|
|
716
716
|
|
|
717
|
-
|
|
717
|
+
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:
|
|
718
718
|
|
|
719
|
-
|
|
720
|
-
import { z } from 'zod'
|
|
721
|
-
import { zUrlBoolean } from 'vue-context-storage/zod'
|
|
722
|
-
|
|
723
|
-
const Schema = z.object({
|
|
724
|
-
active: zUrlBoolean(), // defaults to false
|
|
725
|
-
enabled: zUrlBoolean(true), // defaults to true
|
|
726
|
-
})
|
|
727
|
-
```
|
|
719
|
+
#### Array Coercion
|
|
728
720
|
|
|
729
|
-
|
|
721
|
+
A URL with multiple values (`?ids=1&ids=2`) produces an array `['1', '2']`, but a single value (`?ids=1`) produces just `'1'`. Without correction, `z.string().array()` would reject the single-value case with `"expected array, received string"`.
|
|
730
722
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
This helper normalizes the input by accepting both a single value and an array, coercing each element to a number.
|
|
723
|
+
The library introspects the Zod schema before validation and wraps non-array values into single-element arrays wherever the schema expects `.array()`. This works recursively for nested objects. **No special helpers are needed** — plain `.array()` schemas work out of the box:
|
|
734
724
|
|
|
735
725
|
```typescript
|
|
736
|
-
import { z } from 'zod'
|
|
737
|
-
import { zNumberArray } from 'vue-context-storage/zod'
|
|
738
|
-
|
|
739
726
|
const Schema = z.object({
|
|
740
|
-
|
|
727
|
+
tags: z.string().array().default([]),
|
|
728
|
+
ids: z.coerce.number().array().default([]),
|
|
729
|
+
statuses: z.enum(['active', 'inactive']).array().default([]),
|
|
730
|
+
filters: z
|
|
731
|
+
.object({
|
|
732
|
+
categories: z.string().array().default([]),
|
|
733
|
+
})
|
|
734
|
+
.default({ categories: [] }),
|
|
741
735
|
})
|
|
742
736
|
|
|
743
|
-
// All of these work:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
737
|
+
// All of these work automatically:
|
|
738
|
+
// ?tags=vue → { tags: ['vue'], ... }
|
|
739
|
+
// ?tags=vue&tags=ts → { tags: ['vue', 'ts'], ... }
|
|
740
|
+
// (no tags param) → { tags: [], ... }
|
|
747
741
|
```
|
|
748
742
|
|
|
749
|
-
|
|
743
|
+
#### Boolean Coercion
|
|
750
744
|
|
|
751
|
-
|
|
745
|
+
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:
|
|
752
746
|
|
|
753
747
|
```typescript
|
|
754
|
-
import { z } from 'zod'
|
|
755
|
-
import { zStringArray } from 'vue-context-storage/zod'
|
|
756
|
-
|
|
757
748
|
const Schema = z.object({
|
|
758
|
-
|
|
749
|
+
active: z.boolean().default(false),
|
|
750
|
+
enabled: z.boolean().default(true),
|
|
759
751
|
})
|
|
760
752
|
|
|
761
|
-
//
|
|
762
|
-
|
|
763
|
-
Schema.parse({ tags: 'vue' }) // → { tags: ['vue'] }
|
|
764
|
-
Schema.parse({}) // → { tags: [] }
|
|
753
|
+
// ?active=1 → { active: true, enabled: true }
|
|
754
|
+
// ?active=0 → { active: false, enabled: true }
|
|
765
755
|
```
|
|
766
756
|
|
|
767
757
|
### `createSchemaObject(schema, options?)`
|
package/dist/index.js
CHANGED
|
@@ -118,12 +118,46 @@ function syncReactive(target, source) {
|
|
|
118
118
|
for (const key of Object.keys(target)) if (!(key in source)) delete target[key];
|
|
119
119
|
Object.assign(target, source);
|
|
120
120
|
}
|
|
121
|
+
function zodDefType(field) {
|
|
122
|
+
return field?._zod?.def?.type;
|
|
123
|
+
}
|
|
124
|
+
function unwrapZodField(field) {
|
|
125
|
+
let t = field;
|
|
126
|
+
while (true) {
|
|
127
|
+
const type = zodDefType(t);
|
|
128
|
+
if (type === "default" || type === "optional" || type === "nullable") t = t.unwrap();
|
|
129
|
+
else break;
|
|
130
|
+
}
|
|
131
|
+
return t;
|
|
132
|
+
}
|
|
133
|
+
function coerceDataForSchema(data, schema) {
|
|
134
|
+
const shape = schema?.shape;
|
|
135
|
+
if (!shape || typeof shape !== "object") return data;
|
|
136
|
+
const result = { ...data };
|
|
137
|
+
for (const key of Object.keys(shape)) {
|
|
138
|
+
if (!(key in result)) continue;
|
|
139
|
+
const base = unwrapZodField(shape[key]);
|
|
140
|
+
const baseType = zodDefType(base);
|
|
141
|
+
if (baseType === "boolean") {
|
|
142
|
+
const value = result[key];
|
|
143
|
+
if (value === "1") result[key] = true;
|
|
144
|
+
else if (value === "0") result[key] = false;
|
|
145
|
+
} else if (baseType === "array") {
|
|
146
|
+
const value = result[key];
|
|
147
|
+
if (value !== void 0 && value !== null && !Array.isArray(value)) result[key] = [value];
|
|
148
|
+
} else if (baseType === "object" && base.shape) {
|
|
149
|
+
const nested = result[key];
|
|
150
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) result[key] = coerceDataForSchema(nested, base);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
121
155
|
function applyTransform(input) {
|
|
122
156
|
const warnings = [];
|
|
123
157
|
let data = input.state;
|
|
124
158
|
if (input.schema) {
|
|
125
|
-
const
|
|
126
|
-
const result = input.schema.safeParse(
|
|
159
|
+
const coerced = coerceDataForSchema(merge({}, input.initialData, data), input.schema);
|
|
160
|
+
const result = input.schema.safeParse(coerced);
|
|
127
161
|
if (result.success) data = result.data;
|
|
128
162
|
else {
|
|
129
163
|
warnings.push({
|
package/dist/zod.d.ts
CHANGED
|
@@ -40,86 +40,6 @@ type MaybeWithSchema<T extends ZodRawShape> = z.infer<ZodObject<T>> & {
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
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 a Zod schema for booleans serialized as URL query parameters.
|
|
45
|
-
*
|
|
46
|
-
* URL query parameters serialize booleans as `'1'`/`'0'` strings.
|
|
47
|
-
* `z.coerce.boolean()` cannot be used because `Boolean('0')` is `true` in JavaScript.
|
|
48
|
-
*
|
|
49
|
-
* This helper correctly handles `'1'`, `'true'`, `'0'`, `'false'`, and native booleans.
|
|
50
|
-
*
|
|
51
|
-
* @param defaultValue - The default value when the field is missing (defaults to `false`)
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* import { z } from 'zod'
|
|
56
|
-
* import { zUrlBoolean } from 'vue-context-storage/zod'
|
|
57
|
-
*
|
|
58
|
-
* const Schema = z.object({
|
|
59
|
-
* active: zUrlBoolean(), // defaults to false
|
|
60
|
-
* enabled: zUrlBoolean(true), // defaults to true
|
|
61
|
-
* })
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
declare function zUrlBoolean(defaultValue?: boolean): z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>, z.ZodTransform<boolean, string | boolean>>>;
|
|
65
|
-
/**
|
|
66
|
-
* Creates a Zod schema for arrays of numbers serialized as URL query parameters.
|
|
67
|
-
*
|
|
68
|
-
* URL query parameters serialize arrays as repeated keys:
|
|
69
|
-
* `?users_ids=1&users_ids=2` → `['1', '2']` (array of strings)
|
|
70
|
-
*
|
|
71
|
-
* But when only one value is present:
|
|
72
|
-
* `?users_ids=1` → `'1'` (just a string, not an array)
|
|
73
|
-
*
|
|
74
|
-
* `z.coerce.number().array()` would fail with "expected array, received string"
|
|
75
|
-
* for the single-value case. This helper normalizes the input by wrapping
|
|
76
|
-
* a single value into an array before coercing each element to a number.
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* ```ts
|
|
80
|
-
* import { z } from 'zod'
|
|
81
|
-
* import { zNumberArray } from 'vue-context-storage/zod'
|
|
82
|
-
*
|
|
83
|
-
* const Schema = z.object({
|
|
84
|
-
* users_ids: zNumberArray(),
|
|
85
|
-
* })
|
|
86
|
-
*
|
|
87
|
-
* // All of these work:
|
|
88
|
-
* Schema.parse({ users_ids: ['1', '2'] }) // → { users_ids: [1, 2] }
|
|
89
|
-
* Schema.parse({ users_ids: '1' }) // → { users_ids: [1] }
|
|
90
|
-
* Schema.parse({}) // → { users_ids: [] }
|
|
91
|
-
* ```
|
|
92
|
-
*/
|
|
93
|
-
declare function zNumberArray(): z.ZodDefault<z.ZodUnion<readonly [z.ZodArray<z.ZodCoercedNumber<unknown>>, z.ZodPipe<z.ZodCoercedNumber<unknown>, z.ZodTransform<number[], number>>]>>;
|
|
94
|
-
/**
|
|
95
|
-
* Creates a Zod schema for arrays of strings serialized as URL query parameters.
|
|
96
|
-
*
|
|
97
|
-
* URL query parameters serialize arrays as repeated keys:
|
|
98
|
-
* `?tags=vue&tags=react` → `['vue', 'react']` (array of strings)
|
|
99
|
-
*
|
|
100
|
-
* But when only one value is present:
|
|
101
|
-
* `?tags=vue` → `'vue'` (just a string, not an array)
|
|
102
|
-
*
|
|
103
|
-
* `z.string().array()` would fail with "expected array, received string"
|
|
104
|
-
* for the single-value case. This helper normalizes the input by wrapping
|
|
105
|
-
* a single value into an array.
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* ```ts
|
|
109
|
-
* import { z } from 'zod'
|
|
110
|
-
* import { zStringArray } from 'vue-context-storage/zod'
|
|
111
|
-
*
|
|
112
|
-
* const Schema = z.object({
|
|
113
|
-
* tags: zStringArray(),
|
|
114
|
-
* })
|
|
115
|
-
*
|
|
116
|
-
* // All of these work:
|
|
117
|
-
* Schema.parse({ tags: ['vue', 'react'] }) // → { tags: ['vue', 'react'] }
|
|
118
|
-
* Schema.parse({ tags: 'vue' }) // → { tags: ['vue'] }
|
|
119
|
-
* Schema.parse({}) // → { tags: [] }
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
declare function zStringArray(): z.ZodDefault<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>]>>;
|
|
123
43
|
/**
|
|
124
44
|
* Creates an object with empty values based on a Zod schema.
|
|
125
45
|
* Useful for initializing forms.
|
|
@@ -136,4 +56,4 @@ declare function createEmptyObject<T extends ZodRawShape>(schema: ZodObject<T>,
|
|
|
136
56
|
withSchema?: false;
|
|
137
57
|
}): z.infer<ZodObject<T>>;
|
|
138
58
|
//#endregion
|
|
139
|
-
export { MaybeWithSchema, SCHEMA_SYMBOL, createEmptyObject,
|
|
59
|
+
export { MaybeWithSchema, SCHEMA_SYMBOL, createEmptyObject, zObjectArray };
|
package/dist/zod.js
CHANGED
|
@@ -6,18 +6,6 @@ const SCHEMA_SYMBOL = /* @__PURE__ */ Symbol("schema");
|
|
|
6
6
|
function zObjectArray(itemSchema) {
|
|
7
7
|
return z.record(z.string(), itemSchema).default({}).transform((record) => Object.entries(record).sort(([a], [b]) => Number(a) - Number(b)).map(([, v]) => v));
|
|
8
8
|
}
|
|
9
|
-
function zUrlBoolean(defaultValue = false) {
|
|
10
|
-
return z.union([z.boolean(), z.string()]).transform((val) => {
|
|
11
|
-
if (typeof val === "boolean") return val;
|
|
12
|
-
return val === "1" || val === "true";
|
|
13
|
-
}).default(defaultValue);
|
|
14
|
-
}
|
|
15
|
-
function zNumberArray() {
|
|
16
|
-
return z.union([z.coerce.number().array(), z.coerce.number().transform((v) => [v])]).default([]);
|
|
17
|
-
}
|
|
18
|
-
function zStringArray() {
|
|
19
|
-
return z.union([z.string().array(), z.string().transform((v) => [v])]).default([]);
|
|
20
|
-
}
|
|
21
9
|
function createEmptyObject(schema, options) {
|
|
22
10
|
const shape = schema.shape;
|
|
23
11
|
const result = {};
|
|
@@ -77,4 +65,4 @@ function createEmptyObject(schema, options) {
|
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
//#endregion
|
|
80
|
-
export { SCHEMA_SYMBOL, createEmptyObject,
|
|
68
|
+
export { SCHEMA_SYMBOL, createEmptyObject, zObjectArray };
|