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.
Files changed (65) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +53 -4
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.es.js +1 -1
  5. package/dist/index.umd.js +1 -1
  6. package/dist/types/core/di/Container.d.ts +84 -0
  7. package/dist/types/core/di/Tokens.d.ts +29 -0
  8. package/dist/types/core/di/index.d.ts +3 -0
  9. package/dist/types/core/event/EventBus.d.ts +66 -0
  10. package/dist/types/core/event/index.d.ts +2 -0
  11. package/dist/types/core/index.d.ts +5 -4
  12. package/dist/types/core/plugin/PluginManager.d.ts +64 -0
  13. package/dist/types/core/plugin/index.d.ts +2 -0
  14. package/dist/types/device/MultiPrinterManager.d.ts +2 -0
  15. package/dist/types/factory/di-factory.d.ts +52 -0
  16. package/dist/types/index.d.ts +5 -1
  17. package/dist/types/providers/ServiceProvider.d.ts +56 -0
  18. package/dist/types/providers/index.d.ts +2 -0
  19. package/dist/types/template/TemplateEngine.d.ts +24 -68
  20. package/dist/types/template/engines/TemplateRenderer.d.ts +71 -0
  21. package/dist/types/template/parsers/TemplateParser.d.ts +23 -0
  22. package/dist/types/utils/index.d.ts +8 -0
  23. package/dist/types/utils/logger.d.ts +4 -3
  24. package/dist/types/utils/outputLimiter.d.ts +87 -0
  25. package/dist/types/utils/validation.d.ts +11 -309
  26. package/dist/types/utils/validators/array.d.ts +19 -0
  27. package/dist/types/utils/validators/buffer.d.ts +18 -0
  28. package/dist/types/utils/validators/chain.d.ts +31 -0
  29. package/dist/types/utils/validators/common.d.ts +22 -0
  30. package/dist/types/utils/validators/number.d.ts +20 -0
  31. package/dist/types/utils/validators/object.d.ts +24 -0
  32. package/dist/types/utils/validators/printer.d.ts +40 -0
  33. package/dist/types/utils/validators/types.d.ts +125 -0
  34. package/dist/types/utils/validators/uuid.d.ts +23 -0
  35. package/package.json +1 -1
  36. package/src/core/BluetoothPrinter.ts +2 -1
  37. package/src/core/di/Container.ts +332 -0
  38. package/src/core/di/Tokens.ts +45 -0
  39. package/src/core/di/index.ts +3 -0
  40. package/src/core/event/EventBus.ts +251 -0
  41. package/src/core/event/index.ts +2 -0
  42. package/src/core/index.ts +10 -4
  43. package/src/core/plugin/PluginManager.ts +161 -0
  44. package/src/core/plugin/index.ts +2 -0
  45. package/src/device/MultiPrinterManager.ts +15 -6
  46. package/src/factory/di-factory.ts +61 -0
  47. package/src/index.ts +50 -1
  48. package/src/providers/ServiceProvider.ts +213 -0
  49. package/src/providers/index.ts +2 -0
  50. package/src/template/TemplateEngine.ts +27 -792
  51. package/src/template/engines/TemplateRenderer.ts +762 -0
  52. package/src/template/parsers/TemplateParser.ts +94 -0
  53. package/src/utils/index.ts +9 -0
  54. package/src/utils/logger.ts +17 -4
  55. package/src/utils/outputLimiter.ts +227 -0
  56. package/src/utils/validation.ts +21 -1138
  57. package/src/utils/validators/array.ts +95 -0
  58. package/src/utils/validators/buffer.ts +81 -0
  59. package/src/utils/validators/chain.ts +181 -0
  60. package/src/utils/validators/common.ts +216 -0
  61. package/src/utils/validators/number.ts +101 -0
  62. package/src/utils/validators/object.ts +63 -0
  63. package/src/utils/validators/printer.ts +294 -0
  64. package/src/utils/validators/types.ts +105 -0
  65. package/src/utils/validators/uuid.ts +49 -0
@@ -15,1141 +15,24 @@
15
15
  * ```
16
16
  */
17
17
 
18
- /**
19
- * Validation error details
20
- */
21
- export interface ValidationError {
22
- /** Field that failed validation */
23
- field: string;
24
- /** Human-readable error message */
25
- message: string;
26
- /** Error code for programmatic handling */
27
- code: string;
28
- /** Actual value that failed (optional) */
29
- value?: unknown;
30
- }
31
-
32
- /**
33
- * Validation result
34
- */
35
- export interface ValidationResult {
36
- /** Whether validation passed */
37
- valid: boolean;
38
- /** List of validation errors */
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';