@veloxts/validation 0.6.82 → 0.6.84
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/CHANGELOG.md +16 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/schemas/query.d.ts +185 -0
- package/dist/schemas/query.js +150 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @veloxts/validation
|
|
2
2
|
|
|
3
|
+
## 0.6.84
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- - auth: add simplified guard() function with overloads + fluent builder
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @veloxts/core@0.6.84
|
|
10
|
+
|
|
11
|
+
## 0.6.83
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- docs(templates): add /veloxts skill to CLAUDE.md files and links to online documentation
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @veloxts/core@0.6.83
|
|
18
|
+
|
|
3
19
|
## 0.6.82
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -33,5 +33,6 @@ export type { BaseEntity, IdParam, TimestampFields } from './schemas/common.js';
|
|
|
33
33
|
export { baseEntitySchema, booleanStringSchema, createIdSchema, datetimeSchema, emailSchema, idParamSchema, integerStringSchema, makePartial, nonEmptyStringSchema, numberStringSchema, omitFields, partialExcept, pickFields, timestampFieldsSchema, urlSchema, uuidSchema, } from './schemas/common.js';
|
|
34
34
|
export type { CursorPaginatedResponse, CursorPaginationInput, CursorPaginationMeta, PaginatedResponse, PaginationInput, PaginationMeta, } from './schemas/pagination.js';
|
|
35
35
|
export { calculateOffset, calculatePaginationMeta, createCursorPaginatedResponseSchema, createPaginatedResponse, createPaginatedResponseSchema, createPaginationSchema, cursorPaginationSchema, PAGINATION_DEFAULTS, paginationInputSchema, } from './schemas/pagination.js';
|
|
36
|
+
export { pagination, queryArray, queryBoolean, queryEnum, queryInt, queryNumber, } from './schemas/query.js';
|
|
36
37
|
export type { InferWithTimestamps, OmitTimestamps, SerializedDates, WithOptional, } from './schemas/serialization.js';
|
|
37
38
|
export { dateToISOString, dateToISOStringNullable, dateToISOStringOptional, dateToIso, dateToIsoNullable, dateToIsoOptional, prismaDecimal, prismaDecimalNullable, prismaDecimalOptional, timestamps, timestampsWithSoftDelete, withTimestamps, } from './schemas/serialization.js';
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,10 @@ export { isSchema, isZodSchema, wrapSchema } from './types.js';
|
|
|
35
35
|
export { assertSchema, createTypeGuard, createValidator, formatZodErrors, parse, parseAll, safeParse, zodErrorToValidationError, } from './middleware.js';
|
|
36
36
|
export { baseEntitySchema, booleanStringSchema, createIdSchema, datetimeSchema, emailSchema, idParamSchema, integerStringSchema, makePartial, nonEmptyStringSchema, numberStringSchema, omitFields, partialExcept, pickFields, timestampFieldsSchema, urlSchema, uuidSchema, } from './schemas/common.js';
|
|
37
37
|
export { calculateOffset, calculatePaginationMeta, createCursorPaginatedResponseSchema, createPaginatedResponse, createPaginatedResponseSchema, createPaginationSchema, cursorPaginationSchema, PAGINATION_DEFAULTS, paginationInputSchema, } from './schemas/pagination.js';
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Query Parameter Helpers
|
|
40
|
+
// ============================================================================
|
|
41
|
+
export { pagination, queryArray, queryBoolean, queryEnum, queryInt, queryNumber, } from './schemas/query.js';
|
|
38
42
|
export {
|
|
39
43
|
// Date serialization
|
|
40
44
|
dateToISOString, dateToISOStringNullable, dateToISOStringOptional,
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Parameter Coercion Helpers
|
|
3
|
+
*
|
|
4
|
+
* Provides type-safe coercion utilities for query string parameters.
|
|
5
|
+
* Query parameters arrive as strings; these helpers provide automatic
|
|
6
|
+
* conversion to appropriate types with sensible defaults.
|
|
7
|
+
*
|
|
8
|
+
* @module schemas/query
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
/**
|
|
12
|
+
* Creates a number schema that coerces from query string
|
|
13
|
+
*
|
|
14
|
+
* @param defaultValue - Default when undefined/empty (optional)
|
|
15
|
+
* @returns Zod schema that parses string to number
|
|
16
|
+
*
|
|
17
|
+
* @example Required number
|
|
18
|
+
* ```typescript
|
|
19
|
+
* .input(z.object({
|
|
20
|
+
* userId: queryNumber(), // Required, no default
|
|
21
|
+
* }))
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example With default value
|
|
25
|
+
* ```typescript
|
|
26
|
+
* .input(z.object({
|
|
27
|
+
* page: queryNumber(1), // Defaults to 1
|
|
28
|
+
* limit: queryNumber(20), // Defaults to 20
|
|
29
|
+
* }))
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function queryNumber(): z.ZodNumber;
|
|
33
|
+
export declare function queryNumber(defaultValue: number): z.ZodDefault<z.ZodNumber>;
|
|
34
|
+
/**
|
|
35
|
+
* Creates an integer schema that coerces from query string
|
|
36
|
+
*
|
|
37
|
+
* @param defaultValue - Default when undefined/empty (optional)
|
|
38
|
+
* @returns Zod schema that parses string to integer
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* .input(z.object({
|
|
43
|
+
* page: queryInt(1), // Defaults to 1
|
|
44
|
+
* userId: queryInt(), // Required integer
|
|
45
|
+
* }))
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function queryInt(): z.ZodNumber;
|
|
49
|
+
export declare function queryInt(defaultValue: number): z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a boolean schema that coerces from query string
|
|
52
|
+
*
|
|
53
|
+
* Accepts common truthy/falsy string values:
|
|
54
|
+
* - Truthy: 'true', '1', 'yes', 'on'
|
|
55
|
+
* - Falsy: 'false', '0', 'no', 'off'
|
|
56
|
+
*
|
|
57
|
+
* @param defaultValue - Default when undefined/empty (optional)
|
|
58
|
+
* @returns Zod schema that parses string to boolean
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* .input(z.object({
|
|
63
|
+
* active: queryBoolean(true), // Defaults to true
|
|
64
|
+
* deleted: queryBoolean(false), // Defaults to false
|
|
65
|
+
* verified: queryBoolean(), // Optional, undefined if not provided
|
|
66
|
+
* }))
|
|
67
|
+
*
|
|
68
|
+
* // Accepts: ?active=true, ?active=1, ?active=yes, ?active=on
|
|
69
|
+
* // Accepts: ?deleted=false, ?deleted=0, ?deleted=no, ?deleted=off
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function queryBoolean(): z.ZodOptional<z.ZodBoolean>;
|
|
73
|
+
export declare function queryBoolean(defaultValue: boolean): z.ZodDefault<z.ZodBoolean>;
|
|
74
|
+
/**
|
|
75
|
+
* Creates a string array schema that coerces from comma-separated query string
|
|
76
|
+
*
|
|
77
|
+
* @param options - Configuration options
|
|
78
|
+
* @returns Zod schema that parses comma-separated string to array
|
|
79
|
+
*
|
|
80
|
+
* @example Basic usage
|
|
81
|
+
* ```typescript
|
|
82
|
+
* .input(z.object({
|
|
83
|
+
* tags: queryArray(), // 'a,b,c' -> ['a', 'b', 'c']
|
|
84
|
+
* ids: queryArray({ min: 1 }), // At least one item required
|
|
85
|
+
* categories: queryArray({ max: 5 }), // Max 5 items
|
|
86
|
+
* }))
|
|
87
|
+
*
|
|
88
|
+
* // GET /api/products?tags=electronics,sale,featured
|
|
89
|
+
* // Result: { tags: ['electronics', 'sale', 'featured'] }
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example Edge cases
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // Empty string results in empty array (whitespace-only items filtered)
|
|
95
|
+
* queryArray().parse('') // []
|
|
96
|
+
* queryArray().parse(' , ') // []
|
|
97
|
+
*
|
|
98
|
+
* // With min constraint, empty string will fail validation
|
|
99
|
+
* queryArray({ min: 1 }).parse('') // throws: "Array must have at least 1 item(s)"
|
|
100
|
+
*
|
|
101
|
+
* // If you want "no parameter = no filter", combine with optional:
|
|
102
|
+
* .input(z.object({
|
|
103
|
+
* tags: queryArray({ min: 1 }).optional(), // undefined or non-empty array
|
|
104
|
+
* }))
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @remarks
|
|
108
|
+
* Empty strings and whitespace-only values are automatically filtered out.
|
|
109
|
+
* This means `?tags=` (empty) and `?tags=,,` (only separators) both result
|
|
110
|
+
* in an empty array `[]`. If you have a `min: 1` constraint, these will fail
|
|
111
|
+
* validation. Use `.optional()` if the parameter should be omittable.
|
|
112
|
+
*/
|
|
113
|
+
export declare function queryArray(options?: {
|
|
114
|
+
/** Minimum number of items required */
|
|
115
|
+
min?: number;
|
|
116
|
+
/** Maximum number of items allowed */
|
|
117
|
+
max?: number;
|
|
118
|
+
/** Separator character (default: ',') */
|
|
119
|
+
separator?: string;
|
|
120
|
+
}): z.ZodType<string[], z.ZodTypeDef, string>;
|
|
121
|
+
/**
|
|
122
|
+
* Creates an enum schema that validates against allowed values
|
|
123
|
+
*
|
|
124
|
+
* @param values - Array of allowed string values (use `as const` for type inference)
|
|
125
|
+
* @param defaultValue - Default value when undefined/empty (optional)
|
|
126
|
+
* @returns Zod schema that validates against enum values
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* .input(z.object({
|
|
131
|
+
* sort: queryEnum(['asc', 'desc'] as const, 'asc'),
|
|
132
|
+
* status: queryEnum(['active', 'pending', 'archived'] as const),
|
|
133
|
+
* }))
|
|
134
|
+
*
|
|
135
|
+
* // GET /api/users?sort=desc&status=active
|
|
136
|
+
* // Result: { sort: 'desc', status: 'active' }
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export declare function queryEnum<T extends readonly [string, ...string[]]>(values: T): z.ZodEnum<[T[number], ...T[number][]]>;
|
|
140
|
+
export declare function queryEnum<T extends readonly [string, ...string[]]>(values: T, defaultValue: T[number]): z.ZodDefault<z.ZodEnum<[T[number], ...T[number][]]>>;
|
|
141
|
+
/**
|
|
142
|
+
* Pre-built pagination schema for common use cases
|
|
143
|
+
*
|
|
144
|
+
* This is a shorthand for creating pagination input schemas.
|
|
145
|
+
* For more customization, use `createPaginationSchema` from pagination.js.
|
|
146
|
+
*
|
|
147
|
+
* @param options - Pagination configuration
|
|
148
|
+
* @returns Zod schema for pagination input
|
|
149
|
+
*
|
|
150
|
+
* @example Default offset-based pagination
|
|
151
|
+
* ```typescript
|
|
152
|
+
* .input(pagination())
|
|
153
|
+
* // { page: number, limit: number }
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @example With custom limits
|
|
157
|
+
* ```typescript
|
|
158
|
+
* .input(pagination({ defaultLimit: 10, maxLimit: 50 }))
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @example Extended with filters
|
|
162
|
+
* ```typescript
|
|
163
|
+
* .input(pagination().extend({
|
|
164
|
+
* search: z.string().optional(),
|
|
165
|
+
* status: queryEnum(['active', 'archived'] as const),
|
|
166
|
+
* }))
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export declare function pagination(options?: {
|
|
170
|
+
/** Default page number (default: 1) */
|
|
171
|
+
defaultPage?: number;
|
|
172
|
+
/** Default items per page (default: 20) */
|
|
173
|
+
defaultLimit?: number;
|
|
174
|
+
/** Maximum allowed items per page (default: 100) */
|
|
175
|
+
maxLimit?: number;
|
|
176
|
+
}): z.ZodObject<{
|
|
177
|
+
page: z.ZodDefault<z.ZodNumber>;
|
|
178
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
179
|
+
}, "strip", z.ZodTypeAny, {
|
|
180
|
+
page: number;
|
|
181
|
+
limit: number;
|
|
182
|
+
}, {
|
|
183
|
+
page?: number | undefined;
|
|
184
|
+
limit?: number | undefined;
|
|
185
|
+
}>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Parameter Coercion Helpers
|
|
3
|
+
*
|
|
4
|
+
* Provides type-safe coercion utilities for query string parameters.
|
|
5
|
+
* Query parameters arrive as strings; these helpers provide automatic
|
|
6
|
+
* conversion to appropriate types with sensible defaults.
|
|
7
|
+
*
|
|
8
|
+
* @module schemas/query
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
export function queryNumber(defaultValue) {
|
|
12
|
+
const base = z.coerce.number();
|
|
13
|
+
return defaultValue !== undefined ? base.default(defaultValue) : base;
|
|
14
|
+
}
|
|
15
|
+
export function queryInt(defaultValue) {
|
|
16
|
+
const base = z.coerce.number().int();
|
|
17
|
+
return defaultValue !== undefined ? base.default(defaultValue) : base;
|
|
18
|
+
}
|
|
19
|
+
export function queryBoolean(defaultValue) {
|
|
20
|
+
// Use preprocess to handle string-to-boolean conversion with query string conventions.
|
|
21
|
+
//
|
|
22
|
+
// NOTE: Type casting is required here because z.preprocess() wraps the inner schema
|
|
23
|
+
// in ZodEffects, but our public API promises ZodDefault<ZodBoolean> / ZodOptional<ZodBoolean>
|
|
24
|
+
// for better consumer type inference. The runtime behavior is correct; we cast to
|
|
25
|
+
// maintain the cleaner public type signature.
|
|
26
|
+
const booleanSchema = z.preprocess((val) => {
|
|
27
|
+
if (typeof val === 'boolean')
|
|
28
|
+
return val;
|
|
29
|
+
if (typeof val === 'string') {
|
|
30
|
+
return ['true', '1', 'yes', 'on'].includes(val.toLowerCase());
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}, z.boolean());
|
|
34
|
+
if (defaultValue !== undefined) {
|
|
35
|
+
return booleanSchema.default(defaultValue);
|
|
36
|
+
}
|
|
37
|
+
return booleanSchema.optional();
|
|
38
|
+
}
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// String Array Helper
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Creates a string array schema that coerces from comma-separated query string
|
|
44
|
+
*
|
|
45
|
+
* @param options - Configuration options
|
|
46
|
+
* @returns Zod schema that parses comma-separated string to array
|
|
47
|
+
*
|
|
48
|
+
* @example Basic usage
|
|
49
|
+
* ```typescript
|
|
50
|
+
* .input(z.object({
|
|
51
|
+
* tags: queryArray(), // 'a,b,c' -> ['a', 'b', 'c']
|
|
52
|
+
* ids: queryArray({ min: 1 }), // At least one item required
|
|
53
|
+
* categories: queryArray({ max: 5 }), // Max 5 items
|
|
54
|
+
* }))
|
|
55
|
+
*
|
|
56
|
+
* // GET /api/products?tags=electronics,sale,featured
|
|
57
|
+
* // Result: { tags: ['electronics', 'sale', 'featured'] }
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example Edge cases
|
|
61
|
+
* ```typescript
|
|
62
|
+
* // Empty string results in empty array (whitespace-only items filtered)
|
|
63
|
+
* queryArray().parse('') // []
|
|
64
|
+
* queryArray().parse(' , ') // []
|
|
65
|
+
*
|
|
66
|
+
* // With min constraint, empty string will fail validation
|
|
67
|
+
* queryArray({ min: 1 }).parse('') // throws: "Array must have at least 1 item(s)"
|
|
68
|
+
*
|
|
69
|
+
* // If you want "no parameter = no filter", combine with optional:
|
|
70
|
+
* .input(z.object({
|
|
71
|
+
* tags: queryArray({ min: 1 }).optional(), // undefined or non-empty array
|
|
72
|
+
* }))
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* Empty strings and whitespace-only values are automatically filtered out.
|
|
77
|
+
* This means `?tags=` (empty) and `?tags=,,` (only separators) both result
|
|
78
|
+
* in an empty array `[]`. If you have a `min: 1` constraint, these will fail
|
|
79
|
+
* validation. Use `.optional()` if the parameter should be omittable.
|
|
80
|
+
*/
|
|
81
|
+
export function queryArray(options = {}) {
|
|
82
|
+
const { min, max, separator = ',' } = options;
|
|
83
|
+
// Split by separator, trim whitespace, and filter out empty strings.
|
|
84
|
+
// Note: Empty input ('') results in [], which may fail min constraint.
|
|
85
|
+
const baseSchema = z.string().transform((val) => val
|
|
86
|
+
.split(separator)
|
|
87
|
+
.map((s) => s.trim())
|
|
88
|
+
.filter(Boolean));
|
|
89
|
+
// Apply min/max constraints via refinement if needed
|
|
90
|
+
if (min !== undefined || max !== undefined) {
|
|
91
|
+
return baseSchema.refine((arr) => {
|
|
92
|
+
if (min !== undefined && arr.length < min)
|
|
93
|
+
return false;
|
|
94
|
+
if (max !== undefined && arr.length > max)
|
|
95
|
+
return false;
|
|
96
|
+
return true;
|
|
97
|
+
}, {
|
|
98
|
+
message: min !== undefined && max !== undefined
|
|
99
|
+
? `Array must have ${min}-${max} items`
|
|
100
|
+
: min !== undefined
|
|
101
|
+
? `Array must have at least ${min} item(s)`
|
|
102
|
+
: `Array must have at most ${max} item(s)`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return baseSchema;
|
|
106
|
+
}
|
|
107
|
+
export function queryEnum(values, defaultValue) {
|
|
108
|
+
// Cast to mutable tuple type that Zod expects
|
|
109
|
+
const mutableValues = [...values];
|
|
110
|
+
const base = z.enum(mutableValues);
|
|
111
|
+
return defaultValue !== undefined ? base.default(defaultValue) : base;
|
|
112
|
+
}
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Pagination Shorthand
|
|
115
|
+
// ============================================================================
|
|
116
|
+
/**
|
|
117
|
+
* Pre-built pagination schema for common use cases
|
|
118
|
+
*
|
|
119
|
+
* This is a shorthand for creating pagination input schemas.
|
|
120
|
+
* For more customization, use `createPaginationSchema` from pagination.js.
|
|
121
|
+
*
|
|
122
|
+
* @param options - Pagination configuration
|
|
123
|
+
* @returns Zod schema for pagination input
|
|
124
|
+
*
|
|
125
|
+
* @example Default offset-based pagination
|
|
126
|
+
* ```typescript
|
|
127
|
+
* .input(pagination())
|
|
128
|
+
* // { page: number, limit: number }
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @example With custom limits
|
|
132
|
+
* ```typescript
|
|
133
|
+
* .input(pagination({ defaultLimit: 10, maxLimit: 50 }))
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @example Extended with filters
|
|
137
|
+
* ```typescript
|
|
138
|
+
* .input(pagination().extend({
|
|
139
|
+
* search: z.string().optional(),
|
|
140
|
+
* status: queryEnum(['active', 'archived'] as const),
|
|
141
|
+
* }))
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function pagination(options = {}) {
|
|
145
|
+
const { defaultPage = 1, defaultLimit = 20, maxLimit = 100 } = options;
|
|
146
|
+
return z.object({
|
|
147
|
+
page: z.coerce.number().int().positive().default(defaultPage),
|
|
148
|
+
limit: z.coerce.number().int().positive().max(maxLimit).default(defaultLimit),
|
|
149
|
+
});
|
|
150
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veloxts/validation",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.84",
|
|
4
4
|
"description": "Zod integration and validation middleware for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"zod": "3.25.76",
|
|
26
|
-
"@veloxts/core": "0.6.
|
|
26
|
+
"@veloxts/core": "0.6.84"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@vitest/coverage-v8": "4.0.16",
|