@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 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.82",
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.82"
26
+ "@veloxts/core": "0.6.84"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@vitest/coverage-v8": "4.0.16",