@theunwalked/cardigantime 0.0.9 → 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.
- package/README.md +29 -912
- package/dist/cardigantime.cjs +431 -1
- package/dist/cardigantime.cjs.map +1 -1
- package/dist/cardigantime.js +73 -4
- package/dist/cardigantime.js.map +1 -1
- package/dist/configure.js +4 -0
- package/dist/configure.js.map +1 -1
- package/dist/read.d.ts +23 -0
- package/dist/read.js +258 -1
- package/dist/read.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.js.map +1 -1
- package/dist/util/schema-defaults.d.ts +57 -0
- package/dist/util/schema-defaults.js +108 -0
- package/dist/util/schema-defaults.js.map +1 -0
- 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,819 +123,42 @@ debug: true
|
|
|
229
123
|
# Use different config directory
|
|
230
124
|
./myapp --config-directory /etc/myapp
|
|
231
125
|
|
|
232
|
-
#
|
|
233
|
-
./myapp --
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Advanced Usage Examples
|
|
237
|
-
|
|
238
|
-
#### Basic Configuration with Path Resolution
|
|
126
|
+
# Generate initial configuration file
|
|
127
|
+
./myapp --init-config
|
|
239
128
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
import { z } from 'zod';
|
|
243
|
-
|
|
244
|
-
const MyConfigSchema = z.object({
|
|
245
|
-
apiKey: z.string().min(1),
|
|
246
|
-
timeout: z.number().default(5000),
|
|
247
|
-
debug: z.boolean().default(false),
|
|
248
|
-
contextDirectories: z.array(z.string()).optional(),
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
const cardigantime = create({
|
|
252
|
-
defaults: {
|
|
253
|
-
configDirectory: './config',
|
|
254
|
-
configFile: 'myapp.yaml',
|
|
255
|
-
// Resolve relative paths in contextDirectories relative to config file location
|
|
256
|
-
pathResolution: {
|
|
257
|
-
pathFields: ['contextDirectories'],
|
|
258
|
-
resolvePathArray: ['contextDirectories']
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
configShape: MyConfigSchema.shape,
|
|
262
|
-
});
|
|
129
|
+
# Analyze configuration with source tracking
|
|
130
|
+
./myapp --check-config
|
|
263
131
|
```
|
|
264
132
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
const cardigantime = create({
|
|
269
|
-
defaults: {
|
|
270
|
-
configDirectory: '.myapp',
|
|
271
|
-
configFile: 'config.yaml',
|
|
272
|
-
fieldOverlaps: {
|
|
273
|
-
'features': 'append', // Accumulate features from all levels
|
|
274
|
-
'excludePatterns': 'prepend', // Higher precedence patterns come first
|
|
275
|
-
'api.endpoints': 'append', // Nested field configuration
|
|
276
|
-
'security.allowedOrigins': 'append' // Security settings accumulate
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
configShape: MyConfigSchema.shape,
|
|
280
|
-
features: ['config', 'hierarchical'], // Enable hierarchical discovery
|
|
281
|
-
});
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
This configuration enables powerful composition scenarios where:
|
|
285
|
-
- **Features** from all configuration levels are combined (e.g., base features + project features + local features)
|
|
286
|
-
- **Exclude patterns** are layered with local patterns taking precedence
|
|
287
|
-
- **API endpoints** can be extended at each level
|
|
288
|
-
- **Security settings** accumulate for maximum flexibility
|
|
289
|
-
|
|
290
|
-
## Core Concepts
|
|
291
|
-
|
|
292
|
-
### 1. Configuration Sources & Precedence
|
|
293
|
-
|
|
294
|
-
Cardigantime merges configuration from multiple sources in this order (highest to lowest priority):
|
|
133
|
+
## Key Features
|
|
295
134
|
|
|
135
|
+
### Configuration Sources & Precedence
|
|
136
|
+
Merges configuration from multiple sources in order of precedence:
|
|
296
137
|
1. **Command-line arguments** (highest priority)
|
|
297
138
|
2. **Configuration file(s)** (medium priority)
|
|
298
139
|
3. **Default values** (lowest priority)
|
|
299
140
|
|
|
300
|
-
```typescript
|
|
301
|
-
// If you have this config file:
|
|
302
|
-
// timeout: 5000
|
|
303
|
-
// debug: false
|
|
304
|
-
|
|
305
|
-
// And run: ./myapp --timeout 10000
|
|
306
|
-
|
|
307
|
-
// The final config will be:
|
|
308
|
-
// timeout: 10000 (from CLI, overrides file)
|
|
309
|
-
// debug: false (from file)
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### 2. Hierarchical Configuration Discovery
|
|
313
|
-
|
|
314
|
-
Cardigantime supports hierarchical configuration discovery, similar to how tools like `.gitignore`, `.eslintrc`, or `package.json` work. When the `hierarchical` feature is enabled, Cardigantime will:
|
|
315
|
-
|
|
316
|
-
1. **Start from the specified config directory** (e.g., `./project/subdir/.kodrdriv`)
|
|
317
|
-
2. **Search up the directory tree** for additional config directories with the same name
|
|
318
|
-
3. **Merge configurations** with proper precedence (closer directories win)
|
|
319
|
-
4. **Apply CLI arguments** as the final override
|
|
320
|
-
|
|
321
|
-
#### Example Directory Structure
|
|
322
|
-
|
|
323
|
-
```
|
|
324
|
-
/home/user/projects/
|
|
325
|
-
├── .kodrdriv/
|
|
326
|
-
│ └── config.yaml # Root-level config
|
|
327
|
-
├── myproject/
|
|
328
|
-
│ ├── .kodrdriv/
|
|
329
|
-
│ │ └── config.yaml # Project-level config
|
|
330
|
-
│ └── submodule/
|
|
331
|
-
│ ├── .kodrdriv/
|
|
332
|
-
│ │ └── config.yaml # Submodule-level config
|
|
333
|
-
│ └── my-script.js
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
#### Hierarchical Discovery Behavior
|
|
337
|
-
|
|
338
|
-
When running from `/home/user/projects/myproject/submodule/` with hierarchical discovery:
|
|
339
|
-
|
|
340
|
-
1. **Level 0 (Highest Priority)**: `/home/user/projects/myproject/submodule/.kodrdriv/config.yaml`
|
|
341
|
-
2. **Level 1**: `/home/user/projects/myproject/.kodrdriv/config.yaml`
|
|
342
|
-
3. **Level 2 (Lowest Priority)**: `/home/user/projects/.kodrdriv/config.yaml`
|
|
343
|
-
|
|
344
|
-
Configurations are deep-merged, with closer directories taking precedence:
|
|
345
|
-
|
|
346
|
-
```yaml
|
|
347
|
-
# /home/user/projects/.kodrdriv/config.yaml (Level 2)
|
|
348
|
-
database:
|
|
349
|
-
host: localhost
|
|
350
|
-
port: 5432
|
|
351
|
-
ssl: false
|
|
352
|
-
logging:
|
|
353
|
-
level: info
|
|
354
|
-
features:
|
|
355
|
-
- auth
|
|
356
|
-
- basic-logging
|
|
357
|
-
|
|
358
|
-
# /home/user/projects/myproject/.kodrdriv/config.yaml (Level 1)
|
|
359
|
-
database:
|
|
360
|
-
port: 5433
|
|
361
|
-
ssl: true
|
|
362
|
-
api:
|
|
363
|
-
timeout: 5000
|
|
364
|
-
features:
|
|
365
|
-
- advanced-logging
|
|
366
|
-
- metrics
|
|
367
|
-
|
|
368
|
-
# /home/user/projects/myproject/submodule/.kodrdriv/config.yaml (Level 0)
|
|
369
|
-
database:
|
|
370
|
-
host: dev.example.com
|
|
371
|
-
logging:
|
|
372
|
-
level: debug
|
|
373
|
-
features:
|
|
374
|
-
- debug-mode
|
|
375
|
-
|
|
376
|
-
# Final merged configuration (with default array behavior):
|
|
377
|
-
database:
|
|
378
|
-
host: dev.example.com # From Level 0 (highest precedence)
|
|
379
|
-
port: 5433 # From Level 1
|
|
380
|
-
ssl: true # From Level 1
|
|
381
|
-
api:
|
|
382
|
-
timeout: 5000 # From Level 1
|
|
383
|
-
logging:
|
|
384
|
-
level: debug # From Level 0 (highest precedence)
|
|
385
|
-
features:
|
|
386
|
-
- debug-mode # From Level 0 (arrays override by default)
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
#### Configurable Array Overlap Behavior
|
|
390
|
-
|
|
391
|
-
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:
|
|
392
|
-
|
|
393
|
-
```typescript
|
|
394
|
-
const cardigantime = create({
|
|
395
|
-
defaults: {
|
|
396
|
-
configDirectory: '.kodrdriv',
|
|
397
|
-
configFile: 'config.yaml',
|
|
398
|
-
fieldOverlaps: {
|
|
399
|
-
'features': 'append', // Combine features by appending
|
|
400
|
-
'excludePatterns': 'prepend', // Combine exclude patterns by prepending
|
|
401
|
-
'middlewares': 'override' // Override middlewares (default behavior)
|
|
402
|
-
}
|
|
403
|
-
},
|
|
404
|
-
configShape: MyConfigSchema.shape,
|
|
405
|
-
features: ['config', 'hierarchical'],
|
|
406
|
-
});
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
**Available Overlap Modes:**
|
|
410
|
-
|
|
411
|
-
- **`override`** (default): Higher precedence arrays completely replace lower precedence arrays
|
|
412
|
-
- **`append`**: Higher precedence array elements are appended to lower precedence arrays
|
|
413
|
-
- **`prepend`**: Higher precedence array elements are prepended to lower precedence arrays
|
|
414
|
-
|
|
415
|
-
**Example with Custom Array Overlap:**
|
|
416
|
-
|
|
417
|
-
```yaml
|
|
418
|
-
# /home/user/projects/.kodrdriv/config.yaml (Level 2)
|
|
419
|
-
features: ['auth', 'basic-logging']
|
|
420
|
-
excludePatterns: ['*.tmp', '*.cache']
|
|
421
|
-
|
|
422
|
-
# /home/user/projects/myproject/.kodrdriv/config.yaml (Level 1)
|
|
423
|
-
features: ['advanced-logging', 'metrics']
|
|
424
|
-
excludePatterns: ['*.log']
|
|
425
|
-
|
|
426
|
-
# /home/user/projects/myproject/submodule/.kodrdriv/config.yaml (Level 0)
|
|
427
|
-
features: ['debug-mode']
|
|
428
|
-
excludePatterns: ['*.debug']
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
With the configuration above (`features: 'append'`, `excludePatterns: 'prepend'`):
|
|
432
|
-
|
|
433
|
-
```yaml
|
|
434
|
-
# Final merged configuration:
|
|
435
|
-
features:
|
|
436
|
-
- auth # From Level 2
|
|
437
|
-
- basic-logging # From Level 2
|
|
438
|
-
- advanced-logging # From Level 1
|
|
439
|
-
- metrics # From Level 1
|
|
440
|
-
- debug-mode # From Level 0 (appended)
|
|
441
|
-
excludePatterns:
|
|
442
|
-
- "*.debug" # From Level 0 (prepended first)
|
|
443
|
-
- "*.log" # From Level 1 (prepended second)
|
|
444
|
-
- "*.tmp" # From Level 2
|
|
445
|
-
- "*.cache" # From Level 2
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
**Nested Field Paths:**
|
|
449
|
-
|
|
450
|
-
You can configure overlap behavior for nested array fields using dot notation:
|
|
451
|
-
|
|
452
|
-
```typescript
|
|
453
|
-
fieldOverlaps: {
|
|
454
|
-
'api.endpoints': 'append',
|
|
455
|
-
'database.migrations': 'prepend',
|
|
456
|
-
'config.features.experimental': 'override'
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
#### Enabling Hierarchical Discovery
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
const cardigantime = create({
|
|
464
|
-
defaults: {
|
|
465
|
-
configDirectory: '.kodrdriv',
|
|
466
|
-
configFile: 'config.yaml'
|
|
467
|
-
},
|
|
468
|
-
configShape: MyConfigSchema.shape,
|
|
469
|
-
features: ['config', 'hierarchical'], // Enable hierarchical discovery
|
|
470
|
-
});
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
#### Hierarchical Discovery Options
|
|
474
|
-
|
|
475
|
-
The hierarchical discovery has several built-in protections and features:
|
|
476
|
-
|
|
477
|
-
- **Maximum traversal depth**: Prevents infinite loops (default: 10 levels)
|
|
478
|
-
- **Symlink protection**: Tracks visited paths to prevent circular references
|
|
479
|
-
- **Graceful fallback**: Falls back to single-directory mode if discovery fails
|
|
480
|
-
- **Error tolerance**: Continues discovery even if some directories are unreadable
|
|
481
|
-
- **Root detection**: Automatically stops at filesystem root
|
|
482
|
-
|
|
483
|
-
#### Use Cases for Hierarchical Configuration
|
|
484
|
-
|
|
485
|
-
1. **Monorepos**: Share common configuration across multiple packages
|
|
486
|
-
2. **Project inheritance**: Override team/organization defaults for specific projects
|
|
487
|
-
3. **Environment layering**: Different configs for development/staging/production
|
|
488
|
-
4. **Tool configuration**: Similar to how ESLint or Prettier find configs up the tree
|
|
489
|
-
5. **Multi-tenant applications**: Tenant-specific overrides of global settings
|
|
490
|
-
|
|
491
|
-
### 3. Schema Validation
|
|
492
|
-
|
|
493
|
-
All configuration is validated against your Zod schema:
|
|
494
|
-
|
|
495
|
-
```typescript
|
|
496
|
-
const ConfigSchema = z.object({
|
|
497
|
-
port: z.number().min(1).max(65535),
|
|
498
|
-
host: z.string().ip().or(z.literal('localhost')),
|
|
499
|
-
database: z.object({
|
|
500
|
-
url: z.string().url(),
|
|
501
|
-
maxConnections: z.number().positive().default(10),
|
|
502
|
-
}),
|
|
503
|
-
features: z.array(z.enum(['auth', 'analytics', 'logging'])).default([]),
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
const cardigantime = create({
|
|
507
|
-
defaults: { configDirectory: './config' },
|
|
508
|
-
configShape: ConfigSchema.shape,
|
|
509
|
-
});
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
### 4. Type Safety
|
|
513
|
-
|
|
514
|
-
Cardigantime provides full TypeScript support:
|
|
515
|
-
|
|
516
|
-
```typescript
|
|
517
|
-
// The config object is fully typed
|
|
518
|
-
const config = await cardigantime.read(args);
|
|
519
|
-
// config.database.maxConnections is number
|
|
520
|
-
// config.features is ('auth' | 'analytics' | 'logging')[]
|
|
521
|
-
// config.port is number
|
|
522
|
-
|
|
523
|
-
// IntelliSense works everywhere
|
|
524
|
-
if (config.features.includes('auth')) {
|
|
525
|
-
// Setup authentication
|
|
526
|
-
}
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
### 5. Error Handling
|
|
530
|
-
|
|
531
|
-
Cardigantime provides detailed error messages for common issues:
|
|
532
|
-
|
|
533
|
-
```typescript
|
|
534
|
-
try {
|
|
535
|
-
await cardigantime.validate(config);
|
|
536
|
-
} catch (error) {
|
|
537
|
-
// Detailed validation errors:
|
|
538
|
-
// "Configuration validation failed: port must be between 1 and 65535"
|
|
539
|
-
// "Unknown configuration keys found: typoKey. Allowed keys are: port, host, database"
|
|
540
|
-
// "Config directory does not exist and is required: /nonexistent/path"
|
|
541
|
-
}
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
## API Reference
|
|
545
|
-
|
|
546
|
-
### `create(options)`
|
|
547
|
-
|
|
548
|
-
Creates a new Cardigantime instance.
|
|
549
|
-
|
|
550
|
-
**Parameters:**
|
|
551
|
-
- `options.defaults` (required): Default configuration options
|
|
552
|
-
- `configDirectory` (required): Directory to look for config files
|
|
553
|
-
- `configFile` (optional): Config filename, defaults to `'config.yaml'`
|
|
554
|
-
- `isRequired` (optional): Whether config directory must exist, defaults to `false`
|
|
555
|
-
- `encoding` (optional): File encoding, defaults to `'utf8'`
|
|
556
|
-
- `options.configShape` (required): Zod schema shape for validation
|
|
557
|
-
- `options.features` (optional): Array of features to enable, defaults to `['config']`
|
|
558
|
-
- `options.logger` (optional): Custom logger implementation
|
|
559
|
-
|
|
560
|
-
**Returns:** `Cardigantime` instance
|
|
561
|
-
|
|
562
|
-
### `cardigantime.configure(command)`
|
|
563
|
-
|
|
564
|
-
Adds Cardigantime's CLI options to a Commander.js command.
|
|
565
|
-
|
|
566
|
-
**Parameters:**
|
|
567
|
-
- `command`: Commander.js Command instance
|
|
568
|
-
|
|
569
|
-
**Returns:** Promise<Command> - The modified command
|
|
570
|
-
|
|
571
|
-
**Added Options:**
|
|
572
|
-
- `-c, --config-directory <path>`: Override config directory
|
|
573
|
-
|
|
574
|
-
### `cardigantime.read(args)`
|
|
575
|
-
|
|
576
|
-
Reads and merges configuration from all sources.
|
|
577
|
-
|
|
578
|
-
**Parameters:**
|
|
579
|
-
- `args`: Parsed command-line arguments object
|
|
580
|
-
|
|
581
|
-
**Returns:** Promise<Config> - Merged and typed configuration object
|
|
582
|
-
|
|
583
|
-
### `cardigantime.validate(config)`
|
|
584
|
-
|
|
585
|
-
Validates configuration against the schema.
|
|
586
|
-
|
|
587
|
-
**Parameters:**
|
|
588
|
-
- `config`: Configuration object to validate
|
|
589
|
-
|
|
590
|
-
**Returns:** Promise<void> - Throws on validation failure
|
|
591
|
-
|
|
592
|
-
### `cardigantime.setLogger(logger)`
|
|
593
|
-
|
|
594
|
-
Sets a custom logger for debugging and error reporting.
|
|
595
|
-
|
|
596
|
-
**Parameters:**
|
|
597
|
-
- `logger`: Logger implementing the Logger interface
|
|
598
|
-
|
|
599
|
-
## Advanced Usage
|
|
600
|
-
|
|
601
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.
|
|
602
143
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
```typescript
|
|
606
|
-
import { create } from '@theunwalked/cardigantime';
|
|
607
|
-
import { z } from 'zod';
|
|
608
|
-
|
|
609
|
-
// Define a comprehensive configuration schema
|
|
610
|
-
const ProjectConfigSchema = z.object({
|
|
611
|
-
projectName: z.string(),
|
|
612
|
-
environment: z.enum(['development', 'staging', 'production']).default('development'),
|
|
613
|
-
database: z.object({
|
|
614
|
-
host: z.string().default('localhost'),
|
|
615
|
-
port: z.number().default(5432),
|
|
616
|
-
ssl: z.boolean().default(false),
|
|
617
|
-
maxConnections: z.number().default(10),
|
|
618
|
-
}),
|
|
619
|
-
api: z.object({
|
|
620
|
-
baseUrl: z.string().url(),
|
|
621
|
-
timeout: z.number().default(5000),
|
|
622
|
-
retries: z.number().default(3),
|
|
623
|
-
}),
|
|
624
|
-
features: z.record(z.boolean()).default({}),
|
|
625
|
-
logging: z.object({
|
|
626
|
-
level: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
|
|
627
|
-
outputs: z.array(z.string()).default(['console']),
|
|
628
|
-
}),
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
// Enable hierarchical discovery
|
|
632
|
-
const cardigantime = create({
|
|
633
|
-
defaults: {
|
|
634
|
-
configDirectory: '.myapp',
|
|
635
|
-
configFile: 'config.yaml',
|
|
636
|
-
},
|
|
637
|
-
configShape: ProjectConfigSchema.shape,
|
|
638
|
-
features: ['config', 'hierarchical'], // Enable hierarchical discovery
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
// Usage in a CLI tool
|
|
642
|
-
async function setupProject() {
|
|
643
|
-
try {
|
|
644
|
-
const config = await cardigantime.read(process.argv);
|
|
645
|
-
await cardigantime.validate(config);
|
|
646
|
-
|
|
647
|
-
console.log(`Setting up ${config.projectName} in ${config.environment} mode`);
|
|
648
|
-
console.log(`Database: ${config.database.host}:${config.database.port}`);
|
|
649
|
-
console.log(`API: ${config.api.baseUrl}`);
|
|
650
|
-
|
|
651
|
-
return config;
|
|
652
|
-
} catch (error) {
|
|
653
|
-
console.error('Configuration error:', error.message);
|
|
654
|
-
process.exit(1);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
**Directory Structure:**
|
|
660
|
-
```
|
|
661
|
-
/workspace/
|
|
662
|
-
├── .myapp/
|
|
663
|
-
│ └── config.yaml # Global defaults
|
|
664
|
-
├── team-frontend/
|
|
665
|
-
│ ├── .myapp/
|
|
666
|
-
│ │ └── config.yaml # Team-specific settings
|
|
667
|
-
│ ├── app1/
|
|
668
|
-
│ │ ├── .myapp/
|
|
669
|
-
│ │ │ └── config.yaml # App-specific overrides
|
|
670
|
-
│ │ └── package.json
|
|
671
|
-
│ └── app2/
|
|
672
|
-
│ └── package.json # Uses team + global config
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
**Configuration Files:**
|
|
676
|
-
```yaml
|
|
677
|
-
# /workspace/.myapp/config.yaml (Global)
|
|
678
|
-
database:
|
|
679
|
-
host: prod.db.company.com
|
|
680
|
-
ssl: true
|
|
681
|
-
api:
|
|
682
|
-
baseUrl: https://api.company.com
|
|
683
|
-
logging:
|
|
684
|
-
level: warn
|
|
685
|
-
outputs: [console, file]
|
|
686
|
-
|
|
687
|
-
# /workspace/team-frontend/.myapp/config.yaml (Team)
|
|
688
|
-
database:
|
|
689
|
-
host: team-frontend.db.company.com
|
|
690
|
-
api:
|
|
691
|
-
timeout: 3000
|
|
692
|
-
features:
|
|
693
|
-
analytics: true
|
|
694
|
-
darkMode: true
|
|
695
|
-
|
|
696
|
-
# /workspace/team-frontend/app1/.myapp/config.yaml (App)
|
|
697
|
-
projectName: frontend-app1
|
|
698
|
-
environment: development
|
|
699
|
-
database:
|
|
700
|
-
host: localhost # Override for local development
|
|
701
|
-
logging:
|
|
702
|
-
level: debug
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
When running from `/workspace/team-frontend/app1/`, the final merged configuration will be:
|
|
706
|
-
|
|
707
|
-
```yaml
|
|
708
|
-
projectName: frontend-app1 # From app level
|
|
709
|
-
environment: development # From app level
|
|
710
|
-
database:
|
|
711
|
-
host: localhost # From app level (highest precedence)
|
|
712
|
-
ssl: true # From global level
|
|
713
|
-
api:
|
|
714
|
-
baseUrl: https://api.company.com # From global level
|
|
715
|
-
timeout: 3000 # From team level
|
|
716
|
-
features:
|
|
717
|
-
analytics: true # From team level
|
|
718
|
-
darkMode: true # From team level
|
|
719
|
-
logging:
|
|
720
|
-
level: debug # From app level (highest precedence)
|
|
721
|
-
outputs: [console, file] # From global level
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
### Custom Logger
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
import winston from 'winston';
|
|
728
|
-
|
|
729
|
-
const logger = winston.createLogger({
|
|
730
|
-
level: 'debug',
|
|
731
|
-
format: winston.format.json(),
|
|
732
|
-
transports: [
|
|
733
|
-
new winston.transports.File({ filename: 'app.log' }),
|
|
734
|
-
new winston.transports.Console(),
|
|
735
|
-
],
|
|
736
|
-
});
|
|
737
|
-
|
|
738
|
-
const cardigantime = create({
|
|
739
|
-
defaults: { configDirectory: './config' },
|
|
740
|
-
configShape: MyConfigSchema.shape,
|
|
741
|
-
logger, // Use Winston for logging
|
|
742
|
-
});
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
### Complex Configuration Schema
|
|
746
|
-
|
|
747
|
-
```typescript
|
|
748
|
-
const DatabaseConfig = z.object({
|
|
749
|
-
host: z.string(),
|
|
750
|
-
port: z.number().min(1).max(65535),
|
|
751
|
-
username: z.string(),
|
|
752
|
-
password: z.string(),
|
|
753
|
-
ssl: z.boolean().default(false),
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
const AppConfigSchema = z.object({
|
|
757
|
-
app: z.object({
|
|
758
|
-
name: z.string(),
|
|
759
|
-
version: z.string(),
|
|
760
|
-
environment: z.enum(['development', 'staging', 'production']),
|
|
761
|
-
}),
|
|
762
|
-
database: DatabaseConfig,
|
|
763
|
-
redis: z.object({
|
|
764
|
-
url: z.string().url(),
|
|
765
|
-
ttl: z.number().positive().default(3600),
|
|
766
|
-
}),
|
|
767
|
-
features: z.record(z.boolean()).default({}), // Dynamic feature flags
|
|
768
|
-
logging: z.object({
|
|
769
|
-
level: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
|
|
770
|
-
file: z.string().optional(),
|
|
771
|
-
}),
|
|
772
|
-
});
|
|
773
|
-
```
|
|
774
|
-
|
|
775
|
-
### Environment-Specific Configuration
|
|
776
|
-
|
|
777
|
-
```typescript
|
|
778
|
-
// Use different config directories for different environments
|
|
779
|
-
const environment = process.env.NODE_ENV || 'development';
|
|
780
|
-
|
|
781
|
-
const cardigantime = create({
|
|
782
|
-
defaults: {
|
|
783
|
-
configDirectory: `./config/${environment}`,
|
|
784
|
-
configFile: 'app.yaml',
|
|
785
|
-
},
|
|
786
|
-
configShape: AppConfigSchema.shape,
|
|
787
|
-
});
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
### Configuration File Discovery
|
|
791
|
-
|
|
792
|
-
```typescript
|
|
793
|
-
// Cardigantime will look for config files in this order:
|
|
794
|
-
// 1. CLI argument: --config-directory /path/to/config
|
|
795
|
-
// 2. Default directory: ./config
|
|
796
|
-
// 3. If not found and isRequired: false, continues with empty config
|
|
797
|
-
// 4. If not found and isRequired: true, throws error
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
## Error Handling
|
|
801
|
-
|
|
802
|
-
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.
|
|
803
|
-
|
|
804
|
-
### Error Types
|
|
805
|
-
|
|
806
|
-
```typescript
|
|
807
|
-
import {
|
|
808
|
-
ConfigurationError,
|
|
809
|
-
FileSystemError,
|
|
810
|
-
ArgumentError
|
|
811
|
-
} from '@theunwalked/cardigantime';
|
|
812
|
-
```
|
|
813
|
-
|
|
814
|
-
#### ConfigurationError
|
|
815
|
-
|
|
816
|
-
Thrown when configuration validation fails, contains extra keys, or schema issues occur.
|
|
817
|
-
|
|
818
|
-
**Properties:**
|
|
819
|
-
- `errorType`: `'validation' | 'schema' | 'extra_keys'`
|
|
820
|
-
- `details`: Additional error context (e.g., Zod error details, extra keys info)
|
|
821
|
-
- `configPath`: Path to the configuration file (when applicable)
|
|
822
|
-
|
|
823
|
-
#### FileSystemError
|
|
824
|
-
|
|
825
|
-
Thrown when file system operations fail (directory access, file reading, etc.).
|
|
826
|
-
|
|
827
|
-
**Properties:**
|
|
828
|
-
- `errorType`: `'not_found' | 'not_readable' | 'not_writable' | 'creation_failed' | 'operation_failed'`
|
|
829
|
-
- `path`: The file/directory path that caused the error
|
|
830
|
-
- `operation`: The operation that failed
|
|
831
|
-
- `originalError`: The underlying error (when applicable)
|
|
832
|
-
|
|
833
|
-
#### ArgumentError
|
|
834
|
-
|
|
835
|
-
Thrown when CLI arguments or function parameters are invalid.
|
|
836
|
-
|
|
837
|
-
**Properties:**
|
|
838
|
-
- `argument`: The name of the invalid argument
|
|
839
|
-
|
|
840
|
-
### Error Handling Examples
|
|
841
|
-
|
|
842
|
-
#### Basic Error Handling
|
|
843
|
-
|
|
844
|
-
```typescript
|
|
845
|
-
import { create, ConfigurationError, FileSystemError, ArgumentError } from '@theunwalked/cardigantime';
|
|
846
|
-
|
|
847
|
-
async function setupApp() {
|
|
848
|
-
const cardigantime = create({
|
|
849
|
-
defaults: { configDirectory: './config' },
|
|
850
|
-
configShape: MyConfigSchema.shape,
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
try {
|
|
854
|
-
const config = await cardigantime.read(args);
|
|
855
|
-
await cardigantime.validate(config);
|
|
856
|
-
|
|
857
|
-
// Your app logic here
|
|
858
|
-
await startApp(config);
|
|
859
|
-
|
|
860
|
-
} catch (error) {
|
|
861
|
-
if (error instanceof ConfigurationError) {
|
|
862
|
-
handleConfigError(error);
|
|
863
|
-
} else if (error instanceof FileSystemError) {
|
|
864
|
-
handleFileSystemError(error);
|
|
865
|
-
} else if (error instanceof ArgumentError) {
|
|
866
|
-
handleArgumentError(error);
|
|
867
|
-
} else {
|
|
868
|
-
console.error('Unexpected error:', error.message);
|
|
869
|
-
process.exit(1);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
```
|
|
874
|
-
|
|
875
|
-
#### Detailed Configuration Error Handling
|
|
876
|
-
|
|
877
|
-
```typescript
|
|
878
|
-
function handleConfigError(error: ConfigurationError) {
|
|
879
|
-
switch (error.errorType) {
|
|
880
|
-
case 'validation':
|
|
881
|
-
console.error('❌ Configuration validation failed');
|
|
882
|
-
console.error('Details:', JSON.stringify(error.details, null, 2));
|
|
883
|
-
console.error('Please check your configuration values against the schema.');
|
|
884
|
-
break;
|
|
885
|
-
|
|
886
|
-
case 'extra_keys':
|
|
887
|
-
console.error('❌ Unknown configuration keys found');
|
|
888
|
-
console.error('Extra keys:', error.details.extraKeys.join(', '));
|
|
889
|
-
console.error('Allowed keys:', error.details.allowedKeys.join(', '));
|
|
890
|
-
console.error('Please remove the unknown keys or update your schema.');
|
|
891
|
-
break;
|
|
892
|
-
|
|
893
|
-
case 'schema':
|
|
894
|
-
console.error('❌ Configuration schema is invalid');
|
|
895
|
-
console.error('Details:', error.details);
|
|
896
|
-
break;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
if (error.configPath) {
|
|
900
|
-
console.error(`Configuration file: ${error.configPath}`);
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
process.exit(1);
|
|
904
|
-
}
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
#### File System Error Handling
|
|
908
|
-
|
|
909
|
-
```typescript
|
|
910
|
-
function handleFileSystemError(error: FileSystemError) {
|
|
911
|
-
switch (error.errorType) {
|
|
912
|
-
case 'not_found':
|
|
913
|
-
if (error.operation === 'directory_access') {
|
|
914
|
-
console.error(`❌ Configuration directory not found: ${error.path}`);
|
|
915
|
-
console.error('Solutions:');
|
|
916
|
-
console.error(' 1. Create the directory: mkdir -p ' + error.path);
|
|
917
|
-
console.error(' 2. Use a different directory with --config-directory');
|
|
918
|
-
console.error(' 3. Set isRequired: false in your options');
|
|
919
|
-
} else {
|
|
920
|
-
console.error(`❌ Configuration file not found: ${error.path}`);
|
|
921
|
-
console.error('Create the configuration file or check the path.');
|
|
922
|
-
}
|
|
923
|
-
break;
|
|
924
|
-
|
|
925
|
-
case 'not_readable':
|
|
926
|
-
console.error(`❌ Cannot read ${error.path}`);
|
|
927
|
-
console.error('Check file/directory permissions:');
|
|
928
|
-
console.error(` chmod +r ${error.path}`);
|
|
929
|
-
break;
|
|
930
|
-
|
|
931
|
-
case 'creation_failed':
|
|
932
|
-
console.error(`❌ Failed to create directory: ${error.path}`);
|
|
933
|
-
console.error('Original error:', error.originalError?.message);
|
|
934
|
-
console.error('Check parent directory permissions.');
|
|
935
|
-
break;
|
|
936
|
-
|
|
937
|
-
case 'operation_failed':
|
|
938
|
-
console.error(`❌ File operation failed: ${error.operation}`);
|
|
939
|
-
console.error('Path:', error.path);
|
|
940
|
-
console.error('Error:', error.originalError?.message);
|
|
941
|
-
break;
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
process.exit(1);
|
|
945
|
-
}
|
|
946
|
-
```
|
|
947
|
-
|
|
948
|
-
#### Argument Error Handling
|
|
949
|
-
|
|
950
|
-
```typescript
|
|
951
|
-
function handleArgumentError(error: ArgumentError) {
|
|
952
|
-
console.error(`❌ Invalid argument: ${error.argument}`);
|
|
953
|
-
console.error(`Error: ${error.message}`);
|
|
954
|
-
console.error('Please check your command line arguments or function parameters.');
|
|
955
|
-
process.exit(1);
|
|
956
|
-
}
|
|
957
|
-
```
|
|
958
|
-
|
|
959
|
-
#### Graceful Degradation
|
|
960
|
-
|
|
961
|
-
```typescript
|
|
962
|
-
async function setupAppWithFallbacks() {
|
|
963
|
-
const cardigantime = create({
|
|
964
|
-
defaults: { configDirectory: './config' },
|
|
965
|
-
configShape: MyConfigSchema.shape,
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
try {
|
|
969
|
-
const config = await cardigantime.read(args);
|
|
970
|
-
await cardigantime.validate(config);
|
|
971
|
-
return config;
|
|
972
|
-
|
|
973
|
-
} catch (error) {
|
|
974
|
-
if (error instanceof FileSystemError && error.errorType === 'not_found') {
|
|
975
|
-
console.warn('⚠️ Configuration not found, using defaults');
|
|
976
|
-
return getDefaultConfig();
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
if (error instanceof ConfigurationError && error.errorType === 'extra_keys') {
|
|
980
|
-
console.warn('⚠️ Unknown config keys found, continuing with valid keys only');
|
|
981
|
-
// Filter out extra keys and retry
|
|
982
|
-
const cleanConfig = removeExtraKeys(config, error.details.allowedKeys);
|
|
983
|
-
await cardigantime.validate(cleanConfig);
|
|
984
|
-
return cleanConfig;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
// Re-throw other errors
|
|
988
|
-
throw error;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
### Error Messages and Troubleshooting
|
|
144
|
+
### Type Safety & Validation
|
|
145
|
+
Full TypeScript support with Zod schema validation for robust, type-safe configuration management.
|
|
994
146
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
**Schema validation failed:**
|
|
998
|
-
```typescript
|
|
999
|
-
// Error type: ConfigurationError with errorType: 'validation'
|
|
1000
|
-
{
|
|
1001
|
-
"port": {
|
|
1002
|
-
"_errors": ["Number must be greater than or equal to 1"]
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
```
|
|
1006
|
-
*Solution:* Fix the configuration values to match your schema requirements.
|
|
147
|
+
### Error Handling
|
|
148
|
+
Comprehensive error handling with detailed, actionable error messages to help users fix configuration issues quickly.
|
|
1007
149
|
|
|
1008
|
-
|
|
1009
|
-
```typescript
|
|
1010
|
-
// Error type: ConfigurationError with errorType: 'extra_keys'
|
|
1011
|
-
// error.details.extraKeys: ['databse']
|
|
1012
|
-
// error.details.allowedKeys: ['database', 'port', 'host']
|
|
1013
|
-
```
|
|
1014
|
-
*Solution:* Fix typos in your configuration file or update your schema.
|
|
150
|
+
## Documentation
|
|
1015
151
|
|
|
1016
|
-
|
|
152
|
+
📚 **[Complete Documentation](https://semicolonambulance.github.io/cardigantime/)** - Full documentation site
|
|
1017
153
|
|
|
1018
|
-
**
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
-
|
|
1026
|
-
- Use a different directory: `--config-directory ./config`
|
|
1027
|
-
- Make it optional: `isRequired: false`
|
|
1028
|
-
|
|
1029
|
-
**Directory not readable:**
|
|
1030
|
-
```typescript
|
|
1031
|
-
// Error type: FileSystemError with errorType: 'not_readable'
|
|
1032
|
-
// error.path: '/etc/restricted'
|
|
1033
|
-
// error.operation: 'directory_read'
|
|
1034
|
-
```
|
|
1035
|
-
*Solution:* Check file permissions: `chmod +r /etc/restricted`
|
|
1036
|
-
|
|
1037
|
-
#### Common Argument Errors
|
|
1038
|
-
|
|
1039
|
-
**Invalid config directory argument:**
|
|
1040
|
-
```typescript
|
|
1041
|
-
// Error type: ArgumentError with argument: 'config-directory'
|
|
1042
|
-
// Triggered by: --config-directory ""
|
|
1043
|
-
```
|
|
1044
|
-
*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
|
|
1045
162
|
|
|
1046
163
|
## Contributing
|
|
1047
164
|
|