taro-bluetooth-print 2.9.0 → 2.9.2
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 -1
- package/README.md +53 -4
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/types/core/di/Container.d.ts +84 -0
- package/dist/types/core/di/Tokens.d.ts +29 -0
- package/dist/types/core/di/index.d.ts +3 -0
- package/dist/types/core/event/EventBus.d.ts +66 -0
- package/dist/types/core/event/index.d.ts +2 -0
- package/dist/types/core/index.d.ts +5 -4
- package/dist/types/core/plugin/PluginManager.d.ts +64 -0
- package/dist/types/core/plugin/index.d.ts +2 -0
- package/dist/types/device/MultiPrinterManager.d.ts +2 -0
- package/dist/types/factory/di-factory.d.ts +52 -0
- package/dist/types/index.d.ts +5 -1
- package/dist/types/providers/ServiceProvider.d.ts +56 -0
- package/dist/types/providers/index.d.ts +2 -0
- package/dist/types/template/TemplateEngine.d.ts +24 -68
- package/dist/types/template/engines/TemplateRenderer.d.ts +71 -0
- package/dist/types/template/parsers/TemplateParser.d.ts +23 -0
- package/dist/types/utils/index.d.ts +8 -0
- package/dist/types/utils/logger.d.ts +4 -3
- package/dist/types/utils/outputLimiter.d.ts +87 -0
- package/dist/types/utils/validation.d.ts +11 -309
- package/dist/types/utils/validators/array.d.ts +19 -0
- package/dist/types/utils/validators/buffer.d.ts +18 -0
- package/dist/types/utils/validators/chain.d.ts +31 -0
- package/dist/types/utils/validators/common.d.ts +22 -0
- package/dist/types/utils/validators/number.d.ts +20 -0
- package/dist/types/utils/validators/object.d.ts +24 -0
- package/dist/types/utils/validators/printer.d.ts +40 -0
- package/dist/types/utils/validators/types.d.ts +125 -0
- package/dist/types/utils/validators/uuid.d.ts +23 -0
- package/package.json +1 -1
- package/src/core/BluetoothPrinter.ts +2 -1
- package/src/core/di/Container.ts +332 -0
- package/src/core/di/Tokens.ts +45 -0
- package/src/core/di/index.ts +3 -0
- package/src/core/event/EventBus.ts +251 -0
- package/src/core/event/index.ts +2 -0
- package/src/core/index.ts +10 -4
- package/src/core/plugin/PluginManager.ts +161 -0
- package/src/core/plugin/index.ts +2 -0
- package/src/device/MultiPrinterManager.ts +15 -6
- package/src/factory/di-factory.ts +61 -0
- package/src/index.ts +50 -1
- package/src/providers/ServiceProvider.ts +213 -0
- package/src/providers/index.ts +2 -0
- package/src/template/TemplateEngine.ts +27 -792
- package/src/template/engines/TemplateRenderer.ts +762 -0
- package/src/template/parsers/TemplateParser.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/logger.ts +17 -4
- package/src/utils/outputLimiter.ts +227 -0
- package/src/utils/validation.ts +21 -1138
- package/src/utils/validators/array.ts +95 -0
- package/src/utils/validators/buffer.ts +81 -0
- package/src/utils/validators/chain.ts +181 -0
- package/src/utils/validators/common.ts +216 -0
- package/src/utils/validators/number.ts +101 -0
- package/src/utils/validators/object.ts +63 -0
- package/src/utils/validators/printer.ts +294 -0
- package/src/utils/validators/types.ts +105 -0
- package/src/utils/validators/uuid.ts +49 -0
package/src/utils/validation.ts
CHANGED
|
@@ -15,1141 +15,24 @@
|
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
errors: ValidationError[];
|
|
40
|
-
/** Warnings (non-fatal issues) */
|
|
41
|
-
warnings?: ValidationError[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Validator function type
|
|
46
|
-
*/
|
|
47
|
-
export type ValidatorFn<T = unknown> = (value: T) => ValidationResult;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Validation rule definition
|
|
51
|
-
*/
|
|
52
|
-
export interface ValidationRule<T = unknown> {
|
|
53
|
-
/** Rule name */
|
|
54
|
-
name: string;
|
|
55
|
-
/** Validation function */
|
|
56
|
-
validate: (value: T) => boolean;
|
|
57
|
-
/** Error message if validation fails */
|
|
58
|
-
message: string;
|
|
59
|
-
/** Error code */
|
|
60
|
-
code: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Common error codes
|
|
65
|
-
*/
|
|
66
|
-
export const ValidationCodes = {
|
|
67
|
-
REQUIRED: 'REQUIRED',
|
|
68
|
-
INVALID_TYPE: 'INVALID_TYPE',
|
|
69
|
-
INVALID_FORMAT: 'INVALID_FORMAT',
|
|
70
|
-
OUT_OF_RANGE: 'OUT_OF_RANGE',
|
|
71
|
-
TOO_SHORT: 'TOO_SHORT',
|
|
72
|
-
TOO_LONG: 'TOO_LONG',
|
|
73
|
-
INVALID_ENUM: 'INVALID_ENUM',
|
|
74
|
-
PATTERN_MISMATCH: 'PATTERN_MISMATCH',
|
|
75
|
-
INVALID_BUFFER: 'INVALID_BUFFER',
|
|
76
|
-
INVALID_ENCODING: 'INVALID_ENCODING',
|
|
77
|
-
EMPTY_ARRAY: 'EMPTY_ARRAY',
|
|
78
|
-
ARRAY_TOO_SHORT: 'ARRAY_TOO_SHORT',
|
|
79
|
-
ARRAY_TOO_LONG: 'ARRAY_TOO_LONG',
|
|
80
|
-
DUPLICATE_VALUE: 'DUPLICATE_VALUE',
|
|
81
|
-
NEGATIVE_VALUE: 'NEGATIVE_VALUE',
|
|
82
|
-
ZERO_VALUE: 'ZERO_VALUE',
|
|
83
|
-
NOT_A_NUMBER: 'NOT_A_NUMBER',
|
|
84
|
-
INFINITY_VALUE: 'INFINITY_VALUE',
|
|
85
|
-
INVALID_JSON: 'INVALID_JSON',
|
|
86
|
-
UNSUPPORTED_VALUE: 'UNSUPPORTED_VALUE',
|
|
87
|
-
INVALID_VERSION: 'INVALID_VERSION',
|
|
88
|
-
} as const;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Common validation rules
|
|
92
|
-
*/
|
|
93
|
-
export const CommonValidators = {
|
|
94
|
-
/** Required string (non-empty) */
|
|
95
|
-
requiredString: (field: string, value: unknown): ValidationError | null => {
|
|
96
|
-
if (value === undefined || value === null || value === '') {
|
|
97
|
-
return {
|
|
98
|
-
field,
|
|
99
|
-
message: `${field} is required`,
|
|
100
|
-
code: ValidationCodes.REQUIRED,
|
|
101
|
-
value,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (typeof value !== 'string') {
|
|
105
|
-
return {
|
|
106
|
-
field,
|
|
107
|
-
message: `${field} must be a string`,
|
|
108
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
109
|
-
value,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
/** Positive number */
|
|
116
|
-
positiveNumber: (field: string, value: unknown): ValidationError | null => {
|
|
117
|
-
if (typeof value !== 'number') {
|
|
118
|
-
return {
|
|
119
|
-
field,
|
|
120
|
-
message: `${field} must be a number`,
|
|
121
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
122
|
-
value,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
if (isNaN(value)) {
|
|
126
|
-
return {
|
|
127
|
-
field,
|
|
128
|
-
message: `${field} must be a valid number`,
|
|
129
|
-
code: ValidationCodes.NOT_A_NUMBER,
|
|
130
|
-
value,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
if (!isFinite(value)) {
|
|
134
|
-
return {
|
|
135
|
-
field,
|
|
136
|
-
message: `${field} must be a finite number`,
|
|
137
|
-
code: ValidationCodes.INFINITY_VALUE,
|
|
138
|
-
value,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (value <= 0) {
|
|
142
|
-
return {
|
|
143
|
-
field,
|
|
144
|
-
message: `${field} must be positive`,
|
|
145
|
-
code: ValidationCodes.NEGATIVE_VALUE,
|
|
146
|
-
value,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
return null;
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
/** Non-negative number */
|
|
153
|
-
nonNegativeNumber: (field: string, value: unknown): ValidationError | null => {
|
|
154
|
-
if (typeof value !== 'number') {
|
|
155
|
-
return {
|
|
156
|
-
field,
|
|
157
|
-
message: `${field} must be a number`,
|
|
158
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
159
|
-
value,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
if (isNaN(value)) {
|
|
163
|
-
return {
|
|
164
|
-
field,
|
|
165
|
-
message: `${field} must be a valid number`,
|
|
166
|
-
code: ValidationCodes.NOT_A_NUMBER,
|
|
167
|
-
value,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
if (value < 0) {
|
|
171
|
-
return {
|
|
172
|
-
field,
|
|
173
|
-
message: `${field} cannot be negative`,
|
|
174
|
-
code: ValidationCodes.NEGATIVE_VALUE,
|
|
175
|
-
value,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
return null;
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
/** Array with items */
|
|
182
|
-
nonEmptyArray: (field: string, value: unknown): ValidationError | null => {
|
|
183
|
-
if (!Array.isArray(value)) {
|
|
184
|
-
return {
|
|
185
|
-
field,
|
|
186
|
-
message: `${field} must be an array`,
|
|
187
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
188
|
-
value,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
if (value.length === 0) {
|
|
192
|
-
return {
|
|
193
|
-
field,
|
|
194
|
-
message: `${field} cannot be empty`,
|
|
195
|
-
code: ValidationCodes.EMPTY_ARRAY,
|
|
196
|
-
value,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
return null;
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
/** In range */
|
|
203
|
-
inRange: (field: string, value: unknown, min: number, max: number): ValidationError | null => {
|
|
204
|
-
if (typeof value !== 'number') {
|
|
205
|
-
return {
|
|
206
|
-
field,
|
|
207
|
-
message: `${field} must be a number`,
|
|
208
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
209
|
-
value,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
if (value < min || value > max) {
|
|
213
|
-
return {
|
|
214
|
-
field,
|
|
215
|
-
message: `${field} must be between ${min} and ${max}`,
|
|
216
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
217
|
-
value,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return null;
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
/** String length */
|
|
224
|
-
stringLength: (
|
|
225
|
-
field: string,
|
|
226
|
-
value: unknown,
|
|
227
|
-
min: number,
|
|
228
|
-
max: number
|
|
229
|
-
): ValidationError | null => {
|
|
230
|
-
if (typeof value !== 'string') {
|
|
231
|
-
return {
|
|
232
|
-
field,
|
|
233
|
-
message: `${field} must be a string`,
|
|
234
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
235
|
-
value,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
if (value.length < min) {
|
|
239
|
-
return {
|
|
240
|
-
field,
|
|
241
|
-
message: `${field} must be at least ${min} characters`,
|
|
242
|
-
code: ValidationCodes.TOO_SHORT,
|
|
243
|
-
value,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
if (value.length > max) {
|
|
247
|
-
return {
|
|
248
|
-
field,
|
|
249
|
-
message: `${field} must be at most ${max} characters`,
|
|
250
|
-
code: ValidationCodes.TOO_LONG,
|
|
251
|
-
value,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
return null;
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
/** Matches pattern */
|
|
258
|
-
matchesPattern: (
|
|
259
|
-
field: string,
|
|
260
|
-
value: unknown,
|
|
261
|
-
pattern: RegExp,
|
|
262
|
-
message?: string
|
|
263
|
-
): ValidationError | null => {
|
|
264
|
-
if (typeof value !== 'string') {
|
|
265
|
-
return {
|
|
266
|
-
field,
|
|
267
|
-
message: `${field} must be a string`,
|
|
268
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
269
|
-
value,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
if (!pattern.test(value)) {
|
|
273
|
-
return {
|
|
274
|
-
field,
|
|
275
|
-
message: message ?? `${field} format is invalid`,
|
|
276
|
-
code: ValidationCodes.PATTERN_MISMATCH,
|
|
277
|
-
value,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
return null;
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
/** Is one of enum values */
|
|
284
|
-
isEnum: <T extends string>(
|
|
285
|
-
field: string,
|
|
286
|
-
value: unknown,
|
|
287
|
-
enumValues: readonly T[]
|
|
288
|
-
): ValidationError | null => {
|
|
289
|
-
if (!enumValues.includes(value as T)) {
|
|
290
|
-
return {
|
|
291
|
-
field,
|
|
292
|
-
message: `${field} must be one of: ${enumValues.join(', ')}`,
|
|
293
|
-
code: ValidationCodes.INVALID_ENUM,
|
|
294
|
-
value,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
return null;
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Printer data validation schema
|
|
303
|
-
*/
|
|
304
|
-
export interface PrinterDataSchema {
|
|
305
|
-
/** Device ID */
|
|
306
|
-
deviceId?: { required?: boolean; pattern?: RegExp };
|
|
307
|
-
/** Device name */
|
|
308
|
-
deviceName?: { required?: boolean; maxLength?: number };
|
|
309
|
-
/** Service UUID */
|
|
310
|
-
serviceUUID?: { required?: boolean; pattern?: RegExp };
|
|
311
|
-
/** Characteristic UUID */
|
|
312
|
-
characteristicUUID?: { required?: boolean; pattern?: RegExp };
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Print job data validation schema
|
|
317
|
-
*/
|
|
318
|
-
export interface PrintJobSchema {
|
|
319
|
-
/** Job ID */
|
|
320
|
-
jobId?: { required?: boolean; maxLength?: number };
|
|
321
|
-
/** Data buffer */
|
|
322
|
-
data?: { required?: boolean; minSize?: number; maxSize?: number };
|
|
323
|
-
/** Priority */
|
|
324
|
-
priority?: { min?: number; max?: number };
|
|
325
|
-
/** Retry count */
|
|
326
|
-
retryCount?: { min?: number; max?: number };
|
|
327
|
-
/** Metadata */
|
|
328
|
-
metadata?: { required?: boolean };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Validate printer connection data
|
|
333
|
-
*
|
|
334
|
-
* @param data - Data to validate
|
|
335
|
-
* @param schema - Validation schema
|
|
336
|
-
* @returns Validation result
|
|
337
|
-
*
|
|
338
|
-
* @example
|
|
339
|
-
* ```typescript
|
|
340
|
-
* const result = validatePrinterData({
|
|
341
|
-
* deviceId: 'device-123',
|
|
342
|
-
* deviceName: 'Thermal Printer',
|
|
343
|
-
* }, {
|
|
344
|
-
* deviceId: { required: true },
|
|
345
|
-
* deviceName: { required: true, maxLength: 50 }
|
|
346
|
-
* });
|
|
347
|
-
* ```
|
|
348
|
-
*/
|
|
349
|
-
export function validatePrinterData(
|
|
350
|
-
data: Record<string, unknown>,
|
|
351
|
-
schema: PrinterDataSchema
|
|
352
|
-
): ValidationResult {
|
|
353
|
-
const errors: ValidationError[] = [];
|
|
354
|
-
|
|
355
|
-
// Device ID validation
|
|
356
|
-
if (schema.deviceId) {
|
|
357
|
-
const { required, pattern } = schema.deviceId;
|
|
358
|
-
const value = data.deviceId;
|
|
359
|
-
|
|
360
|
-
if (required && !value) {
|
|
361
|
-
errors.push({
|
|
362
|
-
field: 'deviceId',
|
|
363
|
-
message: 'Device ID is required',
|
|
364
|
-
code: ValidationCodes.REQUIRED,
|
|
365
|
-
value,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (value && typeof value === 'string' && pattern && !pattern.test(value)) {
|
|
370
|
-
errors.push({
|
|
371
|
-
field: 'deviceId',
|
|
372
|
-
message: 'Device ID format is invalid',
|
|
373
|
-
code: ValidationCodes.PATTERN_MISMATCH,
|
|
374
|
-
value,
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Device name validation
|
|
380
|
-
if (schema.deviceName) {
|
|
381
|
-
const { required, maxLength } = schema.deviceName;
|
|
382
|
-
const value = data.deviceName;
|
|
383
|
-
|
|
384
|
-
if (required && !value) {
|
|
385
|
-
errors.push({
|
|
386
|
-
field: 'deviceName',
|
|
387
|
-
message: 'Device name is required',
|
|
388
|
-
code: ValidationCodes.REQUIRED,
|
|
389
|
-
value,
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (value && typeof value === 'string' && maxLength && value.length > maxLength) {
|
|
394
|
-
errors.push({
|
|
395
|
-
field: 'deviceName',
|
|
396
|
-
message: `Device name must be at most ${maxLength} characters`,
|
|
397
|
-
code: ValidationCodes.TOO_LONG,
|
|
398
|
-
value,
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// Service UUID validation
|
|
404
|
-
if (schema.serviceUUID) {
|
|
405
|
-
const { required, pattern } = schema.serviceUUID;
|
|
406
|
-
const value = data.serviceUUID;
|
|
407
|
-
|
|
408
|
-
if (required && !value) {
|
|
409
|
-
errors.push({
|
|
410
|
-
field: 'serviceUUID',
|
|
411
|
-
message: 'Service UUID is required',
|
|
412
|
-
code: ValidationCodes.REQUIRED,
|
|
413
|
-
value,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (value && typeof value === 'string' && pattern && !pattern.test(value)) {
|
|
418
|
-
errors.push({
|
|
419
|
-
field: 'serviceUUID',
|
|
420
|
-
message: 'Service UUID format is invalid',
|
|
421
|
-
code: ValidationCodes.PATTERN_MISMATCH,
|
|
422
|
-
value,
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Characteristic UUID validation
|
|
428
|
-
if (schema.characteristicUUID) {
|
|
429
|
-
const { required, pattern } = schema.characteristicUUID;
|
|
430
|
-
const value = data.characteristicUUID;
|
|
431
|
-
|
|
432
|
-
if (required && !value) {
|
|
433
|
-
errors.push({
|
|
434
|
-
field: 'characteristicUUID',
|
|
435
|
-
message: 'Characteristic UUID is required',
|
|
436
|
-
code: ValidationCodes.REQUIRED,
|
|
437
|
-
value,
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (value && typeof value === 'string' && pattern && !pattern.test(value)) {
|
|
442
|
-
errors.push({
|
|
443
|
-
field: 'characteristicUUID',
|
|
444
|
-
message: 'Characteristic UUID format is invalid',
|
|
445
|
-
code: ValidationCodes.PATTERN_MISMATCH,
|
|
446
|
-
value,
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return { valid: errors.length === 0, errors };
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Validate print job data
|
|
456
|
-
*
|
|
457
|
-
* @param data - Data to validate
|
|
458
|
-
* @param schema - Validation schema
|
|
459
|
-
* @returns Validation result
|
|
460
|
-
*
|
|
461
|
-
* @example
|
|
462
|
-
* ```typescript
|
|
463
|
-
* const result = validatePrintJob({
|
|
464
|
-
* jobId: 'job-001',
|
|
465
|
-
* data: new ArrayBuffer(1024),
|
|
466
|
-
* priority: 5,
|
|
467
|
-
* }, {
|
|
468
|
-
* jobId: { required: true },
|
|
469
|
-
* data: { required: true, minSize: 1 }
|
|
470
|
-
* });
|
|
471
|
-
* ```
|
|
472
|
-
*/
|
|
473
|
-
export function validatePrintJob(
|
|
474
|
-
data: Record<string, unknown>,
|
|
475
|
-
schema: PrintJobSchema
|
|
476
|
-
): ValidationResult {
|
|
477
|
-
const errors: ValidationError[] = [];
|
|
478
|
-
|
|
479
|
-
// Job ID validation
|
|
480
|
-
if (schema.jobId) {
|
|
481
|
-
const { required, maxLength } = schema.jobId;
|
|
482
|
-
const value = data.jobId;
|
|
483
|
-
|
|
484
|
-
if (required && !value) {
|
|
485
|
-
errors.push({
|
|
486
|
-
field: 'jobId',
|
|
487
|
-
message: 'Job ID is required',
|
|
488
|
-
code: ValidationCodes.REQUIRED,
|
|
489
|
-
value,
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (value && typeof value === 'string' && maxLength && value.length > maxLength) {
|
|
494
|
-
errors.push({
|
|
495
|
-
field: 'jobId',
|
|
496
|
-
message: `Job ID must be at most ${maxLength} characters`,
|
|
497
|
-
code: ValidationCodes.TOO_LONG,
|
|
498
|
-
value,
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Data buffer validation
|
|
504
|
-
if (schema.data) {
|
|
505
|
-
const { required, minSize, maxSize } = schema.data;
|
|
506
|
-
const value = data.data;
|
|
507
|
-
|
|
508
|
-
if (required && !value) {
|
|
509
|
-
errors.push({
|
|
510
|
-
field: 'data',
|
|
511
|
-
message: 'Print data is required',
|
|
512
|
-
code: ValidationCodes.REQUIRED,
|
|
513
|
-
value,
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (value instanceof ArrayBuffer) {
|
|
518
|
-
const byteLength = value.byteLength;
|
|
519
|
-
|
|
520
|
-
if (minSize !== undefined && byteLength < minSize) {
|
|
521
|
-
errors.push({
|
|
522
|
-
field: 'data',
|
|
523
|
-
message: `Print data must be at least ${minSize} bytes`,
|
|
524
|
-
code: ValidationCodes.TOO_SHORT,
|
|
525
|
-
value: byteLength,
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
if (maxSize !== undefined && byteLength > maxSize) {
|
|
530
|
-
errors.push({
|
|
531
|
-
field: 'data',
|
|
532
|
-
message: `Print data must be at most ${maxSize} bytes`,
|
|
533
|
-
code: ValidationCodes.TOO_LONG,
|
|
534
|
-
value: byteLength,
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
} else if (value && !(value instanceof ArrayBuffer)) {
|
|
538
|
-
errors.push({
|
|
539
|
-
field: 'data',
|
|
540
|
-
message: 'Print data must be an ArrayBuffer',
|
|
541
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
542
|
-
value: typeof value,
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Priority validation
|
|
548
|
-
if (schema.priority) {
|
|
549
|
-
const { min, max } = schema.priority;
|
|
550
|
-
const value = data.priority;
|
|
551
|
-
|
|
552
|
-
if (value !== undefined && typeof value !== 'number') {
|
|
553
|
-
errors.push({
|
|
554
|
-
field: 'priority',
|
|
555
|
-
message: 'Priority must be a number',
|
|
556
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
557
|
-
value,
|
|
558
|
-
});
|
|
559
|
-
} else if (typeof value === 'number') {
|
|
560
|
-
if (min !== undefined && value < min) {
|
|
561
|
-
errors.push({
|
|
562
|
-
field: 'priority',
|
|
563
|
-
message: `Priority must be at least ${min}`,
|
|
564
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
565
|
-
value,
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
if (max !== undefined && value > max) {
|
|
569
|
-
errors.push({
|
|
570
|
-
field: 'priority',
|
|
571
|
-
message: `Priority must be at most ${max}`,
|
|
572
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
573
|
-
value,
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Retry count validation
|
|
580
|
-
if (schema.retryCount) {
|
|
581
|
-
const { min, max } = schema.retryCount;
|
|
582
|
-
const value = data.retryCount;
|
|
583
|
-
|
|
584
|
-
if (value !== undefined && typeof value !== 'number') {
|
|
585
|
-
errors.push({
|
|
586
|
-
field: 'retryCount',
|
|
587
|
-
message: 'Retry count must be a number',
|
|
588
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
589
|
-
value,
|
|
590
|
-
});
|
|
591
|
-
} else if (typeof value === 'number') {
|
|
592
|
-
if (min !== undefined && value < min) {
|
|
593
|
-
errors.push({
|
|
594
|
-
field: 'retryCount',
|
|
595
|
-
message: `Retry count must be at least ${min}`,
|
|
596
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
597
|
-
value,
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
if (max !== undefined && value > max) {
|
|
601
|
-
errors.push({
|
|
602
|
-
field: 'retryCount',
|
|
603
|
-
message: `Retry count must be at most ${max}`,
|
|
604
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
605
|
-
value,
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
return { valid: errors.length === 0, errors };
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Validate an ArrayBuffer
|
|
616
|
-
*
|
|
617
|
-
* @param buffer - Buffer to validate
|
|
618
|
-
* @param options - Validation options
|
|
619
|
-
* @returns Validation result
|
|
620
|
-
*
|
|
621
|
-
* @example
|
|
622
|
-
* ```typescript
|
|
623
|
-
* const result = isValidBuffer(data, { minSize: 1, maxSize: 1024 * 1024 });
|
|
624
|
-
* ```
|
|
625
|
-
*/
|
|
626
|
-
export function isValidBuffer(
|
|
627
|
-
buffer: unknown,
|
|
628
|
-
options?: {
|
|
629
|
-
minSize?: number;
|
|
630
|
-
maxSize?: number;
|
|
631
|
-
required?: boolean;
|
|
632
|
-
}
|
|
633
|
-
): ValidationResult {
|
|
634
|
-
const errors: ValidationError[] = [];
|
|
635
|
-
const warnings: ValidationError[] = [];
|
|
636
|
-
|
|
637
|
-
if (!(buffer instanceof ArrayBuffer)) {
|
|
638
|
-
errors.push({
|
|
639
|
-
field: 'buffer',
|
|
640
|
-
message: 'Expected an ArrayBuffer',
|
|
641
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
642
|
-
value: typeof buffer,
|
|
643
|
-
});
|
|
644
|
-
return {
|
|
645
|
-
valid: false,
|
|
646
|
-
errors,
|
|
647
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
const byteLength = buffer.byteLength;
|
|
652
|
-
// When required is true without explicit minSize, treat empty buffer as TOO_SHORT
|
|
653
|
-
const effectiveMinSize =
|
|
654
|
-
options?.required && options?.minSize === undefined ? 1 : options?.minSize;
|
|
655
|
-
|
|
656
|
-
if (effectiveMinSize !== undefined && byteLength < effectiveMinSize) {
|
|
657
|
-
errors.push({
|
|
658
|
-
field: 'buffer',
|
|
659
|
-
message: `Buffer must be at least ${effectiveMinSize} bytes`,
|
|
660
|
-
code: ValidationCodes.TOO_SHORT,
|
|
661
|
-
value: byteLength,
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
if (options?.maxSize !== undefined && byteLength > options.maxSize) {
|
|
666
|
-
errors.push({
|
|
667
|
-
field: 'buffer',
|
|
668
|
-
message: `Buffer exceeds maximum size of ${options.maxSize} bytes`,
|
|
669
|
-
code: ValidationCodes.TOO_LONG,
|
|
670
|
-
value: byteLength,
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (byteLength === 0) {
|
|
675
|
-
warnings.push({
|
|
676
|
-
field: 'buffer',
|
|
677
|
-
message: 'Buffer is empty',
|
|
678
|
-
code: ValidationCodes.EMPTY_ARRAY,
|
|
679
|
-
value: byteLength,
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
return {
|
|
684
|
-
valid: errors.length === 0,
|
|
685
|
-
errors,
|
|
686
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Validate a string is valid UUID
|
|
692
|
-
*
|
|
693
|
-
* @param value - Value to validate
|
|
694
|
-
* @param options - Validation options
|
|
695
|
-
* @returns Validation result
|
|
696
|
-
*
|
|
697
|
-
* @example
|
|
698
|
-
* ```typescript
|
|
699
|
-
* const result = isValidUUID(value, { required: true });
|
|
700
|
-
* ```
|
|
701
|
-
*/
|
|
702
|
-
export function isValidUUID(
|
|
703
|
-
value: unknown,
|
|
704
|
-
options?: { required?: boolean; versions?: number[] }
|
|
705
|
-
): { valid: boolean; version?: number; error?: string } {
|
|
706
|
-
if (value === undefined || value === null || value === '') {
|
|
707
|
-
if (options?.required) {
|
|
708
|
-
return { valid: false, error: 'UUID is required' };
|
|
709
|
-
}
|
|
710
|
-
return { valid: true };
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
if (typeof value !== 'string') {
|
|
714
|
-
return { valid: false, error: 'UUID must be a string' };
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// UUID format: 8-4-4-4-12 hex characters
|
|
718
|
-
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
719
|
-
if (!uuidPattern.test(value)) {
|
|
720
|
-
return { valid: false, error: 'Invalid UUID format (expected 8-4-4-4-12 hex format)' };
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// Extract version
|
|
724
|
-
const versionChar = value.charAt(14);
|
|
725
|
-
const version = parseInt(versionChar, 16);
|
|
726
|
-
|
|
727
|
-
if (options?.versions) {
|
|
728
|
-
if (!options.versions.includes(version)) {
|
|
729
|
-
return { valid: false, error: `UUID version must be one of: ${options.versions.join(', ')}` };
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
return { valid: true, version };
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* Validate numeric range
|
|
738
|
-
*
|
|
739
|
-
* @param value - Value to validate
|
|
740
|
-
* @param field - Field name for error messages
|
|
741
|
-
* @param options - Range options
|
|
742
|
-
* @returns Validation result
|
|
743
|
-
*
|
|
744
|
-
* @example
|
|
745
|
-
* ```typescript
|
|
746
|
-
* const result = validateRange(value, 'timeout', { min: 0, max: 30000, integer: true });
|
|
747
|
-
* ```
|
|
748
|
-
*/
|
|
749
|
-
export function validateRange(
|
|
750
|
-
value: unknown,
|
|
751
|
-
field: string,
|
|
752
|
-
options?: {
|
|
753
|
-
required?: boolean;
|
|
754
|
-
min?: number;
|
|
755
|
-
max?: number;
|
|
756
|
-
integer?: boolean;
|
|
757
|
-
}
|
|
758
|
-
): ValidationResult {
|
|
759
|
-
const errors: ValidationError[] = [];
|
|
760
|
-
|
|
761
|
-
if (value === undefined || value === null) {
|
|
762
|
-
if (options?.required) {
|
|
763
|
-
errors.push({
|
|
764
|
-
field,
|
|
765
|
-
message: `${field} is required`,
|
|
766
|
-
code: ValidationCodes.REQUIRED,
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
return { valid: errors.length === 0, errors };
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
if (typeof value !== 'number') {
|
|
773
|
-
errors.push({
|
|
774
|
-
field,
|
|
775
|
-
message: `${field} must be a number`,
|
|
776
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
777
|
-
value: typeof value,
|
|
778
|
-
});
|
|
779
|
-
return { valid: false, errors };
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
if (isNaN(value)) {
|
|
783
|
-
errors.push({
|
|
784
|
-
field,
|
|
785
|
-
message: `${field} must be a valid number`,
|
|
786
|
-
code: ValidationCodes.NOT_A_NUMBER,
|
|
787
|
-
value,
|
|
788
|
-
});
|
|
789
|
-
return { valid: false, errors };
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
if (!isFinite(value)) {
|
|
793
|
-
errors.push({
|
|
794
|
-
field,
|
|
795
|
-
message: `${field} must be a finite number`,
|
|
796
|
-
code: ValidationCodes.INFINITY_VALUE,
|
|
797
|
-
value,
|
|
798
|
-
});
|
|
799
|
-
return { valid: false, errors };
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (options?.integer && !Number.isInteger(value)) {
|
|
803
|
-
errors.push({
|
|
804
|
-
field,
|
|
805
|
-
message: `${field} must be an integer`,
|
|
806
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
807
|
-
value,
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (options?.min !== undefined && value < options.min) {
|
|
812
|
-
errors.push({
|
|
813
|
-
field,
|
|
814
|
-
message: `${field} must be at least ${options.min}`,
|
|
815
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
816
|
-
value,
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
if (options?.max !== undefined && value > options.max) {
|
|
821
|
-
errors.push({
|
|
822
|
-
field,
|
|
823
|
-
message: `${field} must be at most ${options.max}`,
|
|
824
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
825
|
-
value,
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
return { valid: errors.length === 0, errors };
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
/**
|
|
833
|
-
* Validate an object against a schema
|
|
834
|
-
*
|
|
835
|
-
* @param data - Data object to validate
|
|
836
|
-
* @param rules - Validation rules per field
|
|
837
|
-
* @returns Validation result
|
|
838
|
-
*
|
|
839
|
-
* @example
|
|
840
|
-
* ```typescript
|
|
841
|
-
* const result = validateObject(data, {
|
|
842
|
-
* name: [
|
|
843
|
-
* { name: 'required', validate: v => v !== undefined, message: 'Name is required', code: 'REQUIRED' },
|
|
844
|
-
* { name: 'maxLength', validate: v => v.length <= 50, message: 'Max 50 chars', code: 'TOO_LONG' }
|
|
845
|
-
* ],
|
|
846
|
-
* age: [
|
|
847
|
-
* { name: 'number', validate: v => typeof v === 'number', message: 'Must be number', code: 'INVALID_TYPE' }
|
|
848
|
-
* ]
|
|
849
|
-
* });
|
|
850
|
-
* ```
|
|
851
|
-
*/
|
|
852
|
-
export function validateObject<T extends Record<string, unknown>>(
|
|
853
|
-
data: T,
|
|
854
|
-
rules: {
|
|
855
|
-
[K in keyof T]?: ValidationRule<T[K]>[];
|
|
856
|
-
}
|
|
857
|
-
): ValidationResult {
|
|
858
|
-
const errors: ValidationError[] = [];
|
|
859
|
-
|
|
860
|
-
for (const [field, fieldRules] of Object.entries(rules)) {
|
|
861
|
-
if (!fieldRules) continue;
|
|
862
|
-
|
|
863
|
-
const value = data[field];
|
|
864
|
-
|
|
865
|
-
for (const rule of fieldRules) {
|
|
866
|
-
try {
|
|
867
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
868
|
-
if (!rule.validate(value)) {
|
|
869
|
-
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
|
|
870
|
-
errors.push({ field, message: rule.message, code: rule.code, value });
|
|
871
|
-
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
|
|
872
|
-
}
|
|
873
|
-
} catch (err) {
|
|
874
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
875
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
876
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
877
|
-
errors.push({
|
|
878
|
-
field,
|
|
879
|
-
message: `Validation error: ${errorMessage}`,
|
|
880
|
-
code: 'VALIDATION_ERROR',
|
|
881
|
-
value,
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
return { valid: errors.length === 0, errors };
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
/**
|
|
891
|
-
* Validate an array of items
|
|
892
|
-
*
|
|
893
|
-
* @param items - Array to validate
|
|
894
|
-
* @param itemValidator - Validator function for each item
|
|
895
|
-
* @param options - Array validation options
|
|
896
|
-
* @returns Validation result
|
|
897
|
-
*
|
|
898
|
-
* @example
|
|
899
|
-
* ```typescript
|
|
900
|
-
* const result = validateArray(items, item => validatePrintJob(item, schema), { minItems: 1, maxItems: 100 });
|
|
901
|
-
* ```
|
|
902
|
-
*/
|
|
903
|
-
export function validateArray<T>(
|
|
904
|
-
items: unknown,
|
|
905
|
-
itemValidator: (item: T, index: number) => ValidationResult,
|
|
906
|
-
options?: {
|
|
907
|
-
required?: boolean;
|
|
908
|
-
minItems?: number;
|
|
909
|
-
maxItems?: number;
|
|
910
|
-
}
|
|
911
|
-
): ValidationResult {
|
|
912
|
-
const errors: ValidationError[] = [];
|
|
913
|
-
const warnings: ValidationError[] = [];
|
|
914
|
-
|
|
915
|
-
if (items === undefined || items === null) {
|
|
916
|
-
if (options?.required) {
|
|
917
|
-
errors.push({
|
|
918
|
-
field: 'array',
|
|
919
|
-
message: 'Array is required',
|
|
920
|
-
code: ValidationCodes.REQUIRED,
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
return { valid: errors.length === 0, errors, warnings };
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
if (!Array.isArray(items)) {
|
|
927
|
-
errors.push({
|
|
928
|
-
field: 'array',
|
|
929
|
-
message: 'Expected an array',
|
|
930
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
931
|
-
value: typeof items,
|
|
932
|
-
});
|
|
933
|
-
return { valid: false, errors, warnings };
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
if (options?.minItems !== undefined && items.length < options.minItems) {
|
|
937
|
-
errors.push({
|
|
938
|
-
field: 'array',
|
|
939
|
-
message: `Array must have at least ${options.minItems} items`,
|
|
940
|
-
code: ValidationCodes.ARRAY_TOO_SHORT,
|
|
941
|
-
value: items.length,
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
if (options?.maxItems !== undefined && items.length > options.maxItems) {
|
|
946
|
-
errors.push({
|
|
947
|
-
field: 'array',
|
|
948
|
-
message: `Array must have at most ${options.maxItems} items`,
|
|
949
|
-
code: ValidationCodes.ARRAY_TOO_LONG,
|
|
950
|
-
value: items.length,
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// Validate each item
|
|
955
|
-
for (let i = 0; i < items.length; i++) {
|
|
956
|
-
const itemResult = itemValidator(items[i] as T, i);
|
|
957
|
-
errors.push(
|
|
958
|
-
...itemResult.errors.map(err => ({
|
|
959
|
-
...err,
|
|
960
|
-
field: `${err.field}[${i}]`,
|
|
961
|
-
}))
|
|
962
|
-
);
|
|
963
|
-
if (itemResult.warnings) {
|
|
964
|
-
warnings.push(
|
|
965
|
-
...itemResult.warnings.map(w => ({
|
|
966
|
-
...w,
|
|
967
|
-
field: `${w.field}[${i}]`,
|
|
968
|
-
}))
|
|
969
|
-
);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
return {
|
|
974
|
-
valid: errors.length === 0,
|
|
975
|
-
errors,
|
|
976
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
/**
|
|
981
|
-
* Create a chainable validation helper
|
|
982
|
-
*
|
|
983
|
-
* @param value - Value to validate
|
|
984
|
-
* @param field - Field name
|
|
985
|
-
* @returns Chainable validator
|
|
986
|
-
*
|
|
987
|
-
* @example
|
|
988
|
-
* ```typescript
|
|
989
|
-
* const result = check(value, 'timeout')
|
|
990
|
-
* .required()
|
|
991
|
-
* .number()
|
|
992
|
-
* .range(0, 30000)
|
|
993
|
-
* .integer()
|
|
994
|
-
* .result();
|
|
995
|
-
* ```
|
|
996
|
-
*/
|
|
997
|
-
|
|
998
|
-
export interface ChainableValidator {
|
|
999
|
-
addError(message: string, code: string): ChainableValidator;
|
|
1000
|
-
required(): ChainableValidator;
|
|
1001
|
-
string(): ChainableValidator;
|
|
1002
|
-
number(): ChainableValidator;
|
|
1003
|
-
integer(): ChainableValidator;
|
|
1004
|
-
range(min: number, max: number): ChainableValidator;
|
|
1005
|
-
length(min: number, max: number): ChainableValidator;
|
|
1006
|
-
pattern(regex: RegExp, message?: string): ChainableValidator;
|
|
1007
|
-
oneOf<T>(values: readonly T[], message?: string): ChainableValidator;
|
|
1008
|
-
result(): ValidationResult;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
export function check(value: unknown, field: string): ChainableValidator {
|
|
1012
|
-
const errors: ValidationError[] = [];
|
|
1013
|
-
|
|
1014
|
-
const self: ChainableValidator = {
|
|
1015
|
-
/** Add error manually */
|
|
1016
|
-
addError(message: string, code: string) {
|
|
1017
|
-
errors.push({ field, message, code, value });
|
|
1018
|
-
return this;
|
|
1019
|
-
},
|
|
1020
|
-
|
|
1021
|
-
/** Validate required */
|
|
1022
|
-
required() {
|
|
1023
|
-
if (value === undefined || value === null || value === '') {
|
|
1024
|
-
errors.push({
|
|
1025
|
-
field,
|
|
1026
|
-
message: `${field} is required`,
|
|
1027
|
-
code: ValidationCodes.REQUIRED,
|
|
1028
|
-
value,
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
1031
|
-
return this;
|
|
1032
|
-
},
|
|
1033
|
-
|
|
1034
|
-
/** Validate type is string */
|
|
1035
|
-
string() {
|
|
1036
|
-
if (value !== undefined && value !== null && typeof value !== 'string') {
|
|
1037
|
-
errors.push({
|
|
1038
|
-
field,
|
|
1039
|
-
message: `${field} must be a string`,
|
|
1040
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
1041
|
-
value: typeof value,
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
return this;
|
|
1045
|
-
},
|
|
1046
|
-
|
|
1047
|
-
/** Validate type is number */
|
|
1048
|
-
number() {
|
|
1049
|
-
if (value !== undefined && value !== null && typeof value !== 'number') {
|
|
1050
|
-
errors.push({
|
|
1051
|
-
field,
|
|
1052
|
-
message: `${field} must be a number`,
|
|
1053
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
1054
|
-
value: typeof value,
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
return this;
|
|
1058
|
-
},
|
|
1059
|
-
|
|
1060
|
-
/** Validate is integer */
|
|
1061
|
-
integer() {
|
|
1062
|
-
if (typeof value === 'number' && !Number.isInteger(value)) {
|
|
1063
|
-
errors.push({
|
|
1064
|
-
field,
|
|
1065
|
-
message: `${field} must be an integer`,
|
|
1066
|
-
code: ValidationCodes.INVALID_TYPE,
|
|
1067
|
-
value,
|
|
1068
|
-
});
|
|
1069
|
-
}
|
|
1070
|
-
return this;
|
|
1071
|
-
},
|
|
1072
|
-
|
|
1073
|
-
/** Validate range */
|
|
1074
|
-
range(min: number, max: number) {
|
|
1075
|
-
if (typeof value === 'number') {
|
|
1076
|
-
if (value < min) {
|
|
1077
|
-
errors.push({
|
|
1078
|
-
field,
|
|
1079
|
-
message: `${field} must be at least ${min}`,
|
|
1080
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
1081
|
-
value,
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1084
|
-
if (value > max) {
|
|
1085
|
-
errors.push({
|
|
1086
|
-
field,
|
|
1087
|
-
message: `${field} must be at most ${max}`,
|
|
1088
|
-
code: ValidationCodes.OUT_OF_RANGE,
|
|
1089
|
-
value,
|
|
1090
|
-
});
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
return this;
|
|
1094
|
-
},
|
|
1095
|
-
|
|
1096
|
-
/** Validate string length */
|
|
1097
|
-
length(min: number, max: number) {
|
|
1098
|
-
if (typeof value === 'string') {
|
|
1099
|
-
if (value.length < min) {
|
|
1100
|
-
errors.push({
|
|
1101
|
-
field,
|
|
1102
|
-
message: `${field} must be at least ${min} characters`,
|
|
1103
|
-
code: ValidationCodes.TOO_SHORT,
|
|
1104
|
-
value,
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1107
|
-
if (value.length > max) {
|
|
1108
|
-
errors.push({
|
|
1109
|
-
field,
|
|
1110
|
-
message: `${field} must be at most ${max} characters`,
|
|
1111
|
-
code: ValidationCodes.TOO_LONG,
|
|
1112
|
-
value,
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return this;
|
|
1117
|
-
},
|
|
1118
|
-
|
|
1119
|
-
/** Validate matches pattern */
|
|
1120
|
-
pattern(regex: RegExp, message?: string) {
|
|
1121
|
-
if (typeof value === 'string' && !regex.test(value)) {
|
|
1122
|
-
errors.push({
|
|
1123
|
-
field,
|
|
1124
|
-
message: message ?? `${field} format is invalid`,
|
|
1125
|
-
code: ValidationCodes.PATTERN_MISMATCH,
|
|
1126
|
-
value,
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
return this;
|
|
1130
|
-
},
|
|
1131
|
-
|
|
1132
|
-
/** Validate is one of values */
|
|
1133
|
-
oneOf<T>(values: readonly T[], message?: string) {
|
|
1134
|
-
if (!values.includes(value as T)) {
|
|
1135
|
-
errors.push({
|
|
1136
|
-
field,
|
|
1137
|
-
message: message ?? `${field} must be one of: ${values.join(', ')}`,
|
|
1138
|
-
code: ValidationCodes.INVALID_ENUM,
|
|
1139
|
-
value,
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
return this;
|
|
1143
|
-
},
|
|
1144
|
-
|
|
1145
|
-
/** Get validation result */
|
|
1146
|
-
result() {
|
|
1147
|
-
return {
|
|
1148
|
-
valid: errors.length === 0,
|
|
1149
|
-
errors,
|
|
1150
|
-
};
|
|
1151
|
-
},
|
|
1152
|
-
};
|
|
1153
|
-
|
|
1154
|
-
return self;
|
|
1155
|
-
}
|
|
18
|
+
// Re-export all validators
|
|
19
|
+
export { validatePrinterData, validatePrintJob } from './validators/printer';
|
|
20
|
+
export { isValidBuffer } from './validators/buffer';
|
|
21
|
+
export { isValidUUID } from './validators/uuid';
|
|
22
|
+
export { validateRange } from './validators/number';
|
|
23
|
+
export { validateObject } from './validators/object';
|
|
24
|
+
export { validateArray } from './validators/array';
|
|
25
|
+
export { check } from './validators/chain';
|
|
26
|
+
export type { ChainableValidator } from './validators/chain';
|
|
27
|
+
export { CommonValidators } from './validators/common';
|
|
28
|
+
|
|
29
|
+
// Re-export types and constants
|
|
30
|
+
export { ValidationCodes } from './validators/types';
|
|
31
|
+
export type {
|
|
32
|
+
ValidationError,
|
|
33
|
+
ValidationResult,
|
|
34
|
+
ValidatorFn,
|
|
35
|
+
ValidationRule,
|
|
36
|
+
PrinterDataSchema,
|
|
37
|
+
PrintJobSchema,
|
|
38
|
+
} from './validators/types';
|