@hy_ong/zod-kit 0.0.5 β†’ 0.0.6

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.
@@ -15,7 +15,12 @@
15
15
  "Bash(npx tsc:*)",
16
16
  "WebFetch(domain:www.motc.gov.tw)",
17
17
  "WebFetch(domain:zh.wikipedia.org)",
18
- "Bash(npx ts-node:*)"
18
+ "Bash(npx ts-node:*)",
19
+ "WebFetch(domain:www.post.gov.tw)",
20
+ "WebFetch(domain:foundi.tw)",
21
+ "WebFetch(domain:data.gov.tw)",
22
+ "WebFetch(domain:gist.github.com)",
23
+ "WebFetch(domain:zip5.5432.tw)"
19
24
  ],
20
25
  "deny": [],
21
26
  "ask": []
package/README.md CHANGED
@@ -1,169 +1,495 @@
1
1
  # Zod Kit
2
2
 
3
- A TypeScript library that provides common validation schemas built on top of [Zod](https://github.com/colinhacks/zod) with internationalization support.
3
+ [![npm version](https://badge.fury.io/js/%40hy_ong%2Fzod-kit.svg)](https://badge.fury.io/js/%40hy_ong%2Fzod-kit)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
4
6
 
5
- ## Features
7
+ A comprehensive TypeScript library that provides pre-built validation schemas on top of [Zod](https://github.com/colinhacks/zod) with full internationalization support. Streamline your form validation with battle-tested schemas for common data types and Taiwan-specific formats.
6
8
 
7
- - πŸ” Pre-built validation schemas for common data types
8
- - 🌐 Internationalization support (English and Traditional Chinese)
9
- - πŸ“ TypeScript-first with full type safety
10
- - ⚑ Built on top of Zod for robust validation
11
- - 🎯 Configurable validation options
9
+ ## ✨ Features
12
10
 
13
- ## Installation
11
+ - πŸ” **Pre-built schemas** - Common validation patterns ready to use
12
+ - 🌐 **i18n support** - English and Traditional Chinese localization
13
+ - πŸ“ **TypeScript-first** - Full type safety and IntelliSense support
14
+ - ⚑ **Zero dependencies** - Built on top of Zod (peer dependency)
15
+ - 🎯 **Highly configurable** - Flexible options for every use case
16
+ - πŸ‡ΉπŸ‡Ό **Taiwan-specific** - National ID, business ID, phone numbers, postal codes, etc.
17
+ - ⏰ **Date/Time support** - Comprehensive datetime, time, and date validation
18
+ - πŸ“Ž **File validation** - MIME type filtering, size constraints, and file type checking
19
+ - πŸ§ͺ **Battle-tested** - Comprehensive test suite with 500+ tests
20
+
21
+ ## πŸ“¦ Installation
14
22
 
15
23
  ```bash
16
- npm install zod-kit
24
+ npm install @hy_ong/zod-kit zod
17
25
  ```
18
26
 
19
- ## Quick Start
27
+ ```bash
28
+ yarn add @hy_ong/zod-kit zod
29
+ ```
30
+
31
+ ```bash
32
+ pnpm add @hy_ong/zod-kit zod
33
+ ```
34
+
35
+ > **Note**: Zod is a peer dependency and must be installed separately.
36
+
37
+ ## πŸš€ Quick Start
20
38
 
21
39
  ```typescript
22
- import { email, password, text, number } from '@hy_ong/zod-kit'
40
+ import { email, password, text, mobile, datetime, time, postalCode } from '@hy_ong/zod-kit'
23
41
 
24
- // Email validation
25
- const emailSchema = email({ label: 'Email' })
26
- emailSchema.parse('user@example.com') // βœ…
42
+ // Simple email validation
43
+ const emailSchema = email()
44
+ emailSchema.parse('user@example.com') // βœ… "user@example.com"
27
45
 
28
- // Password validation with requirements
46
+ // Password with complexity requirements
29
47
  const passwordSchema = password({
30
- label: 'Password',
31
- min: 8,
32
- uppercase: true,
33
- lowercase: true,
34
- digits: true,
35
- special: true
48
+ minLength: 8,
49
+ requireUppercase: true,
50
+ requireDigits: true,
51
+ requireSpecialChars: true
36
52
  })
37
53
 
38
- // Text validation
54
+ // Taiwan mobile phone validation
55
+ const phoneSchema = mobile()
56
+ phoneSchema.parse('0912345678') // βœ… "0912345678"
57
+
58
+ // DateTime validation
59
+ const datetimeSchema = datetime()
60
+ datetimeSchema.parse('2024-03-15 14:30') // βœ… "2024-03-15 14:30"
61
+
62
+ // Time validation
63
+ const timeSchema = time()
64
+ timeSchema.parse('14:30') // βœ… "14:30"
65
+
66
+ // Taiwan postal code validation
67
+ const postalSchema = postalCode()
68
+ postalSchema.parse('100001') // βœ… "100001"
69
+ ```
70
+
71
+ ## πŸ“š API Reference
72
+
73
+ ### Common Validators
74
+
75
+ #### `email(options?)`
76
+
77
+ Validates email addresses with comprehensive format checking.
78
+
79
+ ```typescript
80
+ import { email } from '@hy_ong/zod-kit'
81
+
82
+ // Basic usage
83
+ const basicEmail = email()
84
+
85
+ // With options
86
+ const advancedEmail = email({
87
+ required: true, // Default: true
88
+ allowedDomains: ['gmail.com', 'company.com'],
89
+ minLength: 5,
90
+ maxLength: 100,
91
+ transform: (val) => val.toLowerCase(),
92
+ i18n: {
93
+ en: { invalid: 'Please enter a valid email' }
94
+ }
95
+ })
96
+ ```
97
+
98
+ #### `password(options?)`
99
+
100
+ Validates passwords with customizable complexity requirements.
101
+
102
+ ```typescript
103
+ import { password } from '@hy_ong/zod-kit'
104
+
105
+ const passwordSchema = password({
106
+ minLength: 8, // Minimum length
107
+ maxLength: 128, // Maximum length
108
+ requireUppercase: true, // Require A-Z
109
+ requireLowercase: true, // Require a-z
110
+ requireDigits: true, // Require 0-9
111
+ requireSpecialChars: true,// Require !@#$%^&*
112
+ customPatterns: [
113
+ { pattern: /[A-Z]/, message: 'Need uppercase' }
114
+ ]
115
+ })
116
+ ```
117
+
118
+ #### `text(options?)`
119
+
120
+ General text validation with length and pattern constraints.
121
+
122
+ ```typescript
123
+ import { text } from '@hy_ong/zod-kit'
124
+
39
125
  const nameSchema = text({
40
- label: 'Name',
41
- min: 2,
42
- max: 50
126
+ minLength: 2,
127
+ maxLength: 50,
128
+ pattern: /^[a-zA-Z\s]+$/,
129
+ transform: (val) => val.trim()
130
+ })
131
+ ```
132
+
133
+ #### `number(options?)`
134
+
135
+ Validates numeric values with range and type constraints.
136
+
137
+ ```typescript
138
+ import { number } from '@hy_ong/zod-kit'
139
+
140
+ const ageSchema = number({
141
+ min: 0,
142
+ max: 150,
143
+ integer: true,
144
+ positive: true
145
+ })
146
+ ```
147
+
148
+ #### `url(options?)`
149
+
150
+ URL validation with protocol and domain restrictions.
151
+
152
+ ```typescript
153
+ import { url } from '@hy_ong/zod-kit'
154
+
155
+ const urlSchema = url({
156
+ protocols: ['https'], // Only HTTPS allowed
157
+ allowedDomains: ['safe.com'], // Domain whitelist
158
+ requireTLD: true // Require top-level domain
159
+ })
160
+ ```
161
+
162
+ #### `boolean(options?)`
163
+
164
+ Boolean validation with flexible input handling.
165
+
166
+ ```typescript
167
+ import { boolean } from '@hy_ong/zod-kit'
168
+
169
+ const consentSchema = boolean({
170
+ required: true,
171
+ trueValues: ['yes', '1', 'true'], // Custom truthy values
172
+ falseValues: ['no', '0', 'false'] // Custom falsy values
173
+ })
174
+ ```
175
+
176
+ #### `datetime(options?)`
177
+
178
+ Validates datetime with comprehensive format support and timezone handling.
179
+
180
+ ```typescript
181
+ import { datetime } from '@hy_ong/zod-kit'
182
+
183
+ // Basic datetime validation
184
+ const basicSchema = datetime()
185
+ basicSchema.parse('2024-03-15 14:30') // βœ“ Valid
186
+
187
+ // Business hours validation
188
+ const businessHours = datetime({
189
+ format: 'YYYY-MM-DD HH:mm',
190
+ minHour: 9,
191
+ maxHour: 17,
192
+ weekdaysOnly: true
193
+ })
194
+
195
+ // Timezone-aware validation
196
+ const timezoneSchema = datetime({
197
+ timezone: 'Asia/Taipei',
198
+ mustBeFuture: true
199
+ })
200
+
201
+ // Multiple format support
202
+ const flexibleSchema = datetime({
203
+ format: 'DD/MM/YYYY HH:mm'
204
+ })
205
+ flexibleSchema.parse('15/03/2024 14:30') // βœ“ Valid
206
+ ```
207
+
208
+ #### `time(options?)`
209
+
210
+ Time validation with multiple formats and constraints.
211
+
212
+ ```typescript
213
+ import { time } from '@hy_ong/zod-kit'
214
+
215
+ // Basic time validation (24-hour format)
216
+ const basicSchema = time()
217
+ basicSchema.parse('14:30') // βœ“ Valid
218
+
219
+ // 12-hour format with AM/PM
220
+ const ampmSchema = time({ format: 'hh:mm A' })
221
+ ampmSchema.parse('02:30 PM') // βœ“ Valid
222
+
223
+ // Business hours validation
224
+ const businessHours = time({
225
+ format: 'HH:mm',
226
+ minHour: 9,
227
+ maxHour: 17,
228
+ minuteStep: 15 // Only :00, :15, :30, :45
229
+ })
230
+
231
+ // Time range validation
232
+ const timeRangeSchema = time({
233
+ min: '09:00',
234
+ max: '17:00'
43
235
  })
44
236
  ```
45
237
 
46
- ## Available Schemas
238
+ #### `date(options?)`
47
239
 
48
- ### Email
240
+ Date validation with range and format constraints.
49
241
 
50
242
  ```typescript
51
- email({
52
- label: string, // Field label for error messages
53
- required? : boolean, // Default: true
54
- domain? : string, // Restrict to specific domain
55
- min? : number, // Minimum length
56
- max? : number, // Maximum length
57
- includes? : string // Must include substring
243
+ import { date } from '@hy_ong/zod-kit'
244
+
245
+ const birthdateSchema = date({
246
+ format: 'YYYY-MM-DD',
247
+ minDate: '1900-01-01',
248
+ maxDate: new Date(),
249
+ timezone: 'Asia/Taipei'
58
250
  })
59
251
  ```
60
252
 
61
- ### Password
253
+ #### `file(options?)`
254
+
255
+ File validation with MIME type filtering and size constraints.
62
256
 
63
257
  ```typescript
64
- password({
65
- label: string, // Field label for error messages
66
- required? : boolean, // Default: true
67
- min? : number, // Minimum length
68
- max? : number, // Maximum length
69
- uppercase? : boolean, // Require uppercase letters
70
- lowercase? : boolean, // Require lowercase letters
71
- digits? : boolean, // Require digits
72
- special? : boolean // Require special characters
258
+ import { file } from '@hy_ong/zod-kit'
259
+
260
+ // Basic file validation
261
+ const basicSchema = file()
262
+ basicSchema.parse(new File(['content'], 'test.txt'))
263
+
264
+ // Size restrictions
265
+ const sizeSchema = file({
266
+ maxSize: 1024 * 1024, // 1MB
267
+ minSize: 1024 // 1KB
268
+ })
269
+
270
+ // Extension restrictions
271
+ const imageSchema = file({
272
+ extension: ['.jpg', '.png', '.gif'],
273
+ maxSize: 5 * 1024 * 1024 // 5MB
274
+ })
275
+
276
+ // MIME type restrictions
277
+ const documentSchema = file({
278
+ type: ['application/pdf', 'application/msword'],
279
+ maxSize: 10 * 1024 * 1024 // 10MB
73
280
  })
281
+
282
+ // Image files only
283
+ const imageOnlySchema = file({ imageOnly: true })
74
284
  ```
75
285
 
76
- ### Text
286
+ #### `id(options?)`
287
+
288
+ Flexible ID validation supporting multiple formats.
77
289
 
78
290
  ```typescript
79
- text({
80
- label: string, // Field label for error messages
81
- required? : boolean, // Default: true
82
- min? : number, // Minimum length
83
- max? : number, // Maximum length
84
- includes? : string, // Must include substring
85
- regex? : RegExp // Custom regex pattern
291
+ import { id } from '@hy_ong/zod-kit'
292
+
293
+ const userIdSchema = id({
294
+ type: 'uuid', // 'uuid', 'nanoid', 'objectId', 'auto', etc.
295
+ allowedTypes: ['uuid', 'nanoid'], // Multiple allowed types
296
+ customRegex: /^USR_[A-Z0-9]+$/ // Custom pattern
86
297
  })
87
298
  ```
88
299
 
89
- ### Number
300
+ ### Taiwan-Specific Validators
301
+
302
+ #### `nationalId(options?)`
303
+
304
+ Validates Taiwan National ID (θΊ«δ»½θ­‰ε­—θ™Ÿ).
90
305
 
91
306
  ```typescript
92
- number({
93
- label: string, // Field label for error messages
94
- required? : boolean, // Default: true
95
- min? : number, // Minimum value
96
- max? : number, // Maximum value
97
- positive? : boolean, // Must be positive
98
- negative? : boolean, // Must be negative
99
- finite? : boolean // Must be finite
307
+ import { nationalId } from '@hy_ong/zod-kit'
308
+
309
+ const idSchema = nationalId({
310
+ required: true,
311
+ normalize: true, // Convert to uppercase
312
+ whitelist: ['A123456789'] // Allow specific IDs
100
313
  })
314
+
315
+ idSchema.parse('A123456789') // βœ… Valid Taiwan National ID
101
316
  ```
102
317
 
103
- ### Integer
318
+ #### `businessId(options?)`
319
+
320
+ Validates Taiwan Business ID (η΅±δΈ€η·¨θ™Ÿ).
104
321
 
105
322
  ```typescript
106
- integer({
107
- label: string, // Field label for error messages
108
- required? : boolean, // Default: true
109
- min? : number, // Minimum value
110
- max? : number, // Maximum value
111
- positive? : boolean, // Must be positive
112
- negative? : boolean // Must be negative
323
+ import { businessId } from '@hy_ong/zod-kit'
324
+
325
+ const bizSchema = businessId()
326
+ bizSchema.parse('12345675') // βœ… Valid business ID with checksum
327
+ ```
328
+
329
+ #### `mobile(options?)`
330
+
331
+ Validates Taiwan mobile phone numbers.
332
+
333
+ ```typescript
334
+ import { mobile } from '@hy_ong/zod-kit'
335
+
336
+ const phoneSchema = mobile({
337
+ allowInternational: true, // Allow +886 prefix
338
+ allowSeparators: true, // Allow 0912-345-678
339
+ operators: ['09'] // Restrict to specific operators
113
340
  })
114
341
  ```
115
342
 
116
- ### URL
343
+ #### `tel(options?)`
344
+
345
+ Validates Taiwan landline telephone numbers.
117
346
 
118
347
  ```typescript
119
- url({
120
- label: string, // Field label for error messages
121
- required? : boolean, // Default: true
122
- protocol? : string[] // Allowed protocols (e.g., ['https'])
348
+ import { tel } from '@hy_ong/zod-kit'
349
+
350
+ const landlineSchema = tel({
351
+ allowSeparators: true, // Allow 02-1234-5678
352
+ areaCodes: ['02', '03'] // Restrict to specific areas
123
353
  })
124
354
  ```
125
355
 
126
- ### Boolean
356
+ #### `fax(options?)`
357
+
358
+ Validates Taiwan fax numbers (same format as landline).
359
+
360
+ ```typescript
361
+ import { fax } from '@hy_ong/zod-kit'
362
+
363
+ const faxSchema = fax()
364
+ faxSchema.parse('02-2345-6789') // βœ… Valid fax number
365
+ ```
366
+
367
+ #### `postalCode(options?)`
368
+
369
+ Validates Taiwan postal codes with support for 3-digit, 5-digit, and 6-digit formats.
127
370
 
128
371
  ```typescript
129
- boolean({
130
- label: string, // Field label for error messages
131
- required? : boolean // Default: true
372
+ import { postalCode } from '@hy_ong/zod-kit'
373
+
374
+ // Accept 3-digit or 6-digit formats (recommended)
375
+ const modernSchema = postalCode()
376
+ modernSchema.parse('100') // βœ… Valid 3-digit
377
+ modernSchema.parse('100001') // βœ… Valid 6-digit
378
+
379
+ // Accept all formats
380
+ const flexibleSchema = postalCode({ format: 'all' })
381
+ flexibleSchema.parse('100') // βœ… Valid
382
+ flexibleSchema.parse('10001') // βœ… Valid (5-digit legacy)
383
+ flexibleSchema.parse('100001') // βœ… Valid
384
+
385
+ // Only 6-digit format (current standard)
386
+ const modernOnlySchema = postalCode({ format: '6' })
387
+ modernOnlySchema.parse('100001') // βœ… Valid
388
+ modernOnlySchema.parse('100') // ❌ Invalid
389
+
390
+ // With dashes allowed
391
+ const dashSchema = postalCode({ allowDashes: true })
392
+ dashSchema.parse('100-001') // βœ… Valid (normalized to '100001')
393
+
394
+ // Specific areas only
395
+ const taipeiSchema = postalCode({
396
+ allowedPrefixes: ['100', '103', '104', '105', '106']
132
397
  })
398
+ taipeiSchema.parse('100001') // βœ… Valid (Taipei area)
399
+ taipeiSchema.parse('200001') // ❌ Invalid (not in allowlist)
133
400
  ```
134
401
 
135
- ## Internationalization
402
+ ## 🌐 Internationalization
136
403
 
137
- Set the locale for error messages:
404
+ Zod Kit supports multiple languages for error messages:
138
405
 
139
406
  ```typescript
140
407
  import { setLocale } from '@hy_ong/zod-kit'
141
408
 
142
- // Set to Traditional Chinese (default is English)
143
- setLocale('zh-TW')
409
+ // Set global locale
410
+ setLocale('zh-TW') // Traditional Chinese
411
+ setLocale('en') // English (default)
412
+
413
+ // Or use custom messages per validator
414
+ const emailSchema = email({
415
+ i18n: {
416
+ en: {
417
+ required: 'Email is required',
418
+ invalid: 'Please enter a valid email address'
419
+ },
420
+ 'zh-TW': {
421
+ required: 'θ«‹θΌΈε…₯電子郡仢',
422
+ invalid: 'θ«‹θΌΈε…₯ζœ‰ζ•ˆηš„ι›»ε­ιƒ΅δ»Άζ ΌεΌ'
423
+ }
424
+ }
425
+ })
426
+ ```
427
+
428
+ ## 🎨 Advanced Usage
429
+
430
+ ### Optional Fields
431
+
432
+ Make any field optional by setting `required: false`:
433
+
434
+ ```typescript
435
+ const optionalEmail = email({ required: false })
436
+
437
+ optionalEmail.parse(null) // βœ… null
438
+ optionalEmail.parse('') // βœ… null
439
+ optionalEmail.parse('test@example.com') // βœ… "test@example.com"
440
+ ```
441
+
442
+ ### Custom Transformations
144
443
 
145
- // Set to English (default)
146
- setLocale('en')
444
+ Transform values during validation:
445
+
446
+ ```typescript
447
+ const trimmedText = text({
448
+ transform: (val) => val.trim().toLowerCase(),
449
+ minLength: 1
450
+ })
451
+
452
+ trimmedText.parse(' HELLO ') // βœ… "hello"
147
453
  ```
148
454
 
149
- ## Optional Fields
455
+ ### Whitelist Validation
150
456
 
151
- All schemas support optional validation by setting `required: false`:
457
+ Allow specific values regardless of format:
152
458
 
153
459
  ```typescript
154
- const optionalEmail = email({
155
- label: 'Email',
156
- required: false
460
+ const flexibleId = id({
461
+ type: 'uuid',
462
+ whitelist: ['admin', 'system', 'test-user']
157
463
  })
158
464
 
159
- optionalEmail.parse(null) // βœ… null
160
- optionalEmail.parse('') // βœ… null
161
- optionalEmail.parse('user@example.com') // βœ… 'user@example.com'
465
+ flexibleId.parse('admin') // βœ… "admin"
466
+ flexibleId.parse('550e8400-e29b-41d4-a716-446655440000') // βœ… Valid UUID
162
467
  ```
163
468
 
164
- ## Development
469
+ ### Combining Validators
470
+
471
+ Use with Zod's composition features:
472
+
473
+ ```typescript
474
+ import { z } from 'zod'
475
+ import { email, password } from '@hy_ong/zod-kit'
476
+
477
+ const userSchema = z.object({
478
+ email: email(),
479
+ password: password({ minLength: 8 }),
480
+ confirmPassword: z.string()
481
+ }).refine(data => data.password === data.confirmPassword, {
482
+ message: "Passwords don't match"
483
+ })
484
+ ```
485
+
486
+ ## πŸ› οΈ Development
165
487
 
166
488
  ```bash
489
+ # Clone the repository
490
+ git clone https://github.com/hy-ong/zod-kit.git
491
+ cd zod-kit
492
+
167
493
  # Install dependencies
168
494
  npm install
169
495
 
@@ -172,12 +498,54 @@ npm test
172
498
 
173
499
  # Build the package
174
500
  npm run build
501
+
502
+ # Run tests in watch mode
503
+ npm run test:watch
504
+ ```
505
+
506
+ ## πŸ§ͺ Testing
507
+
508
+ The library includes comprehensive tests covering:
509
+
510
+ - βœ… All validator functions
511
+ - βœ… Edge cases and error conditions
512
+ - βœ… Internationalization
513
+ - βœ… TypeScript type safety
514
+ - βœ… Taiwan-specific format validation
515
+
516
+ ```bash
517
+ npm test # Run all tests
518
+ npm run test:coverage # Run with coverage report
175
519
  ```
176
520
 
177
- ## License
521
+ ## πŸ“„ License
522
+
523
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
524
+
525
+ ## 🀝 Contributing
526
+
527
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
528
+
529
+ 1. Fork the repository
530
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
531
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
532
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
533
+ 5. Open a Pull Request
534
+
535
+ ## πŸ“Š Project Stats
536
+
537
+ ![GitHub stars](https://img.shields.io/github/stars/hy-ong/zod-kit?style=social)
538
+ ![GitHub forks](https://img.shields.io/github/forks/hy-ong/zod-kit?style=social)
539
+ ![GitHub issues](https://img.shields.io/github/issues/hy-ong/zod-kit)
540
+
541
+ ## πŸ™ Acknowledgments
542
+
543
+ - Built on top of the amazing [Zod](https://github.com/colinhacks/zod) library
544
+ - Inspired by real-world validation needs in Taiwan's tech ecosystem
545
+ - Thanks to all contributors and users
178
546
 
179
- MIT
547
+ ---
180
548
 
181
- ## Author
549
+ **Made with ❀️ by [Ong Hoe Yuan](https://github.com/hy-ong)**
182
550
 
183
- Ong Hoe Yuan
551
+ For questions or support, please [open an issue](https://github.com/hy-ong/zod-kit/issues) on GitHub.