@veloxts/validation 0.7.1 → 0.7.3

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.7.3
4
+
5
+ ### Patch Changes
6
+
7
+ - feat(cli): auto-populate Zod schemas from Prisma model fields
8
+ - Updated dependencies
9
+ - @veloxts/core@0.7.3
10
+
11
+ ## 0.7.2
12
+
13
+ ### Patch Changes
14
+
15
+ - simplify code for clarity and maintainability
16
+ - Updated dependencies
17
+ - @veloxts/core@0.7.2
18
+
3
19
  ## 0.7.1
4
20
 
5
21
  ### Patch Changes
package/GUIDE.md CHANGED
@@ -200,13 +200,13 @@ const UserSchema = resourceSchema()
200
200
  .build();
201
201
 
202
202
  // Returns different fields based on access level
203
- const publicUser = resource(user, UserSchema).forAnonymous();
203
+ const publicUser = resource(user, UserSchema.public);
204
204
  // Type: { id: string; name: string }
205
205
 
206
- const authUser = resource(user, UserSchema).forAuthenticated();
206
+ const authUser = resource(user, UserSchema.authenticated);
207
207
  // Type: { id: string; name: string; email: string }
208
208
 
209
- const adminUser = resource(user, UserSchema).forAdmin();
209
+ const adminUser = resource(user, UserSchema.admin);
210
210
  // Type: { id: string; name: string; email: string; internalNotes: string | null }
211
211
  ```
212
212
 
@@ -7,7 +7,7 @@
7
7
  * @module middleware
8
8
  */
9
9
  import { ValidationError } from '@veloxts/core';
10
- import { ZodError, type ZodType } from 'zod';
10
+ import type { ZodError, ZodType } from 'zod';
11
11
  import type { AnySchema, AnyZodSchema, SafeParseResult } from './types.js';
12
12
  /**
13
13
  * Transforms Zod validation issues into a field-error map
@@ -7,11 +7,27 @@
7
7
  * @module middleware
8
8
  */
9
9
  import { ValidationError } from '@veloxts/core';
10
- import { ZodError } from 'zod';
11
10
  import { isSchema, isZodSchema } from './types.js';
12
11
  // ============================================================================
13
12
  // Error Transformation
14
13
  // ============================================================================
14
+ /**
15
+ * Filters symbol keys from a Zod issue path, keeping only string and number segments.
16
+ * Zod 4 issue paths use `PropertyKey[]` which may include symbols.
17
+ */
18
+ function filterIssuePath(path) {
19
+ return path.filter((p) => typeof p !== 'symbol');
20
+ }
21
+ /**
22
+ * Converts Zod issues into framework-friendly ValidationIssue objects
23
+ */
24
+ function zodIssuesToValidationIssues(issues) {
25
+ return issues.map((issue) => ({
26
+ path: filterIssuePath(issue.path),
27
+ message: issue.message,
28
+ code: issue.code,
29
+ }));
30
+ }
15
31
  /**
16
32
  * Transforms Zod validation issues into a field-error map
17
33
  *
@@ -22,7 +38,6 @@ export function formatZodErrors(issues) {
22
38
  const fields = {};
23
39
  for (const issue of issues) {
24
40
  const path = issue.path.join('.') || '_root';
25
- // Only keep the first error for each field
26
41
  if (!(path in fields)) {
27
42
  fields[path] = issue.message;
28
43
  }
@@ -37,12 +52,8 @@ export function formatZodErrors(issues) {
37
52
  * @returns ValidationError with field-specific errors
38
53
  */
39
54
  export function zodErrorToValidationError(error, customMessage) {
40
- const fields = formatZodErrors(error.issues.map((i) => ({
41
- path: i.path.filter((p) => typeof p !== 'symbol'),
42
- message: i.message,
43
- })));
44
- const message = customMessage ?? 'Validation failed';
45
- return new ValidationError(message, fields);
55
+ const fields = formatZodErrors(zodIssuesToValidationIssues(error.issues));
56
+ return new ValidationError(customMessage ?? 'Validation failed', fields);
46
57
  }
47
58
  // ============================================================================
48
59
  // Validation Functions
@@ -68,28 +79,21 @@ export function zodErrorToValidationError(error, customMessage) {
68
79
  * ```
69
80
  */
70
81
  export function parse(schema, data, errorMessage) {
71
- try {
72
- if (isSchema(schema)) {
73
- const result = schema.safeParse(data);
74
- if (result.success) {
75
- return result.data;
76
- }
77
- throw new ValidationError(errorMessage ?? 'Validation failed', formatZodErrors(result.error));
78
- }
79
- if (isZodSchema(schema)) {
80
- return schema.parse(data);
82
+ if (isSchema(schema)) {
83
+ const result = schema.safeParse(data);
84
+ if (result.success) {
85
+ return result.data;
81
86
  }
82
- throw new Error('Invalid schema provided to parse()');
87
+ throw new ValidationError(errorMessage ?? 'Validation failed', formatZodErrors(result.error));
83
88
  }
84
- catch (error) {
85
- if (error instanceof ValidationError) {
86
- throw error;
87
- }
88
- if (error instanceof ZodError) {
89
- throw zodErrorToValidationError(error, errorMessage);
89
+ if (isZodSchema(schema)) {
90
+ const result = schema.safeParse(data);
91
+ if (result.success) {
92
+ return result.data;
90
93
  }
91
- throw error;
94
+ throw zodErrorToValidationError(result.error, errorMessage);
92
95
  }
96
+ throw new Error('Invalid schema provided to parse()');
93
97
  }
94
98
  /**
95
99
  * Safely parses data without throwing
@@ -116,18 +120,13 @@ export function safeParse(schema, data) {
116
120
  return schema.safeParse(data);
117
121
  }
118
122
  if (isZodSchema(schema)) {
119
- const zodSchema = schema;
120
- const result = zodSchema.safeParse(data);
123
+ const result = schema.safeParse(data);
121
124
  if (result.success) {
122
125
  return { success: true, data: result.data };
123
126
  }
124
127
  return {
125
128
  success: false,
126
- error: result.error.issues.map((issue) => ({
127
- path: issue.path.filter((p) => typeof p !== 'symbol'),
128
- message: issue.message,
129
- code: issue.code,
130
- })),
129
+ error: zodIssuesToValidationIssues(result.error.issues),
131
130
  };
132
131
  }
133
132
  return {
package/dist/naming.js CHANGED
@@ -76,7 +76,5 @@ export function inferMethodFromName(procedureName) {
76
76
  * ```
77
77
  */
78
78
  export function isQueryProcedure(procedureName) {
79
- return (procedureName.startsWith('get') ||
80
- procedureName.startsWith('list') ||
81
- procedureName.startsWith('find'));
79
+ return inferMethodFromName(procedureName) === 'GET';
82
80
  }
@@ -8,6 +8,7 @@
8
8
  * @module schemas/query
9
9
  */
10
10
  import { z } from 'zod';
11
+ import { createPaginationSchema } from './pagination.js';
11
12
  export function queryNumber(defaultValue) {
12
13
  const base = z.coerce.number();
13
14
  return defaultValue !== undefined ? base.default(defaultValue) : base;
@@ -88,19 +89,23 @@ export function queryArray(options = {}) {
88
89
  .filter(Boolean));
89
90
  // Apply min/max constraints via refinement if needed
90
91
  if (min !== undefined || max !== undefined) {
92
+ let message;
93
+ if (min !== undefined && max !== undefined) {
94
+ message = `Array must have ${min}-${max} items`;
95
+ }
96
+ else if (min !== undefined) {
97
+ message = `Array must have at least ${min} item(s)`;
98
+ }
99
+ else {
100
+ message = `Array must have at most ${max} item(s)`;
101
+ }
91
102
  return baseSchema.refine((arr) => {
92
103
  if (min !== undefined && arr.length < min)
93
104
  return false;
94
105
  if (max !== undefined && arr.length > max)
95
106
  return false;
96
107
  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
- });
108
+ }, { message });
104
109
  }
105
110
  return baseSchema;
106
111
  }
@@ -140,9 +145,5 @@ export function queryEnum(values, defaultValue) {
140
145
  * ```
141
146
  */
142
147
  export function pagination(options = {}) {
143
- const { defaultPage = 1, defaultLimit = 20, maxLimit = 100 } = options;
144
- return z.object({
145
- page: z.coerce.number().int().positive().default(defaultPage),
146
- limit: z.coerce.number().int().positive().max(maxLimit).default(defaultLimit),
147
- });
148
+ return createPaginationSchema(options);
148
149
  }
@@ -10,6 +10,29 @@ import { z } from 'zod';
10
10
  // ============================================================================
11
11
  // Prisma Decimal Helpers
12
12
  // ============================================================================
13
+ /**
14
+ * Converts a Prisma Decimal, number, or string to a number.
15
+ * Shared conversion logic for all prismaDecimal variants.
16
+ */
17
+ function coerceToNumber(val) {
18
+ if (typeof val === 'object' &&
19
+ val !== null &&
20
+ 'toNumber' in val &&
21
+ typeof val.toNumber === 'function') {
22
+ return val.toNumber();
23
+ }
24
+ if (typeof val === 'number') {
25
+ return val;
26
+ }
27
+ if (typeof val === 'string') {
28
+ const num = Number.parseFloat(val);
29
+ if (Number.isNaN(num)) {
30
+ throw new Error(`Cannot convert "${val}" to number`);
31
+ }
32
+ return num;
33
+ }
34
+ throw new Error(`Cannot convert ${typeof val} to number`);
35
+ }
13
36
  /**
14
37
  * Creates a field that accepts Prisma Decimal and transforms to number
15
38
  *
@@ -30,23 +53,7 @@ export function prismaDecimal() {
30
53
  if (val === null || val === undefined) {
31
54
  throw new Error('Expected Decimal, got null/undefined');
32
55
  }
33
- // Prisma Decimal has toNumber() method
34
- if (typeof val === 'object' && 'toNumber' in val && typeof val.toNumber === 'function') {
35
- return val.toNumber();
36
- }
37
- // Already a number
38
- if (typeof val === 'number') {
39
- return val;
40
- }
41
- // String (from JSON)
42
- if (typeof val === 'string') {
43
- const num = Number.parseFloat(val);
44
- if (Number.isNaN(num)) {
45
- throw new Error(`Cannot convert "${val}" to number`);
46
- }
47
- return num;
48
- }
49
- throw new Error(`Cannot convert ${typeof val} to number`);
56
+ return coerceToNumber(val);
50
57
  });
51
58
  }
52
59
  /**
@@ -59,20 +66,7 @@ export function prismaDecimalNullable() {
59
66
  if (val === null || val === undefined) {
60
67
  return null;
61
68
  }
62
- if (typeof val === 'object' && 'toNumber' in val && typeof val.toNumber === 'function') {
63
- return val.toNumber();
64
- }
65
- if (typeof val === 'number') {
66
- return val;
67
- }
68
- if (typeof val === 'string') {
69
- const num = Number.parseFloat(val);
70
- if (Number.isNaN(num)) {
71
- throw new Error(`Cannot convert "${val}" to number`);
72
- }
73
- return num;
74
- }
75
- throw new Error(`Cannot convert ${typeof val} to number`);
69
+ return coerceToNumber(val);
76
70
  });
77
71
  }
78
72
  /**
@@ -85,20 +79,7 @@ export function prismaDecimalOptional() {
85
79
  if (val === null || val === undefined) {
86
80
  return undefined;
87
81
  }
88
- if (typeof val === 'object' && 'toNumber' in val && typeof val.toNumber === 'function') {
89
- return val.toNumber();
90
- }
91
- if (typeof val === 'number') {
92
- return val;
93
- }
94
- if (typeof val === 'string') {
95
- const num = Number.parseFloat(val);
96
- if (Number.isNaN(num)) {
97
- throw new Error(`Cannot convert "${val}" to number`);
98
- }
99
- return num;
100
- }
101
- throw new Error(`Cannot convert ${typeof val} to number`);
82
+ return coerceToNumber(val);
102
83
  });
103
84
  }
104
85
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/validation",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
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": "4.3.6",
26
- "@veloxts/core": "0.7.1"
26
+ "@veloxts/core": "0.7.3"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@vitest/coverage-v8": "4.0.18",