vue-context-storage 0.1.43 → 0.1.44
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 +31 -11
- package/dist/index.d.ts +6 -4
- package/dist/index.js +22 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -154,7 +154,7 @@ useContextStorage('sessionStorage', filters, {
|
|
|
154
154
|
useContextStorage('query', filters, {
|
|
155
155
|
key: 'filters',
|
|
156
156
|
schema: z.object({
|
|
157
|
-
page: z.
|
|
157
|
+
page: z.number().default(1),
|
|
158
158
|
search: z.string().default(''),
|
|
159
159
|
status: z.string().default('active'),
|
|
160
160
|
}),
|
|
@@ -267,7 +267,7 @@ import { useContextStorage } from 'vue-context-storage'
|
|
|
267
267
|
// Define schema with automatic coercion
|
|
268
268
|
const FiltersSchema = z.object({
|
|
269
269
|
search: z.string().default(''),
|
|
270
|
-
page: z.
|
|
270
|
+
page: z.number().int().positive().default(1),
|
|
271
271
|
status: z.enum(['active', 'inactive']).default('active'),
|
|
272
272
|
})
|
|
273
273
|
|
|
@@ -329,7 +329,7 @@ import { z } from 'zod'
|
|
|
329
329
|
|
|
330
330
|
const ItemSchema = z.object({
|
|
331
331
|
product: z.string().default(''),
|
|
332
|
-
quantity: z.
|
|
332
|
+
quantity: z.number().default(0),
|
|
333
333
|
})
|
|
334
334
|
|
|
335
335
|
const DataSchema = z.object({
|
|
@@ -379,7 +379,7 @@ When using Zod schemas, you can also specify `additionalDefaultData` per-field v
|
|
|
379
379
|
|
|
380
380
|
```typescript
|
|
381
381
|
const Schema = z.object({
|
|
382
|
-
page: z.
|
|
382
|
+
page: z.number().default(1).meta({ additionalDefaultData: 3 }),
|
|
383
383
|
search: z.string().default(''),
|
|
384
384
|
})
|
|
385
385
|
|
|
@@ -405,7 +405,7 @@ Nested objects work the same way. Following the documented best practice of decl
|
|
|
405
405
|
const Schema = z.object({
|
|
406
406
|
filters: z
|
|
407
407
|
.object({
|
|
408
|
-
page: z.
|
|
408
|
+
page: z.number(),
|
|
409
409
|
sort: z.string(),
|
|
410
410
|
})
|
|
411
411
|
.default({ page: 1, sort: 'asc' }),
|
|
@@ -759,7 +759,7 @@ npm install zod
|
|
|
759
759
|
|
|
760
760
|
### Automatic Type Coercion
|
|
761
761
|
|
|
762
|
-
When a `schema` is provided, the library automatically coerces URL query parameter values to match the expected Zod types before validation. This handles
|
|
762
|
+
When a `schema` is provided, the library automatically coerces URL query parameter values to match the expected Zod types before validation. This handles the common URL serialization quirks below.
|
|
763
763
|
|
|
764
764
|
#### Array Coercion
|
|
765
765
|
|
|
@@ -770,7 +770,7 @@ The library introspects the Zod schema before validation and wraps non-array val
|
|
|
770
770
|
```typescript
|
|
771
771
|
const Schema = z.object({
|
|
772
772
|
tags: z.string().array().default([]),
|
|
773
|
-
ids: z.
|
|
773
|
+
ids: z.number().array().default([]),
|
|
774
774
|
statuses: z.enum(['active', 'inactive']).array().default([]),
|
|
775
775
|
filters: z
|
|
776
776
|
.object({
|
|
@@ -790,7 +790,7 @@ Arrays of objects are also handled automatically. They deserialize from indexed
|
|
|
790
790
|
```typescript
|
|
791
791
|
const ItemSchema = z.object({
|
|
792
792
|
product: z.string().default(''),
|
|
793
|
-
quantity: z.
|
|
793
|
+
quantity: z.number().default(0),
|
|
794
794
|
})
|
|
795
795
|
|
|
796
796
|
const Schema = z.object({
|
|
@@ -803,7 +803,7 @@ const Schema = z.object({
|
|
|
803
803
|
|
|
804
804
|
#### Boolean Coercion
|
|
805
805
|
|
|
806
|
-
The query handler serializes booleans as `'1'`/`'0'` strings in URL parameters (e.g. `?active=1`). Standard `z.
|
|
806
|
+
The query handler serializes booleans as `'1'`/`'0'` strings in URL parameters (e.g. `?active=1`). Standard `z.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:
|
|
807
807
|
|
|
808
808
|
```typescript
|
|
809
809
|
const Schema = z.object({
|
|
@@ -815,6 +815,26 @@ const Schema = z.object({
|
|
|
815
815
|
// ?active=0 → { active: false, enabled: true }
|
|
816
816
|
```
|
|
817
817
|
|
|
818
|
+
#### Number Coercion
|
|
819
|
+
|
|
820
|
+
URL query parameters are always strings (`?page=5` → `'5'`), so plain `z.number()` would reject them with `"expected number, received string"`. The library automatically converts numeric strings to numbers wherever the schema expects a number — including inside nested objects and `z.array(z.number())`. **`z` is not required** — plain `z.number()` works out of the box:
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
const Schema = z.object({
|
|
824
|
+
page: z.number().default(1),
|
|
825
|
+
score: z.number().nullable(),
|
|
826
|
+
ids: z.array(z.number()).default([]),
|
|
827
|
+
})
|
|
828
|
+
|
|
829
|
+
// ?page=5 → { page: 5, ... }
|
|
830
|
+
// ?score=42 → { score: 42, ... }
|
|
831
|
+
// ?ids=1&ids=2 → { ids: [1, 2], ... }
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
Coercion is conservative: a non-numeric string (`?page=abc`) or an empty value is **not** forced to a number (`Number('')` would be `0`). Instead the schema validation fails and the field falls back to its initial data, so missing/garbage input never silently becomes `0`. `null` is preserved for `.nullable()` fields.
|
|
835
|
+
|
|
836
|
+
> `z.number()` still works and remains useful when you want Zod's own coercion semantics (e.g. coercing booleans or accepting empty strings as `0`).
|
|
837
|
+
|
|
818
838
|
### `createEmptyZodObject(schema, options?)`
|
|
819
839
|
|
|
820
840
|
Creates a plain object with empty/default values based on a Zod schema. Useful for initializing reactive data from a schema definition.
|
|
@@ -825,7 +845,7 @@ import { createEmptyZodObject } from 'vue-context-storage'
|
|
|
825
845
|
|
|
826
846
|
const FiltersSchema = z.object({
|
|
827
847
|
search: z.string().default(''),
|
|
828
|
-
page: z.
|
|
848
|
+
page: z.number().default(1),
|
|
829
849
|
active: z.boolean().default(false),
|
|
830
850
|
score: z.number().nullable(),
|
|
831
851
|
})
|
|
@@ -876,7 +896,7 @@ When using Zod schemas, TypeScript will automatically infer types:
|
|
|
876
896
|
```typescript
|
|
877
897
|
const FiltersSchema = z.object({
|
|
878
898
|
search: z.string().default(''),
|
|
879
|
-
page: z.
|
|
899
|
+
page: z.number().default(1),
|
|
880
900
|
})
|
|
881
901
|
|
|
882
902
|
type Filters = z.infer<typeof FiltersSchema>
|
package/dist/index.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ interface RegisterBaseOptions<T> {
|
|
|
40
40
|
*
|
|
41
41
|
* const FiltersSchema = z.object({
|
|
42
42
|
* search: z.string().default(''),
|
|
43
|
-
* page: z.
|
|
43
|
+
* page: z.number().int().positive().default(1),
|
|
44
44
|
* status: z.enum(['active', 'inactive']).default('active'),
|
|
45
45
|
* })
|
|
46
46
|
*
|
|
@@ -366,10 +366,12 @@ interface RegisterQueryHandlerOptions<T> extends RegisterBaseOptions<T>, Registe
|
|
|
366
366
|
//#region src/handlers/web-storage-base/types.d.ts
|
|
367
367
|
interface WebStorageHandlerBaseOptions {
|
|
368
368
|
/**
|
|
369
|
-
* Default: true
|
|
369
|
+
* Default: true
|
|
370
370
|
*
|
|
371
|
-
* If enabled - storage events will be listened to
|
|
372
|
-
*
|
|
371
|
+
* If enabled - storage events will be listened to so external changes are synced
|
|
372
|
+
* back into the reactive data. For localStorage this covers other tabs; for
|
|
373
|
+
* sessionStorage it covers other contexts of the same session (iframes,
|
|
374
|
+
* `window.open` windows) and manual edits via DevTools.
|
|
373
375
|
*/
|
|
374
376
|
listenToStorageEvents?: boolean;
|
|
375
377
|
}
|
package/dist/index.js
CHANGED
|
@@ -202,6 +202,21 @@ function extractDefaultsFromSchema(schema) {
|
|
|
202
202
|
}
|
|
203
203
|
return result;
|
|
204
204
|
}
|
|
205
|
+
function coerceScalar(value, baseType) {
|
|
206
|
+
if (baseType === "boolean") {
|
|
207
|
+
if (value === "1") return true;
|
|
208
|
+
if (value === "0") return false;
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
if (baseType === "number") {
|
|
212
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
213
|
+
const n = Number(value);
|
|
214
|
+
if (!Number.isNaN(n)) return n;
|
|
215
|
+
}
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
205
220
|
function coerceDataForSchema(data, schema) {
|
|
206
221
|
const shape = schema?.shape;
|
|
207
222
|
if (!shape || typeof shape !== "object") return data;
|
|
@@ -210,11 +225,8 @@ function coerceDataForSchema(data, schema) {
|
|
|
210
225
|
if (!(key in result)) continue;
|
|
211
226
|
const base = unwrapZodField(shape[key]);
|
|
212
227
|
const baseType = zodDefType(base);
|
|
213
|
-
if (baseType === "boolean")
|
|
214
|
-
|
|
215
|
-
if (value === "1") result[key] = true;
|
|
216
|
-
else if (value === "0") result[key] = false;
|
|
217
|
-
} else if (baseType === "array") {
|
|
228
|
+
if (baseType === "boolean" || baseType === "number") result[key] = coerceScalar(result[key], baseType);
|
|
229
|
+
else if (baseType === "array") {
|
|
218
230
|
const value = result[key];
|
|
219
231
|
if (value !== void 0 && value !== null) {
|
|
220
232
|
let arr;
|
|
@@ -222,7 +234,9 @@ function coerceDataForSchema(data, schema) {
|
|
|
222
234
|
else if (typeof value === "object") arr = Object.entries(value).sort(([a], [b]) => Number(a) - Number(b)).map(([, v]) => v);
|
|
223
235
|
else arr = [value];
|
|
224
236
|
const elementBase = unwrapZodField(base.element ?? base._zod?.def?.element);
|
|
225
|
-
|
|
237
|
+
const elementType = zodDefType(elementBase);
|
|
238
|
+
if (elementType === "object" && elementBase.shape) arr = arr.map((v) => v && typeof v === "object" && !Array.isArray(v) ? coerceDataForSchema(v, elementBase) : v);
|
|
239
|
+
else if (elementType === "number" || elementType === "boolean") arr = arr.map((v) => coerceScalar(v, elementType));
|
|
226
240
|
result[key] = arr;
|
|
227
241
|
}
|
|
228
242
|
} else if (baseType === "object" && base.shape) {
|
|
@@ -635,6 +649,7 @@ function createWebStorageHandlerInstance(config) {
|
|
|
635
649
|
}
|
|
636
650
|
function handleStorageEvent(event) {
|
|
637
651
|
if (!enabled) return;
|
|
652
|
+
if (event.storageArea && event.storageArea !== config.storage) return;
|
|
638
653
|
registered.forEach((item) => {
|
|
639
654
|
if (event.key === item.options.key) syncStorageToRegisteredItem(item);
|
|
640
655
|
});
|
|
@@ -757,7 +772,7 @@ function createSessionStorageHandler(customOptions) {
|
|
|
757
772
|
injectionKey: contextStorageSessionStorageHandler,
|
|
758
773
|
handlerName: "sessionStorage",
|
|
759
774
|
options: {
|
|
760
|
-
listenToStorageEvents:
|
|
775
|
+
listenToStorageEvents: true,
|
|
761
776
|
...customOptions
|
|
762
777
|
}
|
|
763
778
|
});
|
package/package.json
CHANGED