@theunwalked/cardigantime 0.0.10 → 0.0.11

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 (2) hide show
  1. package/README.md +26 -1383
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -7,7 +7,7 @@ A robust TypeScript library for configuration management in command-line applica
7
7
  Cardigantime is a configuration management library designed to solve the common problem of handling configuration in CLI applications. It provides a unified way to:
8
8
 
9
9
  - **Read configuration from YAML files** with intelligent file discovery
10
- - **Validate configuration** using Zod schemas for type safetygit sta
10
+ - **Validate configuration** using Zod schemas for type safety
11
11
  - **Integrate with CLI frameworks** like Commander.js seamlessly
12
12
  - **Merge configuration sources** (files, CLI args, defaults) with proper precedence
13
13
  - **Handle errors gracefully** with comprehensive logging and user-friendly error messages
@@ -16,119 +16,13 @@ Cardigantime is a configuration management library designed to solve the common
16
16
 
17
17
  Building CLI applications with proper configuration management is harder than it should be. **Cardigantime was created specifically to solve the complex problem of supporting sophisticated configuration systems that seamlessly merge command-line arguments, configuration files, and default values.**
18
18
 
19
- ### The Configuration Complexity Problem
19
+ Without Cardigantime, you need to manually handle:
20
+ - Multi-layered configuration sources with proper precedence
21
+ - Nested configuration objects with deep validation
22
+ - Type safety throughout the configuration pipeline
23
+ - Graceful error handling with actionable messages
20
24
 
21
- Modern CLI applications need to handle increasingly complex configuration scenarios:
22
-
23
- - **Multi-layered configuration sources** with proper precedence (CLI args > config files > defaults)
24
- - **Nested configuration objects** with deep validation requirements
25
- - **Environment-specific configurations** (development, staging, production)
26
- - **Dynamic feature flags** and optional modules
27
- - **Type safety** throughout the entire configuration pipeline
28
- - **User-friendly error messages** when configuration goes wrong
29
-
30
- ### What You Need to Handle
31
-
32
- Without Cardigantime, building robust configuration management requires:
33
-
34
- 1. **Parse command-line arguments** - handled by Commander.js, but integration is manual
35
- 2. **Read configuration files** - YAML/JSON parsing with proper error handling
36
- 3. **Implement sophisticated merging logic** - CLI args should override file config, which should override defaults, with proper deep merging
37
- 4. **Validate complex nested structures** - ensure required fields exist, types are correct, and business rules are followed
38
- 5. **Handle edge cases gracefully** - missing files, malformed YAML, permission errors, invalid paths
39
- 6. **Provide actionable error messages** - users need to know exactly what's wrong and how to fix it
40
- 7. **Maintain type safety** - TypeScript support with proper IntelliSense throughout the entire pipeline
41
- 8. **Support advanced scenarios** - schema evolution, backward compatibility, configuration discovery
42
-
43
- ### The Manual Approach Pain Points
44
-
45
- Implementing this manually leads to common problems:
46
-
47
- ```typescript
48
- // Typical manual configuration merging - fragile and error-prone
49
- const config = {
50
- ...defaultConfig, // Defaults
51
- ...yamlConfig, // File config
52
- ...processCliArgs(args), // CLI overrides
53
- };
54
-
55
- // Problems:
56
- // ❌ Shallow merging loses nested structure
57
- // ❌ No validation until runtime failures
58
- // ❌ Poor error messages: "Cannot read property 'x' of undefined"
59
- // ❌ Type safety lost after merging
60
- // ❌ No protection against typos in config files
61
- // ❌ Manual path resolution and security checks
62
- ```
63
-
64
- ### How Cardigantime Solves This
65
-
66
- Cardigantime provides a complete, battle-tested solution:
67
-
68
- ```typescript
69
- // Cardigantime approach - robust and type-safe
70
- const cardigantime = create({
71
- defaults: { configDirectory: './config' },
72
- configShape: ComplexConfigSchema.shape, // Full type safety
73
- });
74
-
75
- const config = await cardigantime.read(args); // Smart merging
76
- await cardigantime.validate(config); // Comprehensive validation
77
-
78
- // Benefits:
79
- // ✅ Deep merging preserves nested structures
80
- // ✅ Schema validation with detailed error messages
81
- // ✅ Full TypeScript support with IntelliSense
82
- // ✅ Typo detection and helpful suggestions
83
- // ✅ Built-in security protections
84
- // ✅ Graceful error handling with actionable messages
85
- ```
86
-
87
- ### Real-World Example: Complex Configuration
88
-
89
- Here's the kind of complex configuration Cardigantime was designed to handle:
90
-
91
- ```typescript
92
- const ComplexConfigSchema = z.object({
93
- // Database configuration with multiple environments
94
- database: z.object({
95
- primary: z.object({
96
- host: z.string().default('localhost'),
97
- port: z.number().min(1).max(65535).default(5432),
98
- ssl: z.boolean().default(false),
99
- }),
100
- replicas: z.array(z.string().url()).default([]),
101
- maxConnections: z.number().positive().default(10),
102
- }),
103
-
104
- // Feature flags and optional modules
105
- features: z.record(z.boolean()).default({}),
106
-
107
- // API configuration with validation
108
- api: z.object({
109
- key: z.string().min(32, "API key must be at least 32 characters"),
110
- timeout: z.number().min(1000).max(30000).default(5000),
111
- retries: z.number().min(0).max(10).default(3),
112
- baseUrl: z.string().url(),
113
- }),
114
-
115
- // Logging configuration
116
- logging: z.object({
117
- level: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
118
- outputs: z.array(z.enum(['console', 'file', 'syslog'])).default(['console']),
119
- rotation: z.object({
120
- maxSize: z.string().regex(/^\d+[KMG]B$/),
121
- maxFiles: z.number().positive().default(5),
122
- }).optional(),
123
- }),
124
- });
125
-
126
- // Users can now run:
127
- // ./myapp --api-timeout 10000 --features-analytics true --config-directory ./prod-config
128
- // And everything just works with full validation and type safety
129
- ```
130
-
131
- Cardigantime handles all of this complexity while providing excellent developer experience and robust error handling. **It was specifically created because existing solutions either lacked the sophistication needed for complex configuration scenarios or required too much boilerplate code to achieve proper integration between CLI arguments, configuration files, and defaults.**
25
+ Cardigantime provides a complete, battle-tested solution for all of this complexity.
132
26
 
133
27
  ## Installation
134
28
 
@@ -229,1293 +123,42 @@ debug: true
229
123
  # Use different config directory
230
124
  ./myapp --config-directory /etc/myapp
231
125
 
232
- # Enable debug mode
233
- ./myapp --debug
234
-
235
126
  # Generate initial configuration file
236
127
  ./myapp --init-config
237
128
 
238
- # Analyze configuration with source tracking (git blame-like output)
129
+ # Analyze configuration with source tracking
239
130
  ./myapp --check-config
240
-
241
- # Generate config in custom directory, then analyze it
242
- ./myapp --config-directory ./prod-config --init-config
243
- ./myapp --config-directory ./prod-config --check-config
244
- ```
245
-
246
- ### Advanced Usage Examples
247
-
248
- #### Basic Configuration with Path Resolution
249
-
250
- ```typescript
251
- import { create } from '@theunwalked/cardigantime';
252
- import { z } from 'zod';
253
-
254
- const MyConfigSchema = z.object({
255
- apiKey: z.string().min(1),
256
- timeout: z.number().default(5000),
257
- debug: z.boolean().default(false),
258
- contextDirectories: z.array(z.string()).optional(),
259
- });
260
-
261
- const cardigantime = create({
262
- defaults: {
263
- configDirectory: './config',
264
- configFile: 'myapp.yaml',
265
- // Resolve relative paths in contextDirectories relative to config file location
266
- pathResolution: {
267
- pathFields: ['contextDirectories'],
268
- resolvePathArray: ['contextDirectories']
269
- }
270
- },
271
- configShape: MyConfigSchema.shape,
272
- });
273
131
  ```
274
132
 
275
- #### Hierarchical Configuration with Custom Array Overlap
276
-
277
- ```typescript
278
- const cardigantime = create({
279
- defaults: {
280
- configDirectory: '.myapp',
281
- configFile: 'config.yaml',
282
- fieldOverlaps: {
283
- 'features': 'append', // Accumulate features from all levels
284
- 'excludePatterns': 'prepend', // Higher precedence patterns come first
285
- 'api.endpoints': 'append', // Nested field configuration
286
- 'security.allowedOrigins': 'append' // Security settings accumulate
287
- }
288
- },
289
- configShape: MyConfigSchema.shape,
290
- features: ['config', 'hierarchical'], // Enable hierarchical discovery
291
- });
292
- ```
293
-
294
- This configuration enables powerful composition scenarios where:
295
- - **Features** from all configuration levels are combined (e.g., base features + project features + local features)
296
- - **Exclude patterns** are layered with local patterns taking precedence
297
- - **API endpoints** can be extended at each level
298
- - **Security settings** accumulate for maximum flexibility
299
-
300
- ## Core Concepts
301
-
302
- ### 1. Configuration Sources & Precedence
303
-
304
- Cardigantime merges configuration from multiple sources in this order (highest to lowest priority):
133
+ ## Key Features
305
134
 
135
+ ### Configuration Sources & Precedence
136
+ Merges configuration from multiple sources in order of precedence:
306
137
  1. **Command-line arguments** (highest priority)
307
138
  2. **Configuration file(s)** (medium priority)
308
139
  3. **Default values** (lowest priority)
309
140
 
310
- ```typescript
311
- // If you have this config file:
312
- // timeout: 5000
313
- // debug: false
314
-
315
- // And run: ./myapp --timeout 10000
316
-
317
- // The final config will be:
318
- // timeout: 10000 (from CLI, overrides file)
319
- // debug: false (from file)
320
- ```
321
-
322
- ### 2. Hierarchical Configuration Discovery
323
-
324
- Cardigantime supports hierarchical configuration discovery, similar to how tools like `.gitignore`, `.eslintrc`, or `package.json` work. When the `hierarchical` feature is enabled, Cardigantime will:
325
-
326
- 1. **Start from the specified config directory** (e.g., `./project/subdir/.kodrdriv`)
327
- 2. **Search up the directory tree** for additional config directories with the same name
328
- 3. **Merge configurations** with proper precedence (closer directories win)
329
- 4. **Apply CLI arguments** as the final override
330
-
331
- #### Example Directory Structure
332
-
333
- ```
334
- /home/user/projects/
335
- ├── .kodrdriv/
336
- │ └── config.yaml # Root-level config
337
- ├── myproject/
338
- │ ├── .kodrdriv/
339
- │ │ └── config.yaml # Project-level config
340
- │ └── submodule/
341
- │ ├── .kodrdriv/
342
- │ │ └── config.yaml # Submodule-level config
343
- │ └── my-script.js
344
- ```
345
-
346
- #### Hierarchical Discovery Behavior
347
-
348
- When running from `/home/user/projects/myproject/submodule/` with hierarchical discovery:
349
-
350
- 1. **Level 0 (Highest Priority)**: `/home/user/projects/myproject/submodule/.kodrdriv/config.yaml`
351
- 2. **Level 1**: `/home/user/projects/myproject/.kodrdriv/config.yaml`
352
- 3. **Level 2 (Lowest Priority)**: `/home/user/projects/.kodrdriv/config.yaml`
353
-
354
- Configurations are deep-merged, with closer directories taking precedence:
355
-
356
- ```yaml
357
- # /home/user/projects/.kodrdriv/config.yaml (Level 2)
358
- database:
359
- host: localhost
360
- port: 5432
361
- ssl: false
362
- logging:
363
- level: info
364
- features:
365
- - auth
366
- - basic-logging
367
-
368
- # /home/user/projects/myproject/.kodrdriv/config.yaml (Level 1)
369
- database:
370
- port: 5433
371
- ssl: true
372
- api:
373
- timeout: 5000
374
- features:
375
- - advanced-logging
376
- - metrics
377
-
378
- # /home/user/projects/myproject/submodule/.kodrdriv/config.yaml (Level 0)
379
- database:
380
- host: dev.example.com
381
- logging:
382
- level: debug
383
- features:
384
- - debug-mode
385
-
386
- # Final merged configuration (with default array behavior):
387
- database:
388
- host: dev.example.com # From Level 0 (highest precedence)
389
- port: 5433 # From Level 1
390
- ssl: true # From Level 1
391
- api:
392
- timeout: 5000 # From Level 1
393
- logging:
394
- level: debug # From Level 0 (highest precedence)
395
- features:
396
- - debug-mode # From Level 0 (arrays override by default)
397
- ```
398
-
399
- #### Configurable Array Overlap Behavior
400
-
401
- By default, arrays in hierarchical configurations follow the **override** behavior - arrays from higher precedence levels completely replace arrays from lower precedence levels. However, you can configure custom overlap behavior for array fields:
402
-
403
- ```typescript
404
- const cardigantime = create({
405
- defaults: {
406
- configDirectory: '.kodrdriv',
407
- configFile: 'config.yaml',
408
- fieldOverlaps: {
409
- 'features': 'append', // Combine features by appending
410
- 'excludePatterns': 'prepend', // Combine exclude patterns by prepending
411
- 'middlewares': 'override' // Override middlewares (default behavior)
412
- }
413
- },
414
- configShape: MyConfigSchema.shape,
415
- features: ['config', 'hierarchical'],
416
- });
417
- ```
418
-
419
- **Available Overlap Modes:**
420
-
421
- - **`override`** (default): Higher precedence arrays completely replace lower precedence arrays
422
- - **`append`**: Higher precedence array elements are appended to lower precedence arrays
423
- - **`prepend`**: Higher precedence array elements are prepended to lower precedence arrays
424
-
425
- **Example with Custom Array Overlap:**
426
-
427
- ```yaml
428
- # /home/user/projects/.kodrdriv/config.yaml (Level 2)
429
- features: ['auth', 'basic-logging']
430
- excludePatterns: ['*.tmp', '*.cache']
431
-
432
- # /home/user/projects/myproject/.kodrdriv/config.yaml (Level 1)
433
- features: ['advanced-logging', 'metrics']
434
- excludePatterns: ['*.log']
435
-
436
- # /home/user/projects/myproject/submodule/.kodrdriv/config.yaml (Level 0)
437
- features: ['debug-mode']
438
- excludePatterns: ['*.debug']
439
- ```
440
-
441
- With the configuration above (`features: 'append'`, `excludePatterns: 'prepend'`):
442
-
443
- ```yaml
444
- # Final merged configuration:
445
- features:
446
- - auth # From Level 2
447
- - basic-logging # From Level 2
448
- - advanced-logging # From Level 1
449
- - metrics # From Level 1
450
- - debug-mode # From Level 0 (appended)
451
- excludePatterns:
452
- - "*.debug" # From Level 0 (prepended first)
453
- - "*.log" # From Level 1 (prepended second)
454
- - "*.tmp" # From Level 2
455
- - "*.cache" # From Level 2
456
- ```
457
-
458
- **Nested Field Paths:**
459
-
460
- You can configure overlap behavior for nested array fields using dot notation:
461
-
462
- ```typescript
463
- fieldOverlaps: {
464
- 'api.endpoints': 'append',
465
- 'database.migrations': 'prepend',
466
- 'config.features.experimental': 'override'
467
- }
468
- ```
469
-
470
- #### Enabling Hierarchical Discovery
471
-
472
- ```typescript
473
- const cardigantime = create({
474
- defaults: {
475
- configDirectory: '.kodrdriv',
476
- configFile: 'config.yaml'
477
- },
478
- configShape: MyConfigSchema.shape,
479
- features: ['config', 'hierarchical'], // Enable hierarchical discovery
480
- });
481
- ```
482
-
483
- #### Hierarchical Discovery Options
484
-
485
- The hierarchical discovery has several built-in protections and features:
486
-
487
- - **Maximum traversal depth**: Prevents infinite loops (default: 10 levels)
488
- - **Symlink protection**: Tracks visited paths to prevent circular references
489
- - **Graceful fallback**: Falls back to single-directory mode if discovery fails
490
- - **Error tolerance**: Continues discovery even if some directories are unreadable
491
- - **Root detection**: Automatically stops at filesystem root
492
-
493
- #### Use Cases for Hierarchical Configuration
494
-
495
- 1. **Monorepos**: Share common configuration across multiple packages
496
- 2. **Project inheritance**: Override team/organization defaults for specific projects
497
- 3. **Environment layering**: Different configs for development/staging/production
498
- 4. **Tool configuration**: Similar to how ESLint or Prettier find configs up the tree
499
- 5. **Multi-tenant applications**: Tenant-specific overrides of global settings
500
-
501
- ### 3. Schema Validation
502
-
503
- All configuration is validated against your Zod schema:
504
-
505
- ```typescript
506
- const ConfigSchema = z.object({
507
- port: z.number().min(1).max(65535),
508
- host: z.string().ip().or(z.literal('localhost')),
509
- database: z.object({
510
- url: z.string().url(),
511
- maxConnections: z.number().positive().default(10),
512
- }),
513
- features: z.array(z.enum(['auth', 'analytics', 'logging'])).default([]),
514
- });
515
-
516
- const cardigantime = create({
517
- defaults: { configDirectory: './config' },
518
- configShape: ConfigSchema.shape,
519
- });
520
- ```
521
-
522
- ### 4. Type Safety
523
-
524
- Cardigantime provides full TypeScript support:
525
-
526
- ```typescript
527
- // The config object is fully typed
528
- const config = await cardigantime.read(args);
529
- // config.database.maxConnections is number
530
- // config.features is ('auth' | 'analytics' | 'logging')[]
531
- // config.port is number
532
-
533
- // IntelliSense works everywhere
534
- if (config.features.includes('auth')) {
535
- // Setup authentication
536
- }
537
- ```
538
-
539
- ### 5. Error Handling
540
-
541
- Cardigantime provides detailed error messages for common issues:
542
-
543
- ```typescript
544
- try {
545
- await cardigantime.validate(config);
546
- } catch (error) {
547
- // Detailed validation errors:
548
- // "Configuration validation failed: port must be between 1 and 65535"
549
- // "Unknown configuration keys found: typoKey. Allowed keys are: port, host, database"
550
- // "Config directory does not exist and is required: /nonexistent/path"
551
- }
552
- ```
553
-
554
- ## API Reference
555
-
556
- ### `create(options)`
557
-
558
- Creates a new Cardigantime instance.
559
-
560
- **Parameters:**
561
- - `options.defaults` (required): Default configuration options
562
- - `configDirectory` (required): Directory to look for config files
563
- - `configFile` (optional): Config filename, defaults to `'config.yaml'`
564
- - `isRequired` (optional): Whether config directory must exist, defaults to `false`
565
- - `encoding` (optional): File encoding, defaults to `'utf8'`
566
- - `pathResolution` (optional): Configuration for resolving relative paths
567
- - `fieldOverlaps` (optional): Array merge behavior for hierarchical mode
568
- - `options.configShape` (required): Zod schema shape for validation
569
- - `options.features` (optional): Array of features to enable, defaults to `['config']`
570
- - `options.logger` (optional): Custom logger implementation
571
-
572
- **Returns:** `Cardigantime` instance with methods:
573
- - `configure(command)`: Add CLI options to Commander.js command
574
- - `read(args)`: Read and merge configuration from all sources
575
- - `validate(config)`: Validate configuration against schema
576
- - `generateConfig(dir?)`: Generate config file with defaults
577
- - `checkConfig(args)`: Analyze configuration with source tracking
578
- - `setLogger(logger)`: Set custom logger
579
-
580
- ### `cardigantime.configure(command)`
581
-
582
- Adds Cardigantime's CLI options to a Commander.js command.
583
-
584
- **Parameters:**
585
- - `command`: Commander.js Command instance
586
-
587
- **Returns:** Promise<Command> - The modified command
588
-
589
- **Added CLI Options:**
590
- - `-c, --config-directory <path>`: Override the default configuration directory path
591
- - `--init-config`: Generate initial configuration file with default values and exit
592
- - `--check-config`: Display resolved configuration with source tracking and exit
593
-
594
- ### `cardigantime.read(args)`
595
-
596
- Reads and merges configuration from all sources.
597
-
598
- **Parameters:**
599
- - `args`: Parsed command-line arguments object
600
-
601
- **Returns:** Promise<Config> - Merged and typed configuration object
602
-
603
- ### `cardigantime.validate(config)`
604
-
605
- Validates configuration against the schema.
606
-
607
- **Parameters:**
608
- - `config`: Configuration object to validate
609
-
610
- **Returns:** Promise<void> - Throws on validation failure
611
-
612
- ### `cardigantime.generateConfig(configDirectory?)`
613
-
614
- Generates a configuration file with default values from your Zod schema.
615
-
616
- **Parameters:**
617
- - `configDirectory` (optional): Target directory for the config file. Uses default if not specified.
618
-
619
- **Returns:** Promise<void> - Resolves when file is created
620
-
621
- **Features:**
622
- - Creates the directory if it doesn't exist
623
- - Generates YAML with all default values from your schema
624
- - Includes helpful comments and formatting
625
- - Won't overwrite existing files (shows preview instead)
626
-
627
- ### `cardigantime.checkConfig(args)`
628
-
629
- Analyzes and displays resolved configuration with detailed source tracking.
630
-
631
- **Parameters:**
632
- - `args`: Parsed command-line arguments object
633
-
634
- **Returns:** Promise<void> - Displays analysis and exits
635
-
636
- **Features:**
637
- - Shows which file/level contributed each configuration value
638
- - Git blame-like output format
639
- - Hierarchical source tracking
640
- - Precedence visualization
641
- - Summary statistics
642
-
643
- ### `cardigantime.setLogger(logger)`
644
-
645
- Sets a custom logger for debugging and error reporting.
646
-
647
- **Parameters:**
648
- - `logger`: Logger implementing the Logger interface
649
-
650
- ## Configuration Options Reference
651
-
652
- Cardigantime provides extensive configuration options to customize how configuration files are loaded, processed, and merged. Here's a comprehensive reference of all available options.
653
-
654
- ### Default Options (`defaults`)
655
-
656
- All options passed to the `defaults` property when creating a Cardigantime instance:
657
-
658
- #### Required Options
659
-
660
- **`configDirectory`** (string, required)
661
- - Directory path where configuration files are located
662
- - Can be relative (`./config`) or absolute (`/etc/myapp`)
663
- - Will be resolved relative to the current working directory
664
- - Example: `'./config'`, `'/etc/myapp'`, `'~/.config/myapp'`
665
-
666
- #### Optional Options
667
-
668
- **`configFile`** (string, optional)
669
- - Name of the configuration file within the config directory
670
- - Default: `'config.yaml'`
671
- - Supports any YAML file extension (`.yaml`, `.yml`)
672
- - Example: `'app.yaml'`, `'settings.yml'`, `'myapp-config.yaml'`
673
-
674
- **`isRequired`** (boolean, optional)
675
- - Whether the configuration directory must exist
676
- - Default: `false`
677
- - When `true`: Throws error if directory doesn't exist
678
- - When `false`: Continues with empty config if directory is missing
679
- - Useful for applications that can run without configuration files
680
-
681
- **`encoding`** (string, optional)
682
- - File encoding for reading configuration files
683
- - Default: `'utf8'`
684
- - Common values: `'utf8'`, `'ascii'`, `'utf16le'`, `'latin1'`
685
- - Must be a valid Node.js buffer encoding
686
-
687
- **`pathResolution`** (PathResolutionOptions, optional)
688
- - Configuration for resolving relative paths in configuration values
689
- - Paths are resolved relative to the configuration file's directory
690
- - Useful for making configuration portable across environments
691
-
692
- `pathResolution.pathFields` (string[], optional)
693
- - Array of field names (using dot notation) that contain paths to be resolved
694
- - Example: `['outputDir', 'logFile', 'database.backupPath']`
695
- - Supports nested fields using dot notation
696
-
697
- `pathResolution.resolvePathArray` (string[], optional)
698
- - Array of field names whose array elements should all be resolved as paths
699
- - Example: `['includePaths', 'excludePatterns']`
700
- - Only affects array fields - individual elements that are strings will be resolved as paths
701
-
702
- **Example:**
703
- ```typescript
704
- const cardigantime = create({
705
- defaults: {
706
- configDirectory: './config',
707
- configFile: 'myapp.yaml',
708
- isRequired: true,
709
- encoding: 'utf8',
710
- pathResolution: {
711
- pathFields: ['outputDir', 'logFile', 'database.backupDir'],
712
- resolvePathArray: ['includePaths', 'watchDirectories']
713
- }
714
- },
715
- configShape: MySchema.shape
716
- });
717
- ```
718
-
719
- If your config file at `./config/myapp.yaml` contains:
720
- ```yaml
721
- outputDir: ./build # Resolved to ./config/build
722
- logFile: ../logs/app.log # Resolved to ./logs/app.log
723
- includePaths: # Each element resolved as path
724
- - ./src
725
- - ../shared
726
- database:
727
- backupDir: ./backups # Resolved to ./config/backups
728
- ```
729
-
730
- **`fieldOverlaps`** (FieldOverlapOptions, optional)
731
- - Configuration for how array fields should be merged in hierarchical mode
732
- - Only applies when the `'hierarchical'` feature is enabled
733
- - Controls whether arrays are replaced, combined, or prepended during hierarchical merging
734
- - Default: All arrays use `'override'` behavior
735
-
736
- **Available overlap modes:**
737
- - `'override'` (default): Higher precedence arrays completely replace lower precedence arrays
738
- - `'append'`: Higher precedence array elements are appended to lower precedence arrays
739
- - `'prepend'`: Higher precedence array elements are prepended to lower precedence arrays
740
-
741
- **Example:**
742
- ```typescript
743
- const cardigantime = create({
744
- defaults: {
745
- configDirectory: '.myapp',
746
- fieldOverlaps: {
747
- 'features': 'append', // Combine features from all levels
748
- 'excludePatterns': 'prepend', // Higher precedence first
749
- 'api.endpoints': 'append', // Nested field configuration
750
- 'middleware.stack': 'override' // Replace entirely (default)
751
- }
752
- },
753
- features: ['config', 'hierarchical'],
754
- configShape: MySchema.shape
755
- });
756
- ```
757
-
758
- With this configuration and hierarchy:
759
- ```yaml
760
- # Parent level (lower precedence)
761
- features: ['auth', 'logging']
762
- excludePatterns: ['*.tmp']
763
-
764
- # Child level (higher precedence)
765
- features: ['analytics']
766
- excludePatterns: ['*.log']
767
- ```
768
-
769
- Results in:
770
- ```yaml
771
- features: ['auth', 'logging', 'analytics'] # append mode
772
- excludePatterns: ['*.log', '*.tmp'] # prepend mode
773
- ```
774
-
775
- ### Instance Options
776
-
777
- **`features`** (Feature[], optional)
778
- - Array of features to enable in the Cardigantime instance
779
- - Default: `['config']`
780
- - Available features:
781
- - `'config'`: Basic configuration file loading and validation
782
- - `'hierarchical'`: Hierarchical configuration discovery and merging
783
-
784
- **`configShape`** (ZodRawShape, required)
785
- - Zod schema shape defining your configuration structure
786
- - Used for validation and generating default configuration files
787
- - Must be the `.shape` property of a Zod object schema
788
- - Provides TypeScript type inference for the final configuration
789
-
790
- **`logger`** (Logger, optional)
791
- - Custom logger implementation for debugging and error reporting
792
- - Default: Console-based logger
793
- - Must implement the Logger interface with methods: `debug`, `info`, `warn`, `error`, `verbose`, `silly`
794
-
795
- **Complete example:**
796
- ```typescript
797
- import { create } from '@theunwalked/cardigantime';
798
- import { z } from 'zod';
799
- import winston from 'winston';
800
-
801
- const MyConfigSchema = z.object({
802
- api: z.object({
803
- endpoint: z.string().url(),
804
- timeout: z.number().default(5000),
805
- retries: z.number().default(3)
806
- }),
807
- features: z.array(z.string()).default(['auth']),
808
- debug: z.boolean().default(false),
809
- outputDir: z.string().default('./output'),
810
- includePaths: z.array(z.string()).default([])
811
- });
812
-
813
- const customLogger = winston.createLogger({
814
- level: 'debug',
815
- format: winston.format.json(),
816
- transports: [new winston.transports.Console()]
817
- });
818
-
819
- const cardigantime = create({
820
- // Default options
821
- defaults: {
822
- configDirectory: './.myapp-config',
823
- configFile: 'config.yaml',
824
- isRequired: false,
825
- encoding: 'utf8',
826
- pathResolution: {
827
- pathFields: ['outputDir'],
828
- resolvePathArray: ['includePaths']
829
- },
830
- fieldOverlaps: {
831
- 'features': 'append',
832
- 'includePaths': 'append'
833
- }
834
- },
835
- // Instance options
836
- features: ['config', 'hierarchical'],
837
- configShape: MyConfigSchema.shape,
838
- logger: customLogger
839
- });
840
- ```
841
-
842
- ### Environment-Specific Configuration
843
-
844
- You can dynamically configure Cardigantime based on environment variables:
845
-
846
- ```typescript
847
- const environment = process.env.NODE_ENV || 'development';
848
-
849
- const cardigantime = create({
850
- defaults: {
851
- configDirectory: `./config/${environment}`,
852
- configFile: `${environment}.yaml`,
853
- isRequired: environment === 'production', // Require config in prod
854
- pathResolution: {
855
- pathFields: environment === 'development' ? ['outputDir'] : []
856
- }
857
- },
858
- features: environment === 'development' ? ['config'] : ['config', 'hierarchical'],
859
- configShape: MySchema.shape
860
- });
861
- ```
862
-
863
- ### Configuration Discovery and Precedence
864
-
865
- When using hierarchical configuration (`features: ['config', 'hierarchical']`):
866
-
867
- 1. **Discovery**: Cardigantime searches up the directory tree from the specified config directory
868
- 2. **Loading**: Loads configuration files from each discovered directory
869
- 3. **Merging**: Merges configurations with proper precedence (closer directories win)
870
- 4. **CLI Override**: Command-line arguments take final precedence over all file sources
871
-
872
- **Example discovery path:**
873
- Starting from `/project/subdir/.myapp-config`:
874
- 1. `/project/subdir/.myapp-config/config.yaml` (Level 0 - highest precedence)
875
- 2. `/project/.myapp-config/config.yaml` (Level 1 - lower precedence)
876
- 3. `/.myapp-config/config.yaml` (Level 2 - lowest precedence)
877
-
878
- ## Configuration Analysis & Debugging
879
-
880
- Cardigantime provides powerful tools for understanding how your configuration is resolved and where values come from. These are especially useful when working with complex hierarchical configurations.
881
-
882
- ### Configuration Generation (`--init-config`)
883
-
884
- The `--init-config` option generates a complete configuration file with all default values from your Zod schema:
885
-
886
- ```bash
887
- # Generate config file in default directory
888
- ./myapp --init-config
889
-
890
- # Generate config file in custom directory
891
- ./myapp --config-directory ./my-config --init-config
892
- ```
893
-
894
- **What it does:**
895
- - Creates the target directory if it doesn't exist
896
- - Generates a YAML file with all default values from your schema
897
- - Includes helpful comments and proper formatting
898
- - Won't overwrite existing files (shows what would be generated instead)
899
-
900
- **Example generated file:**
901
- ```yaml
902
- # Configuration file generated by Cardigantime
903
- # This file contains default values for your application configuration.
904
- # Modify the values below to customize your application's behavior.
905
-
906
- api:
907
- endpoint: https://api.example.com
908
- retries: 3
909
- timeout: 5000
910
- debug: false
911
- excludePatterns:
912
- - "*.tmp"
913
- - "*.log"
914
- features:
915
- - auth
916
- - logging
917
- outputDir: ./output
918
- ```
919
-
920
- **Programmatic usage:**
921
- ```typescript
922
- // Generate config file programmatically
923
- await cardigantime.generateConfig('./production-config');
924
- ```
925
-
926
- ### Configuration Source Analysis (`--check-config`)
927
-
928
- The `--check-config` option provides a git blame-like view of your configuration, showing exactly which file and hierarchical level contributed each configuration value:
929
-
930
- ```bash
931
- # Analyze configuration with source tracking
932
- ./myapp --check-config
933
-
934
- # Analyze with custom config directory
935
- ./myapp --config-directory ./prod-config --check-config
936
- ```
937
-
938
- **Example output:**
939
- ```
940
- ================================================================================
941
- CONFIGURATION SOURCE ANALYSIS
942
- ================================================================================
943
-
944
- DISCOVERED CONFIGURATION HIERARCHY:
945
- Level 0: /project/subdir/.myapp-config (highest precedence)
946
- Level 1: /project/.myapp-config (lowest precedence)
947
-
948
- RESOLVED CONFIGURATION WITH SOURCES:
949
- Format: [Source] key: value
950
-
951
- [Level 0: subdir ] api.endpoint : "https://api.child.com"
952
- [Level 1: project ] api.retries : 3
953
- [Level 0: subdir ] api.timeout : 10000
954
- [Built-in (runtime) ] configDirectory : "/project/subdir/.myapp-config"
955
- [Level 0: subdir ] debug : true
956
- [Level 1: project ] excludePatterns : ["*.tmp", "*.log"]
957
- [Level 0: subdir ] features : ["auth", "logging", "analytics"]
958
-
959
- --------------------------------------------------------------------------------
960
- SUMMARY:
961
- Total configuration keys: 7
962
- Configuration sources: 2
963
- Values by source:
964
- Level 0: subdir: 4 value(s)
965
- Level 1: project: 2 value(s)
966
- Built-in (runtime): 1 value(s)
967
- ================================================================================
968
- ```
969
-
970
- **Key features:**
971
- - **Source tracking**: Shows exactly which file provided each value
972
- - **Hierarchical visualization**: Displays precedence levels clearly
973
- - **Conflict resolution**: Shows how higher precedence values override lower ones
974
- - **Array merging insight**: For hierarchical configs with custom `fieldOverlaps`
975
- - **Built-in values**: Tracks runtime-generated configuration values
976
-
977
- **Programmatic usage:**
978
- ```typescript
979
- // Analyze configuration programmatically
980
- await cardigantime.checkConfig(args);
981
- ```
982
-
983
- ### Debugging Complex Configurations
984
-
985
- These tools are especially powerful for debugging complex scenarios:
986
-
987
- #### Hierarchical Configuration Debugging
988
-
989
- When using hierarchical configuration with custom array merging:
990
-
991
- ```typescript
992
- const cardigantime = create({
993
- defaults: {
994
- configDirectory: '.myapp',
995
- fieldOverlaps: {
996
- 'features': 'append', // Combine features from all levels
997
- 'excludePatterns': 'prepend' // Higher precedence first
998
- }
999
- },
1000
- features: ['config', 'hierarchical'],
1001
- configShape: MySchema.shape
1002
- });
1003
- ```
1004
-
1005
- Use `--check-config` to see exactly how arrays are being merged:
1006
-
1007
- ```bash
1008
- ./myapp --check-config
1009
- # Shows which level contributed each array element
1010
- ```
1011
-
1012
- #### Configuration Validation Issues
1013
-
1014
- When you get validation errors, use these steps:
1015
-
1016
- 1. **Generate a reference config**: `./myapp --init-config` to see what valid config looks like
1017
- 2. **Check your current config**: `./myapp --check-config` to see what values are actually being used
1018
- 3. **Compare the differences**: Look for typos, wrong types, or missing required fields
1019
-
1020
- #### Path Resolution Debugging
1021
-
1022
- When using `pathResolution` options:
1023
-
1024
- ```typescript
1025
- const cardigantime = create({
1026
- defaults: {
1027
- configDirectory: './config',
1028
- pathResolution: {
1029
- pathFields: ['outputDir', 'logFile'],
1030
- resolvePathArray: ['includePaths']
1031
- }
1032
- },
1033
- configShape: MySchema.shape
1034
- });
1035
- ```
1036
-
1037
- Use `--check-config` to verify paths are being resolved correctly relative to your config file location.
1038
-
1039
- ### Troubleshooting Common Issues
1040
-
1041
- **Problem: "Configuration validation failed"**
1042
- ```bash
1043
- # Step 1: Generate a reference config to see valid structure
1044
- ./myapp --init-config
1045
-
1046
- # Step 2: Check what your current config resolves to
1047
- ./myapp --check-config
1048
-
1049
- # Step 3: Compare and fix validation issues
1050
- ```
1051
-
1052
- **Problem: "Unknown configuration keys found"**
1053
- ```bash
1054
- # Check what keys are actually being loaded
1055
- ./myapp --check-config
1056
- # Look for typos in key names (e.g., 'databse' instead of 'database')
1057
- ```
1058
-
1059
- **Problem: "Values not being overridden as expected"**
1060
- ```bash
1061
- # Check configuration precedence and sources
1062
- ./myapp --check-config
1063
- # Verify hierarchical levels and CLI argument parsing
1064
- ```
1065
-
1066
- **Problem: "Array values not merging correctly"**
1067
- ```bash
1068
- # For hierarchical configurations, check array merge behavior
1069
- ./myapp --check-config
1070
- # Verify your fieldOverlaps configuration
1071
- ```
1072
-
1073
- ## Advanced Usage
1074
-
1075
141
  ### Hierarchical Configuration Discovery
142
+ Supports hierarchical configuration discovery, similar to how `.gitignore`, `.eslintrc`, or `package.json` work - searching up the directory tree for configuration directories.
1076
143
 
1077
- Here's a complete example of using hierarchical configuration discovery for a monorepo setup:
1078
-
1079
- ```typescript
1080
- import { create } from '@theunwalked/cardigantime';
1081
- import { z } from 'zod';
1082
-
1083
- // Define a comprehensive configuration schema
1084
- const ProjectConfigSchema = z.object({
1085
- projectName: z.string(),
1086
- environment: z.enum(['development', 'staging', 'production']).default('development'),
1087
- database: z.object({
1088
- host: z.string().default('localhost'),
1089
- port: z.number().default(5432),
1090
- ssl: z.boolean().default(false),
1091
- maxConnections: z.number().default(10),
1092
- }),
1093
- api: z.object({
1094
- baseUrl: z.string().url(),
1095
- timeout: z.number().default(5000),
1096
- retries: z.number().default(3),
1097
- }),
1098
- features: z.record(z.boolean()).default({}),
1099
- logging: z.object({
1100
- level: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
1101
- outputs: z.array(z.string()).default(['console']),
1102
- }),
1103
- });
1104
-
1105
- // Enable hierarchical discovery
1106
- const cardigantime = create({
1107
- defaults: {
1108
- configDirectory: '.myapp',
1109
- configFile: 'config.yaml',
1110
- },
1111
- configShape: ProjectConfigSchema.shape,
1112
- features: ['config', 'hierarchical'], // Enable hierarchical discovery
1113
- });
1114
-
1115
- // Usage in a CLI tool
1116
- async function setupProject() {
1117
- try {
1118
- const config = await cardigantime.read(process.argv);
1119
- await cardigantime.validate(config);
1120
-
1121
- console.log(`Setting up ${config.projectName} in ${config.environment} mode`);
1122
- console.log(`Database: ${config.database.host}:${config.database.port}`);
1123
- console.log(`API: ${config.api.baseUrl}`);
1124
-
1125
- return config;
1126
- } catch (error) {
1127
- console.error('Configuration error:', error.message);
1128
- process.exit(1);
1129
- }
1130
- }
1131
- ```
1132
-
1133
- **Directory Structure:**
1134
- ```
1135
- /workspace/
1136
- ├── .myapp/
1137
- │ └── config.yaml # Global defaults
1138
- ├── team-frontend/
1139
- │ ├── .myapp/
1140
- │ │ └── config.yaml # Team-specific settings
1141
- │ ├── app1/
1142
- │ │ ├── .myapp/
1143
- │ │ │ └── config.yaml # App-specific overrides
1144
- │ │ └── package.json
1145
- │ └── app2/
1146
- │ └── package.json # Uses team + global config
1147
- ```
1148
-
1149
- **Configuration Files:**
1150
- ```yaml
1151
- # /workspace/.myapp/config.yaml (Global)
1152
- database:
1153
- host: prod.db.company.com
1154
- ssl: true
1155
- api:
1156
- baseUrl: https://api.company.com
1157
- logging:
1158
- level: warn
1159
- outputs: [console, file]
1160
-
1161
- # /workspace/team-frontend/.myapp/config.yaml (Team)
1162
- database:
1163
- host: team-frontend.db.company.com
1164
- api:
1165
- timeout: 3000
1166
- features:
1167
- analytics: true
1168
- darkMode: true
1169
-
1170
- # /workspace/team-frontend/app1/.myapp/config.yaml (App)
1171
- projectName: frontend-app1
1172
- environment: development
1173
- database:
1174
- host: localhost # Override for local development
1175
- logging:
1176
- level: debug
1177
- ```
1178
-
1179
- When running from `/workspace/team-frontend/app1/`, the final merged configuration will be:
1180
-
1181
- ```yaml
1182
- projectName: frontend-app1 # From app level
1183
- environment: development # From app level
1184
- database:
1185
- host: localhost # From app level (highest precedence)
1186
- ssl: true # From global level
1187
- api:
1188
- baseUrl: https://api.company.com # From global level
1189
- timeout: 3000 # From team level
1190
- features:
1191
- analytics: true # From team level
1192
- darkMode: true # From team level
1193
- logging:
1194
- level: debug # From app level (highest precedence)
1195
- outputs: [console, file] # From global level
1196
- ```
1197
-
1198
- ### Custom Logger
1199
-
1200
- ```typescript
1201
- import winston from 'winston';
1202
-
1203
- const logger = winston.createLogger({
1204
- level: 'debug',
1205
- format: winston.format.json(),
1206
- transports: [
1207
- new winston.transports.File({ filename: 'app.log' }),
1208
- new winston.transports.Console(),
1209
- ],
1210
- });
1211
-
1212
- const cardigantime = create({
1213
- defaults: { configDirectory: './config' },
1214
- configShape: MyConfigSchema.shape,
1215
- logger, // Use Winston for logging
1216
- });
1217
- ```
1218
-
1219
- ### Complex Configuration Schema
1220
-
1221
- ```typescript
1222
- const DatabaseConfig = z.object({
1223
- host: z.string(),
1224
- port: z.number().min(1).max(65535),
1225
- username: z.string(),
1226
- password: z.string(),
1227
- ssl: z.boolean().default(false),
1228
- });
1229
-
1230
- const AppConfigSchema = z.object({
1231
- app: z.object({
1232
- name: z.string(),
1233
- version: z.string(),
1234
- environment: z.enum(['development', 'staging', 'production']),
1235
- }),
1236
- database: DatabaseConfig,
1237
- redis: z.object({
1238
- url: z.string().url(),
1239
- ttl: z.number().positive().default(3600),
1240
- }),
1241
- features: z.record(z.boolean()).default({}), // Dynamic feature flags
1242
- logging: z.object({
1243
- level: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
1244
- file: z.string().optional(),
1245
- }),
1246
- });
1247
- ```
1248
-
1249
- ### Environment-Specific Configuration
1250
-
1251
- ```typescript
1252
- // Use different config directories for different environments
1253
- const environment = process.env.NODE_ENV || 'development';
1254
-
1255
- const cardigantime = create({
1256
- defaults: {
1257
- configDirectory: `./config/${environment}`,
1258
- configFile: 'app.yaml',
1259
- },
1260
- configShape: AppConfigSchema.shape,
1261
- });
1262
- ```
1263
-
1264
- ### Configuration File Discovery
1265
-
1266
- ```typescript
1267
- // Cardigantime will look for config files in this order:
1268
- // 1. CLI argument: --config-directory /path/to/config
1269
- // 2. Default directory: ./config
1270
- // 3. If not found and isRequired: false, continues with empty config
1271
- // 4. If not found and isRequired: true, throws error
1272
- ```
1273
-
1274
- ## Error Handling
1275
-
1276
- Cardigantime provides structured error types that allow you to handle different failure scenarios programmatically. All custom errors extend the standard JavaScript `Error` class and can be imported from the main package.
1277
-
1278
- ### Error Types
1279
-
1280
- ```typescript
1281
- import {
1282
- ConfigurationError,
1283
- FileSystemError,
1284
- ArgumentError
1285
- } from '@theunwalked/cardigantime';
1286
- ```
1287
-
1288
- #### ConfigurationError
1289
-
1290
- Thrown when configuration validation fails, contains extra keys, or schema issues occur.
144
+ ### Type Safety & Validation
145
+ Full TypeScript support with Zod schema validation for robust, type-safe configuration management.
1291
146
 
1292
- **Properties:**
1293
- - `errorType`: `'validation' | 'schema' | 'extra_keys'`
1294
- - `details`: Additional error context (e.g., Zod error details, extra keys info)
1295
- - `configPath`: Path to the configuration file (when applicable)
147
+ ### Error Handling
148
+ Comprehensive error handling with detailed, actionable error messages to help users fix configuration issues quickly.
1296
149
 
1297
- #### FileSystemError
150
+ ## Documentation
1298
151
 
1299
- Thrown when file system operations fail (directory access, file reading, etc.).
152
+ 📚 **[Complete Documentation](https://semicolonambulance.github.io/cardigantime/)** - Full documentation site
1300
153
 
1301
- **Properties:**
1302
- - `errorType`: `'not_found' | 'not_readable' | 'not_writable' | 'creation_failed' | 'operation_failed'`
1303
- - `path`: The file/directory path that caused the error
1304
- - `operation`: The operation that failed
1305
- - `originalError`: The underlying error (when applicable)
1306
-
1307
- #### ArgumentError
1308
-
1309
- Thrown when CLI arguments or function parameters are invalid.
1310
-
1311
- **Properties:**
1312
- - `argument`: The name of the invalid argument
1313
-
1314
- ### Error Handling Examples
1315
-
1316
- #### Basic Error Handling
1317
-
1318
- ```typescript
1319
- import { create, ConfigurationError, FileSystemError, ArgumentError } from '@theunwalked/cardigantime';
1320
-
1321
- async function setupApp() {
1322
- const cardigantime = create({
1323
- defaults: { configDirectory: './config' },
1324
- configShape: MyConfigSchema.shape,
1325
- });
1326
-
1327
- try {
1328
- const config = await cardigantime.read(args);
1329
- await cardigantime.validate(config);
1330
-
1331
- // Your app logic here
1332
- await startApp(config);
1333
-
1334
- } catch (error) {
1335
- if (error instanceof ConfigurationError) {
1336
- handleConfigError(error);
1337
- } else if (error instanceof FileSystemError) {
1338
- handleFileSystemError(error);
1339
- } else if (error instanceof ArgumentError) {
1340
- handleArgumentError(error);
1341
- } else {
1342
- console.error('Unexpected error:', error.message);
1343
- process.exit(1);
1344
- }
1345
- }
1346
- }
1347
- ```
1348
-
1349
- #### Detailed Configuration Error Handling
1350
-
1351
- ```typescript
1352
- function handleConfigError(error: ConfigurationError) {
1353
- switch (error.errorType) {
1354
- case 'validation':
1355
- console.error('❌ Configuration validation failed');
1356
- console.error('Details:', JSON.stringify(error.details, null, 2));
1357
- console.error('Please check your configuration values against the schema.');
1358
- break;
1359
-
1360
- case 'extra_keys':
1361
- console.error('❌ Unknown configuration keys found');
1362
- console.error('Extra keys:', error.details.extraKeys.join(', '));
1363
- console.error('Allowed keys:', error.details.allowedKeys.join(', '));
1364
- console.error('Please remove the unknown keys or update your schema.');
1365
- break;
1366
-
1367
- case 'schema':
1368
- console.error('❌ Configuration schema is invalid');
1369
- console.error('Details:', error.details);
1370
- break;
1371
- }
1372
-
1373
- if (error.configPath) {
1374
- console.error(`Configuration file: ${error.configPath}`);
1375
- }
1376
-
1377
- process.exit(1);
1378
- }
1379
- ```
1380
-
1381
- #### File System Error Handling
1382
-
1383
- ```typescript
1384
- function handleFileSystemError(error: FileSystemError) {
1385
- switch (error.errorType) {
1386
- case 'not_found':
1387
- if (error.operation === 'directory_access') {
1388
- console.error(`❌ Configuration directory not found: ${error.path}`);
1389
- console.error('Solutions:');
1390
- console.error(' 1. Create the directory: mkdir -p ' + error.path);
1391
- console.error(' 2. Use a different directory with --config-directory');
1392
- console.error(' 3. Set isRequired: false in your options');
1393
- } else {
1394
- console.error(`❌ Configuration file not found: ${error.path}`);
1395
- console.error('Create the configuration file or check the path.');
1396
- }
1397
- break;
1398
-
1399
- case 'not_readable':
1400
- console.error(`❌ Cannot read ${error.path}`);
1401
- console.error('Check file/directory permissions:');
1402
- console.error(` chmod +r ${error.path}`);
1403
- break;
1404
-
1405
- case 'creation_failed':
1406
- console.error(`❌ Failed to create directory: ${error.path}`);
1407
- console.error('Original error:', error.originalError?.message);
1408
- console.error('Check parent directory permissions.');
1409
- break;
1410
-
1411
- case 'operation_failed':
1412
- console.error(`❌ File operation failed: ${error.operation}`);
1413
- console.error('Path:', error.path);
1414
- console.error('Error:', error.originalError?.message);
1415
- break;
1416
- }
1417
-
1418
- process.exit(1);
1419
- }
1420
- ```
1421
-
1422
- #### Argument Error Handling
1423
-
1424
- ```typescript
1425
- function handleArgumentError(error: ArgumentError) {
1426
- console.error(`❌ Invalid argument: ${error.argument}`);
1427
- console.error(`Error: ${error.message}`);
1428
- console.error('Please check your command line arguments or function parameters.');
1429
- process.exit(1);
1430
- }
1431
- ```
1432
-
1433
- #### Graceful Degradation
1434
-
1435
- ```typescript
1436
- async function setupAppWithFallbacks() {
1437
- const cardigantime = create({
1438
- defaults: { configDirectory: './config' },
1439
- configShape: MyConfigSchema.shape,
1440
- });
1441
-
1442
- try {
1443
- const config = await cardigantime.read(args);
1444
- await cardigantime.validate(config);
1445
- return config;
1446
-
1447
- } catch (error) {
1448
- if (error instanceof FileSystemError && error.errorType === 'not_found') {
1449
- console.warn('⚠️ Configuration not found, using defaults');
1450
- return getDefaultConfig();
1451
- }
1452
-
1453
- if (error instanceof ConfigurationError && error.errorType === 'extra_keys') {
1454
- console.warn('⚠️ Unknown config keys found, continuing with valid keys only');
1455
- // Filter out extra keys and retry
1456
- const cleanConfig = removeExtraKeys(config, error.details.allowedKeys);
1457
- await cardigantime.validate(cleanConfig);
1458
- return cleanConfig;
1459
- }
1460
-
1461
- // Re-throw other errors
1462
- throw error;
1463
- }
1464
- }
1465
- ```
1466
-
1467
- ### Error Messages and Troubleshooting
1468
-
1469
- #### Common Configuration Errors
1470
-
1471
- **Schema validation failed:**
1472
- ```typescript
1473
- // Error type: ConfigurationError with errorType: 'validation'
1474
- {
1475
- "port": {
1476
- "_errors": ["Number must be greater than or equal to 1"]
1477
- }
1478
- }
1479
- ```
1480
- *Solution:* Fix the configuration values to match your schema requirements.
1481
-
1482
- **Unknown configuration keys:**
1483
- ```typescript
1484
- // Error type: ConfigurationError with errorType: 'extra_keys'
1485
- // error.details.extraKeys: ['databse']
1486
- // error.details.allowedKeys: ['database', 'port', 'host']
1487
- ```
1488
- *Solution:* Fix typos in your configuration file or update your schema.
1489
-
1490
- #### Common File System Errors
1491
-
1492
- **Configuration directory not found:**
1493
- ```typescript
1494
- // Error type: FileSystemError with errorType: 'not_found'
1495
- // error.path: '/etc/myapp'
1496
- // error.operation: 'directory_access'
1497
- ```
1498
- *Solutions:*
1499
- - Create the directory: `mkdir -p /etc/myapp`
1500
- - Use a different directory: `--config-directory ./config`
1501
- - Make it optional: `isRequired: false`
1502
-
1503
- **Directory not readable:**
1504
- ```typescript
1505
- // Error type: FileSystemError with errorType: 'not_readable'
1506
- // error.path: '/etc/restricted'
1507
- // error.operation: 'directory_read'
1508
- ```
1509
- *Solution:* Check file permissions: `chmod +r /etc/restricted`
1510
-
1511
- #### Common Argument Errors
1512
-
1513
- **Invalid config directory argument:**
1514
- ```typescript
1515
- // Error type: ArgumentError with argument: 'config-directory'
1516
- // Triggered by: --config-directory ""
1517
- ```
1518
- *Solution:* Provide a valid directory path: `--config-directory ./config`
154
+ **Quick Links:**
155
+ - [Getting Started Guide](https://semicolonambulance.github.io/cardigantime/#getting-started) - Detailed setup and basic concepts
156
+ - [Core Concepts](https://semicolonambulance.github.io/cardigantime/#core-concepts) - Configuration sources, hierarchical discovery
157
+ - [API Reference](https://semicolonambulance.github.io/cardigantime/#api-reference) - Complete API documentation
158
+ - [Configuration Options](https://semicolonambulance.github.io/cardigantime/#configuration-options) - All available options
159
+ - [Debugging & Analysis](https://semicolonambulance.github.io/cardigantime/#debugging-analysis) - Tools for analyzing config
160
+ - [Advanced Usage](https://semicolonambulance.github.io/cardigantime/#advanced-usage) - Complex examples and scenarios
161
+ - [Error Handling](https://semicolonambulance.github.io/cardigantime/#error-handling) - Comprehensive error handling guide
1519
162
 
1520
163
  ## Contributing
1521
164