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 CHANGED
@@ -712,56 +712,46 @@ const DataSchema = z.object({
712
712
  })
713
713
  ```
714
714
 
715
- ### `zUrlBoolean(defaultValue?)`
715
+ ### Automatic Type Coercion
716
716
 
717
- Creates a Zod schema for booleans serialized as URL query parameters. Standard `z.coerce.boolean()` cannot be used because `Boolean('0')` is `true` in JavaScript. This helper correctly handles `'1'`, `'true'`, `'0'`, `'false'`, and native booleans.
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
- ```typescript
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
- ### `zNumberArray()`
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
- Creates a Zod schema for arrays of numbers serialized as URL query parameters. Handles the common issue where a single query value (`?ids=1`) is deserialized as a string `'1'` instead of an array `['1']`, which would cause `z.coerce.number().array()` to fail with `"expected array, received string"`.
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
- users_ids: zNumberArray(),
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
- Schema.parse({ users_ids: ['1', '2'] }) // → { users_ids: [1, 2] }
745
- Schema.parse({ users_ids: '1' }) // → { users_ids: [1] }
746
- Schema.parse({}) // → { users_ids: [] }
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
- ### `zStringArray()`
743
+ #### Boolean Coercion
750
744
 
751
- Creates a Zod schema for arrays of strings serialized as URL query parameters. Same problem as `zNumberArray()` a single query value (`?tags=vue`) is deserialized as a string `'vue'` instead of an array `['vue']`, which would cause `z.string().array()` to fail.
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
- tags: zStringArray(),
749
+ active: z.boolean().default(false),
750
+ enabled: z.boolean().default(true),
759
751
  })
760
752
 
761
- // All of these work:
762
- Schema.parse({ tags: ['vue', 'react'] }) // → { tags: ['vue', 'react'] }
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 merged = merge({}, input.initialData, data);
126
- const result = input.schema.safeParse(merged);
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, zNumberArray, zObjectArray, zStringArray, zUrlBoolean };
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, zNumberArray, zObjectArray, zStringArray, zUrlBoolean };
68
+ export { SCHEMA_SYMBOL, createEmptyObject, zObjectArray };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vue-context-storage",
3
3
  "type": "module",
4
- "version": "0.1.39",
4
+ "version": "0.1.41",
5
5
  "description": "Vue 3 context storage system with URL query synchronization support",
6
6
  "author": "",
7
7
  "license": "MIT",