@simtlix/simfinity-js 2.1.0 → 2.3.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 +627 -14
- package/package.json +1 -1
- package/src/index.js +102 -35
- package/src/scalars.js +188 -0
- package/src/validators.js +250 -0
|
@@ -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
|
+
|