zodselect 1.0.14 → 1.0.15
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.
|
@@ -6,9 +6,9 @@ export type ZodSelectAll<T extends ZodType<object> | ZodUnion> = {
|
|
|
6
6
|
/**
|
|
7
7
|
* Produces a runtime select-all object for the given Zod schema.
|
|
8
8
|
*
|
|
9
|
-
* Every
|
|
10
|
-
*
|
|
11
|
-
* set of keys across all variants.
|
|
9
|
+
* Every scalar key is set to `true`. Nested object fields are recursively
|
|
10
|
+
* expanded into nested select objects. Array fields remain `true`.
|
|
11
|
+
* For union schemas the result is the merged set of keys across all variants.
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```ts
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { ZodCatch, ZodDefault, ZodLazy, ZodNullable, ZodOptional } from 'zod';
|
|
1
2
|
import { getInnerType, isZodObject, isZodUnion } from './utilities.js';
|
|
2
3
|
/**
|
|
3
4
|
* Produces a runtime select-all object for the given Zod schema.
|
|
4
5
|
*
|
|
5
|
-
* Every
|
|
6
|
-
*
|
|
7
|
-
* set of keys across all variants.
|
|
6
|
+
* Every scalar key is set to `true`. Nested object fields are recursively
|
|
7
|
+
* expanded into nested select objects. Array fields remain `true`.
|
|
8
|
+
* For union schemas the result is the merged set of keys across all variants.
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* ```ts
|
|
@@ -17,20 +18,47 @@ export function buildSelectObject(schema) {
|
|
|
17
18
|
collectKeys(schema, result);
|
|
18
19
|
return result;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Unwraps optional/nullable/default/catch/lazy wrappers but NOT arrays.
|
|
23
|
+
*/
|
|
24
|
+
function unwrapField(schema) {
|
|
25
|
+
if (schema instanceof ZodOptional
|
|
26
|
+
|| schema instanceof ZodNullable
|
|
27
|
+
|| schema instanceof ZodDefault
|
|
28
|
+
|| schema instanceof ZodCatch) {
|
|
29
|
+
return unwrapField(schema.def.innerType);
|
|
30
|
+
}
|
|
31
|
+
if (schema instanceof ZodLazy) {
|
|
32
|
+
return unwrapField(schema.def.getter());
|
|
33
|
+
}
|
|
34
|
+
return schema;
|
|
35
|
+
}
|
|
36
|
+
const MAX_DEPTH = 10;
|
|
37
|
+
function collectKeys(schema, result, depth = 0) {
|
|
38
|
+
if (depth >= MAX_DEPTH) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
21
41
|
schema = getInnerType(schema);
|
|
22
42
|
if (isZodObject(schema)) {
|
|
23
43
|
for (const key in schema.shape) {
|
|
24
|
-
|
|
44
|
+
const fieldInner = unwrapField(schema.shape[key]);
|
|
45
|
+
if (isZodObject(fieldInner)) {
|
|
46
|
+
const nested = {};
|
|
47
|
+
collectKeys(fieldInner, nested, depth + 1);
|
|
48
|
+
result[key] = nested;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
result[key] = true;
|
|
52
|
+
}
|
|
25
53
|
}
|
|
26
54
|
}
|
|
27
55
|
else if (isZodUnion(schema)) {
|
|
28
56
|
for (const option of schema.options) {
|
|
29
|
-
collectKeys(option, result);
|
|
57
|
+
collectKeys(option, result, depth);
|
|
30
58
|
}
|
|
31
59
|
}
|
|
32
60
|
else if (schema.def.type === 'intersection') {
|
|
33
|
-
collectKeys(schema.def.left, result);
|
|
34
|
-
collectKeys(schema.def.right, result);
|
|
61
|
+
collectKeys(schema.def.left, result, depth);
|
|
62
|
+
collectKeys(schema.def.right, result, depth);
|
|
35
63
|
}
|
|
36
64
|
}
|
|
@@ -45,7 +45,7 @@ describe('.buildSelectObject()', () => {
|
|
|
45
45
|
c: true
|
|
46
46
|
});
|
|
47
47
|
});
|
|
48
|
-
it('should
|
|
48
|
+
it('should recursively expand nested objects', () => {
|
|
49
49
|
const schema = z.object({
|
|
50
50
|
active: z.boolean(),
|
|
51
51
|
user: z.object({
|
|
@@ -53,6 +53,69 @@ describe('.buildSelectObject()', () => {
|
|
|
53
53
|
name: z.string()
|
|
54
54
|
})
|
|
55
55
|
});
|
|
56
|
-
assert.deepStrictEqual(buildSelectObject(schema), {
|
|
56
|
+
assert.deepStrictEqual(buildSelectObject(schema), {
|
|
57
|
+
active: true,
|
|
58
|
+
user: { age: true, name: true }
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
it('should recursively expand deeply nested objects', () => {
|
|
62
|
+
const schema = z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
organization: z.object({
|
|
65
|
+
_id: z.string(),
|
|
66
|
+
address: z.object({
|
|
67
|
+
city: z.string(),
|
|
68
|
+
country: z.string()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
assert.deepStrictEqual(buildSelectObject(schema), {
|
|
73
|
+
name: true,
|
|
74
|
+
organization: { _id: true, address: { city: true, country: true } }
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
it('should expand optional nested objects', () => {
|
|
78
|
+
const schema = z.object({
|
|
79
|
+
name: z.string(),
|
|
80
|
+
organization: z.object({
|
|
81
|
+
_id: z.string(),
|
|
82
|
+
name: z.string()
|
|
83
|
+
}).optional()
|
|
84
|
+
});
|
|
85
|
+
assert.deepStrictEqual(buildSelectObject(schema), {
|
|
86
|
+
name: true,
|
|
87
|
+
organization: { _id: true, name: true }
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it('should not expand arrays', () => {
|
|
91
|
+
const schema = z.object({
|
|
92
|
+
name: z.string(),
|
|
93
|
+
tags: z.array(z.string())
|
|
94
|
+
});
|
|
95
|
+
assert.deepStrictEqual(buildSelectObject(schema), { name: true, tags: true });
|
|
96
|
+
});
|
|
97
|
+
it('should not expand arrays of objects', () => {
|
|
98
|
+
const schema = z.object({
|
|
99
|
+
items: z.array(z.object({ id: z.string(), value: z.number() })),
|
|
100
|
+
name: z.string()
|
|
101
|
+
});
|
|
102
|
+
assert.deepStrictEqual(buildSelectObject(schema), { items: true, name: true });
|
|
103
|
+
});
|
|
104
|
+
it('should handle recursive schemas without infinite loop', () => {
|
|
105
|
+
;
|
|
106
|
+
const node = z.lazy(() => z.object({
|
|
107
|
+
children: z.array(node).optional(),
|
|
108
|
+
name: z.string(),
|
|
109
|
+
parent: node.optional()
|
|
110
|
+
}));
|
|
111
|
+
const schema = z.object({
|
|
112
|
+
name: z.string(),
|
|
113
|
+
root: node
|
|
114
|
+
});
|
|
115
|
+
// Should not throw or hang — recursive objects hit depth limit
|
|
116
|
+
const result = buildSelectObject(schema);
|
|
117
|
+
assert.strictEqual(result.name, true);
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
119
|
+
assert.ok(typeof result.root === 'object' && result.root !== null);
|
|
57
120
|
});
|
|
58
121
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zodselect",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
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",
|