@theunwalked/cardigantime 0.0.9 → 0.0.10

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 CHANGED
@@ -231,6 +231,16 @@ debug: true
231
231
 
232
232
  # Enable debug mode
233
233
  ./myapp --debug
234
+
235
+ # Generate initial configuration file
236
+ ./myapp --init-config
237
+
238
+ # Analyze configuration with source tracking (git blame-like output)
239
+ ./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
234
244
  ```
235
245
 
236
246
  ### Advanced Usage Examples
@@ -553,11 +563,19 @@ Creates a new Cardigantime instance.
553
563
  - `configFile` (optional): Config filename, defaults to `'config.yaml'`
554
564
  - `isRequired` (optional): Whether config directory must exist, defaults to `false`
555
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
556
568
  - `options.configShape` (required): Zod schema shape for validation
557
569
  - `options.features` (optional): Array of features to enable, defaults to `['config']`
558
570
  - `options.logger` (optional): Custom logger implementation
559
571
 
560
- **Returns:** `Cardigantime` instance
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
561
579
 
562
580
  ### `cardigantime.configure(command)`
563
581
 
@@ -568,8 +586,10 @@ Adds Cardigantime's CLI options to a Commander.js command.
568
586
 
569
587
  **Returns:** Promise<Command> - The modified command
570
588
 
571
- **Added Options:**
572
- - `-c, --config-directory <path>`: Override config directory
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
573
593
 
574
594
  ### `cardigantime.read(args)`
575
595
 
@@ -589,6 +609,37 @@ Validates configuration against the schema.
589
609
 
590
610
  **Returns:** Promise<void> - Throws on validation failure
591
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
+
592
643
  ### `cardigantime.setLogger(logger)`
593
644
 
594
645
  Sets a custom logger for debugging and error reporting.
@@ -596,6 +647,429 @@ Sets a custom logger for debugging and error reporting.
596
647
  **Parameters:**
597
648
  - `logger`: Logger implementing the Logger interface
598
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
+
599
1073
  ## Advanced Usage
600
1074
 
601
1075
  ### Hierarchical Configuration Discovery