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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # vue-context-storage
2
2
 
3
- Vue 3 context storage system with URL query, localStorage, and sessionStorage synchronization support.
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
  [![npm downloads](https://img.shields.io/npm/dm/vue-context-storage.svg)](https://www.npmjs.com/package/vue-context-storage)
6
6
  [![TypeScript](https://badgen.net/badge/icon/TypeScript?icon=typescript&label)](https://www.typescriptlang.org/)
@@ -13,15 +13,29 @@ Vue 3 context storage system with URL query, localStorage, and sessionStorage sy
13
13
  [![codecov](https://codecov.io/gh/lviobio/vue-context-storage/branch/main/graph/badge.svg)](https://codecov.io/gh/lviobio/vue-context-storage)
14
14
  [![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](https://lviobio.github.io/vue-context-storage/)
15
15
 
16
- A powerful state management solution for Vue 3 applications that provides:
16
+ Key features:
17
17
 
18
- - **Context-based storage** using Vue's provide/inject API
19
- - **Automatic URL query synchronization** for preserving state across page reloads
20
- - **localStorage & sessionStorage handlers** for persistent and session-scoped state
21
- - **Multiple storage contexts** with activation management
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. Use `transform.asObjectArray` or the Zod helper `zObjectArray` to convert them back.
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: zObjectArray(ItemSchema),
337
+ items: z.array(ItemSchema),
323
338
  })
324
339
 
325
340
  useContextStorage('query', data, { schema: DataSchema })
326
341
  ```
327
342
 
328
- See [Zod Helpers](#zod-helpers-vue-context-storagezod) for more details.
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 (`vue-context-storage/zod`)
752
+ ## Zod Helpers
689
753
 
690
- The library provides a separate entry point with Zod-specific helpers. Since `zod` is an optional peer dependency, these helpers are isolated in `vue-context-storage/zod` to avoid importing Zod in the main bundle.
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
- ### `createSchemaObject(schema, options?)`
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 { createSchemaObject } from 'vue-context-storage/zod'
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(createSchemaObject(FiltersSchema))
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 { createSchemaObject, SCHEMA_SYMBOL } from 'vue-context-storage/zod'
843
+ import { createEmptyZodObject, SCHEMA_SYMBOL } from 'vue-context-storage'
783
844
 
784
- const data = createSchemaObject(FiltersSchema, { withSchema: true })
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 `createSchemaObject` |
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 vue9 from "vue";
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: vue9.DefineComponent<{}, () => vue9.VNode<vue9.RendererNode, vue9.RendererElement, {
8
+ declare const __VLS_export$4: vue0.DefineComponent<{}, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
8
9
  [key: string]: any;
9
- }>, {}, {}, {}, vue9.ComponentOptionsMixin, vue9.ComponentOptionsMixin, {}, string, vue9.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, vue9.ComponentProvideOptions, true, {}, any>;
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: vue9.DefineComponent<vue9.ExtractPropTypes<{
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
- }>, () => vue9.VNode<vue9.RendererNode, vue9.RendererElement, {
85
+ }>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
85
86
  [key: string]: any;
86
- }>[] | undefined, {}, {}, {}, vue9.ComponentOptionsMixin, vue9.ComponentOptionsMixin, {}, string, vue9.PublicProps, Readonly<vue9.ExtractPropTypes<{
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, vue9.ComponentProvideOptions, true, {}, any>;
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: vue9.DefineComponent<vue9.ExtractPropTypes<{
110
+ declare const __VLS_export$2: vue0.DefineComponent<vue0.ExtractPropTypes<{
110
111
  itemKey: {
111
112
  type: StringConstructor;
112
113
  required: true;
113
114
  };
114
- }>, () => vue9.VNode<vue9.RendererNode, vue9.RendererElement, {
115
+ }>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
115
116
  [key: string]: any;
116
- }>[] | undefined, {}, {}, {}, vue9.ComponentOptionsMixin, vue9.ComponentOptionsMixin, {}, string, vue9.PublicProps, Readonly<vue9.ExtractPropTypes<{
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, vue9.ComponentProvideOptions, true, {}, any>;
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: vue9.DefineComponent<vue9.ExtractPropTypes<{
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
- }>, () => vue9.VNode<vue9.RendererNode, vue9.RendererElement, {
142
+ }>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
142
143
  [key: string]: any;
143
- }>[] | undefined, {}, {}, {}, vue9.ComponentOptionsMixin, vue9.ComponentOptionsMixin, {}, string, vue9.PublicProps, Readonly<vue9.ExtractPropTypes<{
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, vue9.ComponentProvideOptions, true, {}, any>;
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: vue9.DefineComponent<vue9.ExtractPropTypes<{
185
+ declare const __VLS_export: vue0.DefineComponent<vue0.ExtractPropTypes<{
185
186
  name: {
186
187
  type: PropType<ContextStoragePrefixSegment>;
187
188
  required: true;
188
189
  };
189
- }>, () => vue9.VNode<vue9.RendererNode, vue9.RendererElement, {
190
+ }>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
190
191
  [key: string]: any;
191
- }>, {}, {}, {}, vue9.ComponentOptionsMixin, vue9.ComponentOptionsMixin, {}, string, vue9.PublicProps, Readonly<vue9.ExtractPropTypes<{
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, vue9.ComponentProvideOptions, true, {}, any>;
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 && !Array.isArray(value)) result[key] = [value];
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: serializeParams(initialData, { key: registerOptions.key }),
464
- additionalDefaultQueryData: registerOptions.additionalDefaultData ? serializeParams(registerOptions.additionalDefaultData, { key: registerOptions.key }) : void 0,
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.41",
5
- "description": "Vue 3 context storage system with URL query synchronization support",
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": "vitest run",
44
- "release": "bumpp && npm publish",
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 };