@theunwalked/cardigantime 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -1383
- package/dist/cardigantime.cjs +20 -3
- package/dist/cardigantime.cjs.map +1 -1
- package/dist/cardigantime.js +10 -0
- package/dist/cardigantime.js.map +1 -1
- package/dist/util/schema-defaults.d.ts +5 -0
- package/dist/util/schema-defaults.js +10 -3
- package/dist/util/schema-defaults.js.map +1 -1
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1293
|
-
|
|
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
|
-
|
|
150
|
+
## Documentation
|
|
1298
151
|
|
|
1299
|
-
|
|
152
|
+
📚 **[Complete Documentation](https://semicolonambulance.github.io/cardigantime/)** - Full documentation site
|
|
1300
153
|
|
|
1301
|
-
**
|
|
1302
|
-
-
|
|
1303
|
-
-
|
|
1304
|
-
-
|
|
1305
|
-
-
|
|
1306
|
-
|
|
1307
|
-
|
|
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
|
|