zodselect 1.0.8 → 1.0.10
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.
|
@@ -61,18 +61,18 @@ function buildSelect(schema, cache = new Map()) {
|
|
|
61
61
|
const hasLazy = types.some(item => isZodLazy(item));
|
|
62
62
|
if (hasLazy) {
|
|
63
63
|
return z.lazy(() => {
|
|
64
|
-
return buildSelectMerge(types);
|
|
64
|
+
return buildSelectMerge(types, cache);
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
-
return buildSelectMerge(types);
|
|
67
|
+
return buildSelectMerge(types, cache);
|
|
68
68
|
}
|
|
69
69
|
return z.boolean().optional();
|
|
70
70
|
}
|
|
71
|
-
function buildSelectMerge(types) {
|
|
71
|
+
function buildSelectMerge(types, cache) {
|
|
72
72
|
const shape = {};
|
|
73
73
|
let isStrict = false;
|
|
74
74
|
for (const cur of types) {
|
|
75
|
-
const select = buildSelect(cur);
|
|
75
|
+
const select = buildSelect(cur, cache);
|
|
76
76
|
let clause;
|
|
77
77
|
if (isZodLazy(select)) {
|
|
78
78
|
clause = select.def.getter();
|
|
@@ -60,6 +60,18 @@ describe('.buildSelectSchema()', () => {
|
|
|
60
60
|
});
|
|
61
61
|
assert.throws(() => selectSchema.parse({ valueC: true }), /Unrecognized key/);
|
|
62
62
|
});
|
|
63
|
+
it('z.infer of a union select schema should be a plain select type', () => {
|
|
64
|
+
const schema = z.union([
|
|
65
|
+
z.object({ shared: z.string(), type: z.literal('a'), valueA: z.string() }).strict(),
|
|
66
|
+
z.object({ shared: z.string(), type: z.literal('b'), valueB: z.number() }).strict()
|
|
67
|
+
]);
|
|
68
|
+
const selectSchema = buildSelectSchema(schema);
|
|
69
|
+
// Runtime: the schema correctly parses a plain select object
|
|
70
|
+
assert.deepEqual(selectSchema.parse({ shared: true, type: true, valueA: true }), { shared: true, type: true, valueA: true });
|
|
71
|
+
const plain = {};
|
|
72
|
+
void plain;
|
|
73
|
+
assert.ok(true);
|
|
74
|
+
});
|
|
63
75
|
it('should support intersections of objects', () => {
|
|
64
76
|
const schema = z.intersection(z.object({ a: z.string() }), z.object({ b: z.number() }));
|
|
65
77
|
const select = buildSelectSchema(schema);
|
|
@@ -75,7 +87,7 @@ describe('.buildSelectSchema()', () => {
|
|
|
75
87
|
assert.strictEqual(getInnerType(select.shape.foo).def.type, 'boolean');
|
|
76
88
|
assert.strictEqual(getInnerType(select.shape.bar).def.type, 'boolean');
|
|
77
89
|
});
|
|
78
|
-
it('should return a strict schema if
|
|
90
|
+
it('should return a strict schema if any member is strict', () => {
|
|
79
91
|
const schema = z.union([
|
|
80
92
|
z.object({ strictA: z.string() }).strict(),
|
|
81
93
|
z.object({ strictB: z.number() }).strict()
|
|
@@ -83,6 +95,16 @@ describe('.buildSelectSchema()', () => {
|
|
|
83
95
|
const select = buildSelectSchema(schema);
|
|
84
96
|
assert.strictEqual(isZodObjectLoose(select), false);
|
|
85
97
|
});
|
|
98
|
+
it('should return a strict schema when only one union member is strict', () => {
|
|
99
|
+
const schema = z.union([
|
|
100
|
+
z.object({ strictA: z.string() }).strict(),
|
|
101
|
+
z.looseObject({ looseB: z.number() })
|
|
102
|
+
]);
|
|
103
|
+
const select = buildSelectSchema(schema);
|
|
104
|
+
// Any strict member makes the merged select strict so callers cannot
|
|
105
|
+
// accidentally reference fields outside any member's defined shape.
|
|
106
|
+
assert.strictEqual(isZodObjectLoose(select), false);
|
|
107
|
+
});
|
|
86
108
|
it('should allow optional keys in the select schema', () => {
|
|
87
109
|
const schema = z.object({
|
|
88
110
|
optionalField: z.string().optional()
|
|
@@ -91,13 +113,13 @@ describe('.buildSelectSchema()', () => {
|
|
|
91
113
|
assert.strictEqual(getInnerType(select.shape.optionalField).def.type, 'boolean');
|
|
92
114
|
});
|
|
93
115
|
it('should handle direct recursion', () => {
|
|
94
|
-
const
|
|
116
|
+
const roleSchema = z.lazy(() => {
|
|
95
117
|
return (z.object({
|
|
96
118
|
name: z.string(),
|
|
97
|
-
roles: z.array(
|
|
119
|
+
roles: z.array(roleSchema)
|
|
98
120
|
}));
|
|
99
121
|
});
|
|
100
|
-
const select = buildSelectSchema(
|
|
122
|
+
const select = buildSelectSchema(roleSchema);
|
|
101
123
|
assert.deepStrictEqual(Object.keys(select.shape), ['name', 'roles']);
|
|
102
124
|
const nameSelect = select.shape.name;
|
|
103
125
|
assert.strictEqual(nameSelect.def.type, 'optional');
|
|
@@ -112,13 +134,13 @@ describe('.buildSelectSchema()', () => {
|
|
|
112
134
|
assert.strictEqual(roleItem.def.type, 'object');
|
|
113
135
|
});
|
|
114
136
|
it('should support mutual recursion (cross-recursive)', () => {
|
|
115
|
-
const
|
|
116
|
-
b:
|
|
137
|
+
const schemaA = z.lazy(() => z.object({
|
|
138
|
+
b: schemaB
|
|
117
139
|
}));
|
|
118
|
-
const
|
|
119
|
-
a:
|
|
140
|
+
const schemaB = z.lazy(() => z.object({
|
|
141
|
+
a: schemaA
|
|
120
142
|
}));
|
|
121
|
-
const select = buildSelectSchema(
|
|
143
|
+
const select = buildSelectSchema(schemaA);
|
|
122
144
|
assert.strictEqual(select.shape.b.def.type, 'lazy');
|
|
123
145
|
const bUnion = select.shape.b.def.getter();
|
|
124
146
|
assert.strictEqual(bUnion.def.innerType.def.type, 'union');
|
|
@@ -126,11 +148,11 @@ describe('.buildSelectSchema()', () => {
|
|
|
126
148
|
assert.strictEqual(bObj.shape.a.def.type, 'lazy');
|
|
127
149
|
});
|
|
128
150
|
it('should share the same instance in recursion (no infinite loop)', () => {
|
|
129
|
-
const
|
|
151
|
+
const role = z.lazy(() => z.object({
|
|
130
152
|
name: z.string(),
|
|
131
|
-
roles: z.array(
|
|
153
|
+
roles: z.array(role)
|
|
132
154
|
}));
|
|
133
|
-
const select = buildSelectSchema(
|
|
155
|
+
const select = buildSelectSchema(role);
|
|
134
156
|
const rolesSelect = select.shape.roles;
|
|
135
157
|
const unionSelect = rolesSelect.def.getter().def.innerType;
|
|
136
158
|
const nestedRoleSelect = unionSelect.def.options[1].shape.roles;
|
|
@@ -152,6 +174,19 @@ describe('.buildSelectSchema()', () => {
|
|
|
152
174
|
assert.strictEqual(union.def.options[1].def.valueType.type, 'optional');
|
|
153
175
|
assert.strictEqual(union.def.options[1].def.valueType.def.innerType.type, 'boolean');
|
|
154
176
|
});
|
|
177
|
+
it('should share the same lazy instance for a recursive schema referenced by multiple union members', () => {
|
|
178
|
+
const nested = z.lazy(() => z.object({ children: z.array(nested) }));
|
|
179
|
+
const schema = z.union([
|
|
180
|
+
z.object({ a: nested }),
|
|
181
|
+
z.object({ b: nested })
|
|
182
|
+
]);
|
|
183
|
+
const select = buildSelectSchema(schema);
|
|
184
|
+
// Both 'a' and 'b' reference the same `nested` schema.
|
|
185
|
+
// Their select schemas must be the same lazy instance (shared cache).
|
|
186
|
+
const aSelect = select.shape.a;
|
|
187
|
+
const bSelect = select.shape.b;
|
|
188
|
+
assert.strictEqual(aSelect, bSelect);
|
|
189
|
+
});
|
|
155
190
|
it('should support unknown types', () => {
|
|
156
191
|
const schema = z.object({
|
|
157
192
|
tags: z.unknown()
|
package/dist/refine-schema.js
CHANGED
|
@@ -6,7 +6,7 @@ export function refineSchema(schema, shape) {
|
|
|
6
6
|
const refinedShape = {};
|
|
7
7
|
const schemaShape = schema.shape;
|
|
8
8
|
for (const key in shape) {
|
|
9
|
-
if (key in schemaShape) {
|
|
9
|
+
if (key in schemaShape && shape[key] !== false) {
|
|
10
10
|
refinedShape[key] = refineSchemaField(schemaShape[key], shape[key]);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -4,16 +4,16 @@ import { z, ZodError } from 'zod';
|
|
|
4
4
|
import { refineSchema } from './refine-schema.js';
|
|
5
5
|
describe('.refineSchema()', () => {
|
|
6
6
|
it('should retain required fields', () => {
|
|
7
|
-
const
|
|
7
|
+
const userSchema = z.object({
|
|
8
8
|
age: z.number().optional(),
|
|
9
9
|
firstName: z.string(),
|
|
10
10
|
lastName: z.string()
|
|
11
11
|
});
|
|
12
|
-
const
|
|
12
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
13
13
|
firstName: true,
|
|
14
14
|
lastName: true
|
|
15
15
|
});
|
|
16
|
-
const parsedData =
|
|
16
|
+
const parsedData = refinedSchema.parse({
|
|
17
17
|
firstName: 'John',
|
|
18
18
|
lastName: 'Doe'
|
|
19
19
|
});
|
|
@@ -21,74 +21,74 @@ describe('.refineSchema()', () => {
|
|
|
21
21
|
assert.equal(parsedData.lastName, 'Doe');
|
|
22
22
|
});
|
|
23
23
|
it('should handle optional fields', () => {
|
|
24
|
-
const
|
|
24
|
+
const userSchema = z.object({
|
|
25
25
|
age: z.number().optional(),
|
|
26
26
|
firstName: z.string(),
|
|
27
27
|
lastName: z.string()
|
|
28
28
|
});
|
|
29
|
-
const
|
|
29
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
30
30
|
age: true
|
|
31
31
|
});
|
|
32
|
-
const parsedData =
|
|
32
|
+
const parsedData = refinedSchema.parse({ age: 25 });
|
|
33
33
|
assert.equal(parsedData.age, 25);
|
|
34
|
-
const parsedWithoutAge =
|
|
34
|
+
const parsedWithoutAge = refinedSchema.parse({});
|
|
35
35
|
assert.equal(parsedWithoutAge.age, undefined);
|
|
36
36
|
});
|
|
37
37
|
it('should handle nullable fields', () => {
|
|
38
|
-
const
|
|
38
|
+
const roleSchema = z.object({
|
|
39
39
|
name: z.string()
|
|
40
40
|
});
|
|
41
|
-
const
|
|
41
|
+
const userSchema = z.object({
|
|
42
42
|
firstName: z.string(),
|
|
43
|
-
role:
|
|
43
|
+
role: roleSchema.nullable()
|
|
44
44
|
});
|
|
45
|
-
const
|
|
45
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
46
46
|
role: true
|
|
47
47
|
});
|
|
48
|
-
const parsedWithNullRole =
|
|
48
|
+
const parsedWithNullRole = refinedSchema.parse({
|
|
49
49
|
firstName: 'Alice',
|
|
50
50
|
role: null
|
|
51
51
|
});
|
|
52
52
|
assert.equal(parsedWithNullRole.role, null);
|
|
53
53
|
});
|
|
54
54
|
it('should handle both optional and nullable fields', () => {
|
|
55
|
-
const
|
|
55
|
+
const roleSchema = z.object({
|
|
56
56
|
name: z.string()
|
|
57
57
|
});
|
|
58
|
-
const
|
|
58
|
+
const userSchema = z.object({
|
|
59
59
|
firstName: z.string(),
|
|
60
|
-
role:
|
|
60
|
+
role: roleSchema.nullable().optional()
|
|
61
61
|
});
|
|
62
|
-
const
|
|
62
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
63
63
|
role: {
|
|
64
64
|
name: true
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
|
-
const parsedWithRole =
|
|
67
|
+
const parsedWithRole = refinedSchema.parse({
|
|
68
68
|
firstName: 'Alice',
|
|
69
69
|
role: { name: 'Admin' }
|
|
70
70
|
});
|
|
71
71
|
assert.deepStrictEqual(parsedWithRole.role, { name: 'Admin' });
|
|
72
|
-
const parsedWithNullRole =
|
|
72
|
+
const parsedWithNullRole = refinedSchema.parse({
|
|
73
73
|
firstName: 'Alice',
|
|
74
74
|
role: null
|
|
75
75
|
});
|
|
76
76
|
assert.strictEqual(parsedWithNullRole.role, null);
|
|
77
|
-
const parsedWithoutRole =
|
|
77
|
+
const parsedWithoutRole = refinedSchema.parse({ firstName: 'Alice' });
|
|
78
78
|
assert.strictEqual(parsedWithoutRole.role, undefined);
|
|
79
79
|
});
|
|
80
80
|
it('should apply custom schema transformations', () => {
|
|
81
|
-
const
|
|
81
|
+
const roleSchema = z.object({
|
|
82
82
|
name: z.string()
|
|
83
83
|
});
|
|
84
|
-
const
|
|
84
|
+
const userSchema = z.object({
|
|
85
85
|
firstName: z.string(),
|
|
86
|
-
role:
|
|
86
|
+
role: roleSchema
|
|
87
87
|
});
|
|
88
|
-
const
|
|
89
|
-
role:
|
|
88
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
89
|
+
role: schema => schema.extend({ permissions: z.array(z.string()) })
|
|
90
90
|
});
|
|
91
|
-
const parsedData =
|
|
91
|
+
const parsedData = refinedSchema.parse({
|
|
92
92
|
firstName: 'Bob',
|
|
93
93
|
role: { name: 'User', permissions: ['read', 'write'] }
|
|
94
94
|
});
|
|
@@ -98,46 +98,46 @@ describe('.refineSchema()', () => {
|
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
100
|
it('should handle nested arrays', () => {
|
|
101
|
-
const
|
|
101
|
+
const roleSchema = z.object({
|
|
102
102
|
level: z.number(),
|
|
103
103
|
title: z.string()
|
|
104
104
|
});
|
|
105
|
-
const
|
|
105
|
+
const userSchema = z.object({
|
|
106
106
|
age: z.number().optional(),
|
|
107
107
|
firstName: z.string(),
|
|
108
108
|
lastName: z.string(),
|
|
109
109
|
nestedArray: z.array(z.object({ name: z.string(), value: z.number() })).optional().nullable(),
|
|
110
|
-
role:
|
|
110
|
+
role: roleSchema.nullable().optional(),
|
|
111
111
|
tags: z.array(z.string()).optional()
|
|
112
112
|
});
|
|
113
|
-
const
|
|
113
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
114
114
|
nestedArray: {
|
|
115
115
|
name: true
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
|
-
const parsedData =
|
|
118
|
+
const parsedData = refinedSchema.parse({
|
|
119
119
|
firstName: 'Alice',
|
|
120
120
|
lastName: 'Smith',
|
|
121
121
|
nestedArray: [{ name: 'Feature1', value: 10 }, { name: 'Feature2', value: 20 }]
|
|
122
122
|
});
|
|
123
123
|
assert.deepStrictEqual(parsedData.nestedArray, [{ name: 'Feature1' }, { name: 'Feature2' }]);
|
|
124
124
|
// Test optional array
|
|
125
|
-
const parsedWithoutNestedArray =
|
|
125
|
+
const parsedWithoutNestedArray = refinedSchema.parse({
|
|
126
126
|
firstName: 'Alice',
|
|
127
127
|
lastName: 'Smith'
|
|
128
128
|
});
|
|
129
129
|
assert.equal(parsedWithoutNestedArray.nestedArray, undefined);
|
|
130
130
|
});
|
|
131
131
|
it('should handle custom fields', () => {
|
|
132
|
-
const
|
|
132
|
+
const userSchema = z.object({
|
|
133
133
|
address: z.custom().optional(),
|
|
134
134
|
firstName: z.string(),
|
|
135
135
|
lastName: z.string()
|
|
136
136
|
});
|
|
137
|
-
const
|
|
137
|
+
const refinedSchema = refineSchema(userSchema, {
|
|
138
138
|
address: true
|
|
139
139
|
});
|
|
140
|
-
const parsedData =
|
|
140
|
+
const parsedData = refinedSchema.parse({
|
|
141
141
|
address: {
|
|
142
142
|
city: 'Austin',
|
|
143
143
|
state: 'TX',
|
|
@@ -155,45 +155,45 @@ describe('.refineSchema()', () => {
|
|
|
155
155
|
});
|
|
156
156
|
});
|
|
157
157
|
it('should support unions of objects', () => {
|
|
158
|
-
const
|
|
158
|
+
const person = z.object({
|
|
159
159
|
firstName: z.string(),
|
|
160
160
|
lastName: z.string()
|
|
161
161
|
});
|
|
162
|
-
const
|
|
162
|
+
const address = z.object({
|
|
163
163
|
address: z.string(),
|
|
164
164
|
city: z.string()
|
|
165
165
|
});
|
|
166
|
-
const
|
|
166
|
+
const schema = refineSchema(z.union([person, address]), {
|
|
167
167
|
address: true,
|
|
168
168
|
firstName: true
|
|
169
169
|
});
|
|
170
|
-
let parseResult =
|
|
170
|
+
let parseResult = schema.parse({
|
|
171
171
|
address: '123 Main Street',
|
|
172
172
|
firstName: 'Jerry',
|
|
173
173
|
lastName: 'Springer'
|
|
174
174
|
});
|
|
175
175
|
deepStrictEqual(parseResult, { firstName: 'Jerry' });
|
|
176
|
-
parseResult =
|
|
176
|
+
parseResult = schema.parse({
|
|
177
177
|
address: '123 Main Street',
|
|
178
178
|
city: 'Austin'
|
|
179
179
|
});
|
|
180
180
|
deepStrictEqual(parseResult, { address: '123 Main Street' });
|
|
181
181
|
});
|
|
182
182
|
it('should error if none of the types are matched', () => {
|
|
183
|
-
const
|
|
183
|
+
const person = z.object({
|
|
184
184
|
firstName: z.string(),
|
|
185
185
|
lastName: z.string()
|
|
186
186
|
});
|
|
187
|
-
const
|
|
187
|
+
const address = z.object({
|
|
188
188
|
address: z.string(),
|
|
189
189
|
city: z.string()
|
|
190
190
|
});
|
|
191
|
-
const
|
|
191
|
+
const schema = refineSchema(z.union([person, address]), {
|
|
192
192
|
address: true,
|
|
193
193
|
firstName: true
|
|
194
194
|
});
|
|
195
195
|
throws(() => {
|
|
196
|
-
|
|
196
|
+
schema.parse({
|
|
197
197
|
lastName: 'Springer'
|
|
198
198
|
});
|
|
199
199
|
}, (err) => {
|
|
@@ -203,21 +203,34 @@ describe('.refineSchema()', () => {
|
|
|
203
203
|
return true;
|
|
204
204
|
});
|
|
205
205
|
});
|
|
206
|
+
it('should exclude fields specified as false in the shape', () => {
|
|
207
|
+
const schema = z.object({
|
|
208
|
+
firstName: z.string(),
|
|
209
|
+
lastName: z.string()
|
|
210
|
+
});
|
|
211
|
+
const refined = refineSchema(schema, {
|
|
212
|
+
firstName: true,
|
|
213
|
+
lastName: false
|
|
214
|
+
});
|
|
215
|
+
// lastName was specified as false — it should be excluded from the refined schema
|
|
216
|
+
const result = refined.parse({ firstName: 'Alice', lastName: 'Smith' });
|
|
217
|
+
assert.deepStrictEqual(result, { firstName: 'Alice' });
|
|
218
|
+
});
|
|
206
219
|
it('should support unions of objects and simple types', () => {
|
|
207
|
-
const
|
|
220
|
+
const person = z.object({
|
|
208
221
|
firstName: z.string(),
|
|
209
222
|
lastName: z.string()
|
|
210
223
|
});
|
|
211
|
-
const
|
|
224
|
+
const schema = refineSchema(z.union([person, z.string()]), {
|
|
212
225
|
address: true,
|
|
213
226
|
firstName: true
|
|
214
227
|
});
|
|
215
|
-
let parseResult =
|
|
228
|
+
let parseResult = schema.parse({
|
|
216
229
|
firstName: 'Jerry',
|
|
217
230
|
lastName: 'Springer'
|
|
218
231
|
});
|
|
219
232
|
deepStrictEqual(parseResult, { firstName: 'Jerry' });
|
|
220
|
-
parseResult =
|
|
233
|
+
parseResult = schema.parse('123 Main Street');
|
|
221
234
|
deepStrictEqual(parseResult, '123 Main Street');
|
|
222
235
|
});
|
|
223
236
|
});
|
package/dist/select.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ type InferTuple<T extends ZodType<object> | ZodUnion, U extends readonly ZodSele
|
|
|
7
7
|
};
|
|
8
8
|
export type InferType<T extends ZodType<object> | ZodUnion, U extends ZodSelect<T>> = U extends ZodType<object> ? z.infer<U> : U extends ZodUnion ? z.infer<U> : T extends ZodUnion ? (U extends RefineZodUnion<T> ? z.infer<RefinedUnionSchema<T, U>> : z.infer<T>) : T extends ZodType<object> ? (U extends RefineObject<z.infer<T>, false> ? z.infer<RefinedTypeSchema<z.infer<T>, U>> : z.infer<T>) : z.infer<T>;
|
|
9
9
|
export type MergeSelect<T extends ZodType<object> | ZodUnion, TSelect1 extends ZodSelect<T>, TSelect2 extends MultiSelect<T>> = TSelect2 extends undefined ? [TSelect1, T] : IsEqual<TSelect2, T, TSelect1, TSelect2 extends [ZodSelect<T>, ...ZodSelect<T>[]] ? [TSelect1, ...TSelect2] : [TSelect1, TSelect2]>;
|
|
10
|
-
export type MultiSelect<T extends ZodType<object> | ZodUnion> = [
|
|
11
|
-
|
|
10
|
+
export type MultiSelect<T extends ZodType<object> | ZodUnion> = [
|
|
11
|
+
ZodSelect<T>,
|
|
12
|
+
...ZodSelect<T>[]
|
|
13
|
+
] | undefined | ZodSelect<T>;
|
|
14
|
+
export type ZodSelect<T extends ZodTuple | ZodType<object> | ZodUnion, TIsSimple extends boolean = false> = (T extends ZodUnion ? (RefineZodUnion<T> | (TIsSimple extends true ? never : ZodUnion)) : T extends ZodTuple ? (RefineZodTuple<T> | (TIsSimple extends true ? never : ZodTuple)) : T extends ZodType<object> ? (RefineObject<z.infer<T>, TIsSimple> | (TIsSimple extends true ? never : ZodType<object>)) : never);
|
|
12
15
|
export {};
|
package/dist/utilities.spec.js
CHANGED
|
@@ -30,8 +30,8 @@ describe('.utilities()', () => {
|
|
|
30
30
|
assert.strictEqual(inner.def.type, 'string');
|
|
31
31
|
});
|
|
32
32
|
it('should unwrap lazy inside nested wrappers', () => {
|
|
33
|
-
const
|
|
34
|
-
const wrapped =
|
|
33
|
+
const lazySchema = z.lazy(() => z.string());
|
|
34
|
+
const wrapped = lazySchema.optional().nullable().default('abc');
|
|
35
35
|
const inner = getInnerType(wrapped);
|
|
36
36
|
assert.strictEqual(inner.def.type, 'string');
|
|
37
37
|
});
|
|
@@ -56,20 +56,20 @@ describe('.utilities()', () => {
|
|
|
56
56
|
});
|
|
57
57
|
it('isZodRecord detects complex record schemas with optional fields', () => {
|
|
58
58
|
// Create a sample RecordAttachmentSchema
|
|
59
|
-
const
|
|
59
|
+
const recordAttachmentSchema = z.object({
|
|
60
60
|
name: z.string(),
|
|
61
61
|
size: z.number(),
|
|
62
62
|
type: z.string()
|
|
63
63
|
});
|
|
64
64
|
// Create a record schema with optional field
|
|
65
|
-
const attachmentsSchema = z.record(z.string(),
|
|
65
|
+
const attachmentsSchema = z.record(z.string(), recordAttachmentSchema).optional();
|
|
66
66
|
const nonRecordSchema = z.object({ field: z.string() });
|
|
67
67
|
// Test record detection
|
|
68
|
-
assert.ok(isZodRecord(z.record(z.string(),
|
|
68
|
+
assert.ok(isZodRecord(z.record(z.string(), recordAttachmentSchema)), 'Should detect record schema');
|
|
69
69
|
assert.ok(isZodRecord(attachmentsSchema.unwrap()), 'Should detect optional record schema when unwrapped');
|
|
70
70
|
assert.ok(!isZodRecord(nonRecordSchema), 'Should not detect object schema as record');
|
|
71
71
|
// Test with actual record data
|
|
72
|
-
const validRecord = z.record(z.string(),
|
|
72
|
+
const validRecord = z.record(z.string(), recordAttachmentSchema).parse({
|
|
73
73
|
'file1': { name: 'test.pdf', size: 1024, type: 'application/pdf' },
|
|
74
74
|
'file2': { name: 'image.jpg', size: 2048, type: 'image/jpeg' }
|
|
75
75
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zodselect",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Type-safe selection utility built on top of Zod. Allows for strongly typed field selection, schema merging, and runtime refinement for frontend or backend use.",
|
|
5
5
|
"author": "SecureDataSystems",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,21 +25,22 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@secure-data-systems/eslint-config": "^1.0.
|
|
29
|
-
"@secure-data-systems/tsconfig": "^1.0.
|
|
30
|
-
"@types/node": "^
|
|
31
|
-
"
|
|
32
|
-
"tsx": "^4.20.6",
|
|
28
|
+
"@secure-data-systems/eslint-config": "^1.0.33",
|
|
29
|
+
"@secure-data-systems/tsconfig": "^1.0.3",
|
|
30
|
+
"@types/node": "^25.3.0",
|
|
31
|
+
"tsx": "^4.21.0",
|
|
33
32
|
"typescript": "^5.9.3"
|
|
34
33
|
},
|
|
35
34
|
"dependencies": {
|
|
36
|
-
"zod": "^4.
|
|
35
|
+
"zod": "^4.3.6"
|
|
37
36
|
},
|
|
38
37
|
"scripts": {
|
|
39
38
|
"build": "node scripts/build.mjs && tsc --project tsconfig.build.json",
|
|
39
|
+
"clean": "node scripts/clean.mjs",
|
|
40
40
|
"deploy": "npm run build && node ./scripts/publish.mjs",
|
|
41
41
|
"lint": "eslint . --c eslint.config.mjs",
|
|
42
42
|
"lint:fix": "eslint . --c eslint.config.mjs --fix",
|
|
43
|
-
"
|
|
43
|
+
"prune": "node scripts/prune.mjs",
|
|
44
|
+
"test": "tsx --tsconfig ./tsconfig.build.json --test ./src/**/*.{spec,test}.ts ./test/**/*.{spec,test}.ts"
|
|
44
45
|
}
|
|
45
46
|
}
|