zodselect 1.0.9 → 1.0.11
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.
|
@@ -5,5 +5,5 @@ export type BasicSelect = ZodObject<Readonly<{
|
|
|
5
5
|
[k: string]: Clause | ZodLazy<Clause>;
|
|
6
6
|
}>> | ZodRecord<$ZodRecordKey, Clause | ZodLazy<Clause>>;
|
|
7
7
|
type Clause = ZodOptional<ZodBoolean> | ZodOptional<ZodUnion<readonly [ZodBoolean, BasicSelect]>>;
|
|
8
|
-
export declare function buildSelectSchema<T extends ZodType<object> | ZodUnion>(schema: T): ZodType<ZodSelect<T, true>>;
|
|
8
|
+
export declare function buildSelectSchema<T extends ZodType<object> | ZodUnion>(schema: T): ZodType<ZodSelect<T, true>, ZodSelect<T, true>>;
|
|
9
9
|
export {};
|
|
@@ -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()
|
|
@@ -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
|
}
|
|
@@ -203,6 +203,19 @@ 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
220
|
const person = z.object({
|
|
208
221
|
firstName: z.string(),
|
package/dist/select.d.ts
CHANGED
|
@@ -11,5 +11,5 @@ export type MultiSelect<T extends ZodType<object> | ZodUnion> = [
|
|
|
11
11
|
ZodSelect<T>,
|
|
12
12
|
...ZodSelect<T>[]
|
|
13
13
|
] | undefined | ZodSelect<T>;
|
|
14
|
-
export type ZodSelect<T extends ZodTuple | ZodType<object> | ZodUnion, TIsSimple extends boolean = false> = (T extends ZodUnion ? (RefineZodUnion<T> | ZodUnion) : T extends ZodTuple ? (RefineZodTuple<T> | ZodTuple) : T extends ZodType<object> ? (RefineObject<z.infer<T>, TIsSimple> | ZodType<object>) : never);
|
|
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);
|
|
15
15
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zodselect",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
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,14 +25,14 @@
|
|
|
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": "^
|
|
28
|
+
"@secure-data-systems/eslint-config": "^1.0.36",
|
|
29
|
+
"@secure-data-systems/tsconfig": "^1.0.5",
|
|
30
|
+
"@types/node": "^25.6.0",
|
|
31
31
|
"tsx": "^4.21.0",
|
|
32
|
-
"typescript": "^
|
|
32
|
+
"typescript": "^6.0.2"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"zod": "^4.
|
|
35
|
+
"zod": "^4.3.6"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "node scripts/build.mjs && tsc --project tsconfig.build.json",
|
|
@@ -40,6 +40,7 @@
|
|
|
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
|
+
"prune": "node scripts/prune.mjs",
|
|
43
44
|
"test": "tsx --tsconfig ./tsconfig.build.json --test ./src/**/*.{spec,test}.ts ./test/**/*.{spec,test}.ts"
|
|
44
45
|
}
|
|
45
46
|
}
|