@simtlix/simfinity-js 2.1.0 → 2.2.0

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/README.md CHANGED
@@ -1101,9 +1101,117 @@ mutation {
1101
1101
 
1102
1102
  ## ✅ Validations
1103
1103
 
1104
- ### Field-Level Validations
1104
+ ### Declarative Validation Helpers
1105
1105
 
1106
- Add validation logic directly to fields:
1106
+ Simfinity.js provides built-in validation helpers to simplify common validation patterns, eliminating verbose boilerplate code.
1107
+
1108
+ #### Using Validators
1109
+
1110
+ ```javascript
1111
+ const { validators } = require('@simtlix/simfinity-js');
1112
+
1113
+ const PersonType = new GraphQLObjectType({
1114
+ name: 'Person',
1115
+ fields: () => ({
1116
+ id: { type: GraphQLID },
1117
+ name: {
1118
+ type: GraphQLString,
1119
+ extensions: {
1120
+ validations: validators.stringLength('Name', 2, 100)
1121
+ }
1122
+ },
1123
+ email: {
1124
+ type: GraphQLString,
1125
+ extensions: {
1126
+ validations: validators.email()
1127
+ }
1128
+ },
1129
+ website: {
1130
+ type: GraphQLString,
1131
+ extensions: {
1132
+ validations: validators.url()
1133
+ }
1134
+ },
1135
+ age: {
1136
+ type: GraphQLInt,
1137
+ extensions: {
1138
+ validations: validators.numberRange('Age', 0, 120)
1139
+ }
1140
+ },
1141
+ price: {
1142
+ type: GraphQLFloat,
1143
+ extensions: {
1144
+ validations: validators.positive('Price')
1145
+ }
1146
+ }
1147
+ })
1148
+ });
1149
+ ```
1150
+
1151
+ #### Available Validators
1152
+
1153
+ **String Validators:**
1154
+ - `validators.stringLength(name, min, max)` - Validates string length with min/max bounds (required for CREATE)
1155
+ - `validators.maxLength(name, max)` - Validates maximum string length
1156
+ - `validators.pattern(name, regex, message)` - Validates against a regex pattern
1157
+ - `validators.email()` - Validates email format
1158
+ - `validators.url()` - Validates URL format
1159
+
1160
+ **Number Validators:**
1161
+ - `validators.numberRange(name, min, max)` - Validates number range
1162
+ - `validators.positive(name)` - Ensures number is positive
1163
+
1164
+ **Array Validators:**
1165
+ - `validators.arrayLength(name, maxItems, itemValidator)` - Validates array length and optionally each item
1166
+
1167
+ **Date Validators:**
1168
+ - `validators.dateFormat(name, format)` - Validates date format
1169
+ - `validators.futureDate(name)` - Ensures date is in the future
1170
+
1171
+ #### Validator Features
1172
+
1173
+ - **Automatic Operation Handling**: Validators work for both `CREATE` (save) and `UPDATE` operations
1174
+ - **Smart Validation**: For CREATE operations, values are required. For UPDATE operations, undefined/null values are allowed (field might not be updated)
1175
+ - **Consistent Error Messages**: All validators throw `SimfinityError` with appropriate messages
1176
+
1177
+ #### Example: Multiple Validators
1178
+
1179
+ ```javascript
1180
+ const ProductType = new GraphQLObjectType({
1181
+ name: 'Product',
1182
+ fields: () => ({
1183
+ id: { type: GraphQLID },
1184
+ name: {
1185
+ type: GraphQLString,
1186
+ extensions: {
1187
+ validations: validators.stringLength('Product Name', 3, 200)
1188
+ }
1189
+ },
1190
+ sku: {
1191
+ type: GraphQLString,
1192
+ extensions: {
1193
+ validations: validators.pattern('SKU', /^[A-Z0-9-]+$/, 'SKU must be uppercase alphanumeric with hyphens')
1194
+ }
1195
+ },
1196
+ price: {
1197
+ type: GraphQLFloat,
1198
+ extensions: {
1199
+ validations: validators.positive('Price')
1200
+ }
1201
+ },
1202
+ tags: {
1203
+ type: new GraphQLList(GraphQLString),
1204
+ extensions: {
1205
+ validations: validators.arrayLength('Tags', 10)
1206
+ }
1207
+ }
1208
+ })
1209
+ });
1210
+ ```
1211
+
1212
+ ### Field-Level Validations (Manual)
1213
+
1214
+ For custom validation logic, you can still write manual validators:
1107
1215
 
1108
1216
  ```javascript
1109
1217
  const { SimfinityError } = require('@simtlix/simfinity-js');
@@ -1189,7 +1297,78 @@ const OrderType = new GraphQLObjectType({
1189
1297
 
1190
1298
  ### Custom Validated Scalar Types
1191
1299
 
1192
- Create custom scalar types with built-in validation. The generated type names follow the pattern `{name}_{baseScalarTypeName}`:
1300
+ Create custom scalar types with built-in validation. The generated type names follow the pattern `{name}_{baseScalarTypeName}`.
1301
+
1302
+ #### Pre-built Scalars
1303
+
1304
+ Simfinity.js provides ready-to-use validated scalars for common patterns:
1305
+
1306
+ ```javascript
1307
+ const { scalars } = require('@simtlix/simfinity-js');
1308
+
1309
+ const UserType = new GraphQLObjectType({
1310
+ name: 'User',
1311
+ fields: () => ({
1312
+ id: { type: GraphQLID },
1313
+ email: { type: scalars.EmailScalar }, // Type name: Email_String
1314
+ website: { type: scalars.URLScalar }, // Type name: URL_String
1315
+ age: { type: scalars.PositiveIntScalar }, // Type name: PositiveInt_Int
1316
+ price: { type: scalars.PositiveFloatScalar } // Type name: PositiveFloat_Float
1317
+ }),
1318
+ });
1319
+ ```
1320
+
1321
+ **Available Pre-built Scalars:**
1322
+ - `scalars.EmailScalar` - Validates email format (`Email_String`)
1323
+ - `scalars.URLScalar` - Validates URL format (`URL_String`)
1324
+ - `scalars.PositiveIntScalar` - Validates positive integers (`PositiveInt_Int`)
1325
+ - `scalars.PositiveFloatScalar` - Validates positive floats (`PositiveFloat_Float`)
1326
+
1327
+ #### Factory Functions for Custom Scalars
1328
+
1329
+ Create custom validated scalars with parameters:
1330
+
1331
+ ```javascript
1332
+ const { scalars } = require('@simtlix/simfinity-js');
1333
+
1334
+ // Create a bounded string scalar (name length between 2-100 characters)
1335
+ const NameScalar = scalars.createBoundedStringScalar('Name', 2, 100);
1336
+
1337
+ // Create a bounded integer scalar (age between 0-120)
1338
+ const AgeScalar = scalars.createBoundedIntScalar('Age', 0, 120);
1339
+
1340
+ // Create a bounded float scalar (rating between 0-10)
1341
+ const RatingScalar = scalars.createBoundedFloatScalar('Rating', 0, 10);
1342
+
1343
+ // Create a pattern-based string scalar (phone number format)
1344
+ const PhoneScalar = scalars.createPatternStringScalar(
1345
+ 'Phone',
1346
+ /^\+?[\d\s\-()]+$/,
1347
+ 'Invalid phone number format'
1348
+ );
1349
+
1350
+ // Use in your types
1351
+ const PersonType = new GraphQLObjectType({
1352
+ name: 'Person',
1353
+ fields: () => ({
1354
+ id: { type: GraphQLID },
1355
+ name: { type: NameScalar }, // Type name: Name_String
1356
+ age: { type: AgeScalar }, // Type name: Age_Int
1357
+ rating: { type: RatingScalar }, // Type name: Rating_Float
1358
+ phone: { type: PhoneScalar } // Type name: Phone_String
1359
+ }),
1360
+ });
1361
+ ```
1362
+
1363
+ **Available Factory Functions:**
1364
+ - `scalars.createBoundedStringScalar(name, min, max)` - String with length bounds
1365
+ - `scalars.createBoundedIntScalar(name, min, max)` - Integer with range validation
1366
+ - `scalars.createBoundedFloatScalar(name, min, max)` - Float with range validation
1367
+ - `scalars.createPatternStringScalar(name, pattern, message)` - String with regex pattern validation
1368
+
1369
+ #### Creating Custom Scalars Manually
1370
+
1371
+ You can also create custom scalars using `createValidatedScalar` directly:
1193
1372
 
1194
1373
  ```javascript
1195
1374
  const { GraphQLString, GraphQLInt } = require('graphql');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-js",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -2075,6 +2075,9 @@ export const addNoEndpointType = (gqltype) => {
2075
2075
 
2076
2076
  export { createValidatedScalar };
2077
2077
 
2078
+ export { default as validators } from './validators.js';
2079
+ export { default as scalars } from './scalars.js';
2080
+
2078
2081
  const createArgsForQuery = (argTypes) => {
2079
2082
  const argsObject = {};
2080
2083
 
package/src/scalars.js ADDED
@@ -0,0 +1,188 @@
1
+ import {
2
+ GraphQLString, GraphQLInt, GraphQLFloat,
3
+ } from 'graphql';
4
+ import { createValidatedScalar } from './index.js';
5
+
6
+ /**
7
+ * Email scalar - validates email format
8
+ * Type name: Email_String
9
+ */
10
+ export const EmailScalar = createValidatedScalar(
11
+ 'Email',
12
+ 'A valid email address',
13
+ GraphQLString,
14
+ (value) => {
15
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
16
+ if (!emailRegex.test(value)) {
17
+ throw new Error('Invalid email format');
18
+ }
19
+ },
20
+ );
21
+
22
+ /**
23
+ * URL scalar - validates URL format
24
+ * Type name: URL_String
25
+ */
26
+ export const URLScalar = createValidatedScalar(
27
+ 'URL',
28
+ 'A valid URL',
29
+ GraphQLString,
30
+ (value) => {
31
+ try {
32
+ new URL(value);
33
+ } catch {
34
+ throw new Error('Invalid URL format');
35
+ }
36
+ },
37
+ );
38
+
39
+ /**
40
+ * PositiveInt scalar - validates positive integers
41
+ * Type name: PositiveInt_Int
42
+ */
43
+ export const PositiveIntScalar = createValidatedScalar(
44
+ 'PositiveInt',
45
+ 'A positive integer',
46
+ GraphQLInt,
47
+ (value) => {
48
+ if (value <= 0) {
49
+ throw new Error('Value must be positive');
50
+ }
51
+ },
52
+ );
53
+
54
+ /**
55
+ * PositiveFloat scalar - validates positive floats
56
+ * Type name: PositiveFloat_Float
57
+ */
58
+ export const PositiveFloatScalar = createValidatedScalar(
59
+ 'PositiveFloat',
60
+ 'A positive float',
61
+ GraphQLFloat,
62
+ (value) => {
63
+ if (value <= 0) {
64
+ throw new Error('Value must be positive');
65
+ }
66
+ },
67
+ );
68
+
69
+ /**
70
+ * Factory function to create a bounded string scalar
71
+ * @param {string} name - Name for the scalar
72
+ * @param {number} min - Minimum length
73
+ * @param {number} max - Maximum length
74
+ * @returns {GraphQLScalarType} A scalar type with length validation
75
+ */
76
+ export const createBoundedStringScalar = (name, min, max) => {
77
+ return createValidatedScalar(
78
+ name,
79
+ `A string with length between ${min} and ${max} characters`,
80
+ GraphQLString,
81
+ (value) => {
82
+ if (typeof value !== 'string') {
83
+ throw new Error('Value must be a string');
84
+ }
85
+ if (min !== undefined && value.length < min) {
86
+ throw new Error(`String must be at least ${min} characters`);
87
+ }
88
+ if (max !== undefined && value.length > max) {
89
+ throw new Error(`String must be at most ${max} characters`);
90
+ }
91
+ },
92
+ );
93
+ };
94
+
95
+ /**
96
+ * Factory function to create a bounded integer scalar
97
+ * @param {string} name - Name for the scalar
98
+ * @param {number} min - Minimum value
99
+ * @param {number} max - Maximum value
100
+ * @returns {GraphQLScalarType} A scalar type with range validation
101
+ */
102
+ export const createBoundedIntScalar = (name, min, max) => {
103
+ return createValidatedScalar(
104
+ name,
105
+ `An integer between ${min} and ${max}`,
106
+ GraphQLInt,
107
+ (value) => {
108
+ if (typeof value !== 'number' || isNaN(value)) {
109
+ throw new Error('Value must be a number');
110
+ }
111
+ if (min !== undefined && value < min) {
112
+ throw new Error(`Value must be at least ${min}`);
113
+ }
114
+ if (max !== undefined && value > max) {
115
+ throw new Error(`Value must be at most ${max}`);
116
+ }
117
+ },
118
+ );
119
+ };
120
+
121
+ /**
122
+ * Factory function to create a bounded float scalar
123
+ * @param {string} name - Name for the scalar
124
+ * @param {number} min - Minimum value
125
+ * @param {number} max - Maximum value
126
+ * @returns {GraphQLScalarType} A scalar type with range validation
127
+ */
128
+ export const createBoundedFloatScalar = (name, min, max) => {
129
+ return createValidatedScalar(
130
+ name,
131
+ `A float between ${min} and ${max}`,
132
+ GraphQLFloat,
133
+ (value) => {
134
+ if (typeof value !== 'number' || isNaN(value)) {
135
+ throw new Error('Value must be a number');
136
+ }
137
+ if (min !== undefined && value < min) {
138
+ throw new Error(`Value must be at least ${min}`);
139
+ }
140
+ if (max !== undefined && value > max) {
141
+ throw new Error(`Value must be at most ${max}`);
142
+ }
143
+ },
144
+ );
145
+ };
146
+
147
+ /**
148
+ * Factory function to create a regex pattern string scalar
149
+ * @param {string} name - Name for the scalar
150
+ * @param {RegExp|string} pattern - Regex pattern to validate against
151
+ * @param {string} message - Error message if validation fails
152
+ * @returns {GraphQLScalarType} A scalar type with pattern validation
153
+ */
154
+ export const createPatternStringScalar = (name, pattern, message) => {
155
+ const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
156
+ const errorMessage = message || 'Value does not match required pattern';
157
+
158
+ return createValidatedScalar(
159
+ name,
160
+ `A string matching the pattern: ${pattern}`,
161
+ GraphQLString,
162
+ (value) => {
163
+ if (typeof value !== 'string') {
164
+ throw new Error('Value must be a string');
165
+ }
166
+ if (!regex.test(value)) {
167
+ throw new Error(errorMessage);
168
+ }
169
+ },
170
+ );
171
+ };
172
+
173
+ // Export all scalars as an object for convenience
174
+ const scalars = {
175
+ // Pre-built scalars
176
+ EmailScalar,
177
+ URLScalar,
178
+ PositiveIntScalar,
179
+ PositiveFloatScalar,
180
+ // Factory functions
181
+ createBoundedStringScalar,
182
+ createBoundedIntScalar,
183
+ createBoundedFloatScalar,
184
+ createPatternStringScalar,
185
+ };
186
+
187
+ export default scalars;
188
+
@@ -0,0 +1,250 @@
1
+ import SimfinityError from './errors/simfinity.error.js';
2
+
3
+ /**
4
+ * Creates a validation object that works for both 'save' (CREATE) and 'update' (UPDATE) operations.
5
+ * The validators will be applied to both operations.
6
+ * For CREATE operations, the value must be provided and valid.
7
+ * For UPDATE operations, undefined/null values are allowed (field might not be updated),
8
+ * but if a value is provided, it must be valid.
9
+ */
10
+ const createValidator = (validatorFn, required = false) => {
11
+ // Validator for CREATE operations - value is required if required=true
12
+ const validateCreate = async (typeName, fieldName, value, session) => {
13
+ if (required && (value === null || value === undefined)) {
14
+ throw new SimfinityError(`${fieldName} is required`, 'VALIDATION_ERROR', 400);
15
+ }
16
+ if (value !== null && value !== undefined) {
17
+ await validatorFn(typeName, fieldName, value, session);
18
+ }
19
+ };
20
+
21
+ // Validator for UPDATE operations - value is optional
22
+ const validateUpdate = async (typeName, fieldName, value, session) => {
23
+ // Skip validation if value is not provided (field is not being updated)
24
+ if (value === null || value === undefined) {
25
+ return;
26
+ }
27
+ // If value is provided, validate it
28
+ await validatorFn(typeName, fieldName, value, session);
29
+ };
30
+
31
+ const validatorCreate = { validate: validateCreate };
32
+ const validatorUpdate = { validate: validateUpdate };
33
+
34
+ // Return validations for both CREATE and UPDATE operations
35
+ // Also support 'save'/'update' for backward compatibility (though code uses CREATE/UPDATE)
36
+ return {
37
+ CREATE: [validatorCreate],
38
+ UPDATE: [validatorUpdate],
39
+ save: [validatorCreate], // For backward compatibility
40
+ update: [validatorUpdate], // For backward compatibility
41
+ };
42
+ };
43
+
44
+ /**
45
+ * String validators
46
+ */
47
+ export const stringLength = (name, min, max) => {
48
+ return createValidator(async (typeName, fieldName, value) => {
49
+ if (typeof value !== 'string') {
50
+ throw new SimfinityError(`${name} must be a string`, 'VALIDATION_ERROR', 400);
51
+ }
52
+
53
+ if (min !== undefined && value.length < min) {
54
+ throw new SimfinityError(`${name} must be at least ${min} characters`, 'VALIDATION_ERROR', 400);
55
+ }
56
+
57
+ if (max !== undefined && value.length > max) {
58
+ throw new SimfinityError(`${name} must be at most ${max} characters`, 'VALIDATION_ERROR', 400);
59
+ }
60
+ }, true); // Required for CREATE operations
61
+ };
62
+
63
+ export const maxLength = (name, max) => {
64
+ return createValidator(async (typeName, fieldName, value) => {
65
+ if (typeof value !== 'string') {
66
+ throw new SimfinityError(`${name} must be a string`, 'VALIDATION_ERROR', 400);
67
+ }
68
+
69
+ if (value.length > max) {
70
+ throw new SimfinityError(`${name} must be at most ${max} characters`, 'VALIDATION_ERROR', 400);
71
+ }
72
+ }, false); // Optional
73
+ };
74
+
75
+ export const pattern = (name, regex, message) => {
76
+ const regexObj = typeof regex === 'string' ? new RegExp(regex) : regex;
77
+ const errorMessage = message || `${name} format is invalid`;
78
+
79
+ return createValidator(async (typeName, fieldName, value) => {
80
+ if (typeof value !== 'string') {
81
+ throw new SimfinityError(`${name} must be a string`, 'VALIDATION_ERROR', 400);
82
+ }
83
+
84
+ if (!regexObj.test(value)) {
85
+ throw new SimfinityError(errorMessage, 'VALIDATION_ERROR', 400);
86
+ }
87
+ }, false); // Optional
88
+ };
89
+
90
+ export const email = () => {
91
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
92
+
93
+ return createValidator(async (typeName, fieldName, value) => {
94
+ if (typeof value !== 'string') {
95
+ throw new SimfinityError('Email must be a string', 'VALIDATION_ERROR', 400);
96
+ }
97
+
98
+ if (!emailRegex.test(value)) {
99
+ throw new SimfinityError('Invalid email format', 'VALIDATION_ERROR', 400);
100
+ }
101
+ }, false); // Optional
102
+ };
103
+
104
+ export const url = () => {
105
+ return createValidator(async (typeName, fieldName, value) => {
106
+ if (typeof value !== 'string') {
107
+ throw new SimfinityError('URL must be a string', 'VALIDATION_ERROR', 400);
108
+ }
109
+
110
+ try {
111
+ // Use URL constructor for better validation
112
+ new URL(value);
113
+ } catch (e) {
114
+ console.log('Invalid URL format', e);
115
+ throw new SimfinityError('Invalid URL format', 'VALIDATION_ERROR', 400);
116
+ }
117
+ }, false); // Optional
118
+ };
119
+
120
+ /**
121
+ * Number validators
122
+ */
123
+ export const numberRange = (name, min, max) => {
124
+ return createValidator(async (typeName, fieldName, value) => {
125
+ if (typeof value !== 'number' || isNaN(value)) {
126
+ throw new SimfinityError(`${name} must be a number`, 'VALIDATION_ERROR', 400);
127
+ }
128
+
129
+ if (min !== undefined && value < min) {
130
+ throw new SimfinityError(`${name} must be at least ${min}`, 'VALIDATION_ERROR', 400);
131
+ }
132
+
133
+ if (max !== undefined && value > max) {
134
+ throw new SimfinityError(`${name} must be at most ${max}`, 'VALIDATION_ERROR', 400);
135
+ }
136
+ }, false); // Optional
137
+ };
138
+
139
+ export const positive = (name) => {
140
+ return createValidator(async (typeName, fieldName, value) => {
141
+ if (typeof value !== 'number' || isNaN(value)) {
142
+ throw new SimfinityError(`${name} must be a number`, 'VALIDATION_ERROR', 400);
143
+ }
144
+
145
+ if (value <= 0) {
146
+ throw new SimfinityError(`${name} must be positive`, 'VALIDATION_ERROR', 400);
147
+ }
148
+ }, false); // Optional
149
+ };
150
+
151
+ /**
152
+ * Array validators
153
+ */
154
+ export const arrayLength = (name, maxItems, itemValidator) => {
155
+ return createValidator(async (typeName, fieldName, value, session) => {
156
+ if (!Array.isArray(value)) {
157
+ throw new SimfinityError(`${name} must be an array`, 'VALIDATION_ERROR', 400);
158
+ }
159
+
160
+ if (maxItems !== undefined && value.length > maxItems) {
161
+ throw new SimfinityError(`${name} must have at most ${maxItems} items`, 'VALIDATION_ERROR', 400);
162
+ }
163
+
164
+ // If itemValidator is provided, validate each item
165
+ if (itemValidator && Array.isArray(itemValidator)) {
166
+ for (let i = 0; i < value.length; i++) {
167
+ for (const validator of itemValidator) {
168
+ await validator.validate(typeName, fieldName, value[i], session);
169
+ }
170
+ }
171
+ }
172
+ }, false); // Optional
173
+ };
174
+
175
+ /**
176
+ * Date validators
177
+ */
178
+ export const dateFormat = (name, format) => {
179
+ return createValidator(async (typeName, fieldName, value) => {
180
+ // Handle Date objects, ISO strings, and timestamps
181
+ let date;
182
+ if (value instanceof Date) {
183
+ date = value;
184
+ } else if (typeof value === 'string') {
185
+ date = new Date(value);
186
+ } else if (typeof value === 'number') {
187
+ date = new Date(value);
188
+ } else {
189
+ throw new SimfinityError(`${name} must be a valid date`, 'VALIDATION_ERROR', 400);
190
+ }
191
+
192
+ if (isNaN(date.getTime())) {
193
+ throw new SimfinityError(`${name} must be a valid date`, 'VALIDATION_ERROR', 400);
194
+ }
195
+
196
+ // If format is provided, validate format
197
+ if (format && typeof value === 'string') {
198
+ // Simple format validation - can be enhanced
199
+ const formatRegex = /^\d{4}-\d{2}-\d{2}$/; // YYYY-MM-DD
200
+ if (format === 'YYYY-MM-DD' && !formatRegex.test(value)) {
201
+ throw new SimfinityError(`${name} must be in format ${format}`, 'VALIDATION_ERROR', 400);
202
+ }
203
+ // Add more format patterns as needed
204
+ }
205
+ }, false); // Optional
206
+ };
207
+
208
+ export const futureDate = (name) => {
209
+ return createValidator(async (typeName, fieldName, value) => {
210
+ let date;
211
+ if (value instanceof Date) {
212
+ date = value;
213
+ } else if (typeof value === 'string') {
214
+ date = new Date(value);
215
+ } else if (typeof value === 'number') {
216
+ date = new Date(value);
217
+ } else {
218
+ throw new SimfinityError(`${name} must be a valid date`, 'VALIDATION_ERROR', 400);
219
+ }
220
+
221
+ if (isNaN(date.getTime())) {
222
+ throw new SimfinityError(`${name} must be a valid date`, 'VALIDATION_ERROR', 400);
223
+ }
224
+
225
+ if (date <= new Date()) {
226
+ throw new SimfinityError(`${name} must be a future date`, 'VALIDATION_ERROR', 400);
227
+ }
228
+ }, false); // Optional
229
+ };
230
+
231
+ // Export all validators as an object
232
+ const validators = {
233
+ // String validators
234
+ stringLength,
235
+ maxLength,
236
+ pattern,
237
+ email,
238
+ url,
239
+ // Number validators
240
+ numberRange,
241
+ positive,
242
+ // Array validators
243
+ arrayLength,
244
+ // Date validators
245
+ dateFormat,
246
+ futureDate,
247
+ };
248
+
249
+ export default validators;
250
+