@utilarium/cardigantime 0.0.24-dev.0
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/LICENSE +65 -0
- package/README.md +398 -0
- package/dist/cardigantime.cjs +2169 -0
- package/dist/cardigantime.cjs.map +1 -0
- package/dist/cardigantime.d.ts +92 -0
- package/dist/cardigantime.js +198 -0
- package/dist/cardigantime.js.map +1 -0
- package/dist/config/executable-security.d.ts +32 -0
- package/dist/config/format-detector.d.ts +59 -0
- package/dist/configure.d.ts +55 -0
- package/dist/configure.js +125 -0
- package/dist/configure.js.map +1 -0
- package/dist/constants.d.ts +25 -0
- package/dist/constants.js +38 -0
- package/dist/constants.js.map +1 -0
- package/dist/discovery/discoverer.d.ts +62 -0
- package/dist/discovery/hierarchical-modes.d.ts +64 -0
- package/dist/discovery/index.d.ts +15 -0
- package/dist/discovery/patterns.d.ts +77 -0
- package/dist/discovery/root-detection.d.ts +100 -0
- package/dist/discovery/traversal-security.d.ts +106 -0
- package/dist/env/errors.d.ts +18 -0
- package/dist/env/index.d.ts +7 -0
- package/dist/env/naming.d.ts +38 -0
- package/dist/env/parser.d.ts +61 -0
- package/dist/env/reader.d.ts +45 -0
- package/dist/env/resolver.d.ts +25 -0
- package/dist/env/schema-utils.d.ts +33 -0
- package/dist/env/types.d.ts +43 -0
- package/dist/error/ArgumentError.d.ts +31 -0
- package/dist/error/ArgumentError.js +48 -0
- package/dist/error/ArgumentError.js.map +1 -0
- package/dist/error/ConfigParseError.d.ts +26 -0
- package/dist/error/ConfigurationError.d.ts +21 -0
- package/dist/error/ConfigurationError.js +46 -0
- package/dist/error/ConfigurationError.js.map +1 -0
- package/dist/error/FileSystemError.d.ts +30 -0
- package/dist/error/FileSystemError.js +58 -0
- package/dist/error/FileSystemError.js.map +1 -0
- package/dist/error/index.d.ts +4 -0
- package/dist/mcp/discovery.d.ts +105 -0
- package/dist/mcp/errors.d.ts +75 -0
- package/dist/mcp/index.d.ts +22 -0
- package/dist/mcp/integration.d.ts +184 -0
- package/dist/mcp/parser.d.ts +141 -0
- package/dist/mcp/resolver.d.ts +165 -0
- package/dist/mcp/tools/check-config-types.d.ts +208 -0
- package/dist/mcp/tools/check-config.d.ts +85 -0
- package/dist/mcp/tools/index.d.ts +12 -0
- package/dist/mcp/types.d.ts +210 -0
- package/dist/parsers/index.d.ts +25 -0
- package/dist/parsers/javascript-parser.d.ts +12 -0
- package/dist/parsers/json-parser.d.ts +6 -0
- package/dist/parsers/typescript-parser.d.ts +15 -0
- package/dist/parsers/yaml-parser.d.ts +6 -0
- package/dist/read.d.ts +56 -0
- package/dist/read.js +653 -0
- package/dist/read.js.map +1 -0
- package/dist/security/audit-logger.d.ts +135 -0
- package/dist/security/cli-validator.d.ts +73 -0
- package/dist/security/config-validator.d.ts +95 -0
- package/dist/security/defaults.d.ts +17 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/numeric-guard.d.ts +111 -0
- package/dist/security/path-guard.d.ts +53 -0
- package/dist/security/profiles.d.ts +127 -0
- package/dist/security/security-validator.d.ts +109 -0
- package/dist/security/string-guard.d.ts +92 -0
- package/dist/security/types.d.ts +126 -0
- package/dist/security/zod-secure-enum.d.ts +20 -0
- package/dist/security/zod-secure-number.d.ts +39 -0
- package/dist/security/zod-secure-path.d.ts +24 -0
- package/dist/security/zod-secure-string.d.ts +38 -0
- package/dist/types.d.ts +584 -0
- package/dist/types.js +56 -0
- package/dist/types.js.map +1 -0
- package/dist/util/hierarchical.d.ts +136 -0
- package/dist/util/hierarchical.js +436 -0
- package/dist/util/hierarchical.js.map +1 -0
- package/dist/util/schema-defaults.d.ts +80 -0
- package/dist/util/schema-defaults.js +118 -0
- package/dist/util/schema-defaults.js.map +1 -0
- package/dist/util/storage.d.ts +31 -0
- package/dist/util/storage.js +154 -0
- package/dist/util/storage.js.map +1 -0
- package/dist/validate.d.ts +113 -0
- package/dist/validate.js +260 -0
- package/dist/validate.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { configure } from './configure.js';
|
|
2
|
+
import { DEFAULT_FEATURES, DEFAULT_LOGGER, DEFAULT_OPTIONS } from './constants.js';
|
|
3
|
+
export { PROGRAM_NAME, VERSION } from './constants.js';
|
|
4
|
+
import { checkConfig, read } from './read.js';
|
|
5
|
+
import { validate } from './validate.js';
|
|
6
|
+
import * as yaml from 'js-yaml';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import { generateDefaultConfig } from './util/schema-defaults.js';
|
|
9
|
+
import { create as create$1 } from './util/storage.js';
|
|
10
|
+
import { FileSystemError } from './error/FileSystemError.js';
|
|
11
|
+
export { ConfigFormat, ConfigSchema, DEFAULT_ROOT_MARKERS } from './types.js';
|
|
12
|
+
export { ArgumentError } from './error/ArgumentError.js';
|
|
13
|
+
export { ConfigurationError } from './error/ConfigurationError.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new Cardigantime instance for configuration management.
|
|
17
|
+
*
|
|
18
|
+
* Cardigantime handles the complete configuration lifecycle including:
|
|
19
|
+
* - Reading configuration from YAML files
|
|
20
|
+
* - Validating configuration against Zod schemas
|
|
21
|
+
* - Merging CLI arguments with file configuration and defaults
|
|
22
|
+
* - Providing type-safe configuration objects
|
|
23
|
+
*
|
|
24
|
+
* @template T - The Zod schema shape type for your configuration
|
|
25
|
+
* @param pOptions - Configuration options for the Cardigantime instance
|
|
26
|
+
* @param pOptions.defaults - Default configuration settings
|
|
27
|
+
* @param pOptions.defaults.configDirectory - Directory to search for configuration files (required)
|
|
28
|
+
* @param pOptions.defaults.configFile - Name of the configuration file (optional, defaults to 'config.yaml')
|
|
29
|
+
* @param pOptions.defaults.isRequired - Whether the config directory must exist (optional, defaults to false)
|
|
30
|
+
* @param pOptions.defaults.encoding - File encoding for reading config files (optional, defaults to 'utf8')
|
|
31
|
+
* @param pOptions.defaults.pathResolution - Configuration for resolving relative paths in config values relative to the config file's directory (optional)
|
|
32
|
+
* @param pOptions.features - Array of features to enable (optional, defaults to ['config'])
|
|
33
|
+
* @param pOptions.configShape - Zod schema shape defining your configuration structure (required)
|
|
34
|
+
* @param pOptions.logger - Custom logger implementation (optional, defaults to console logger)
|
|
35
|
+
* @returns A Cardigantime instance with methods for configure, read, validate, and setLogger
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import { create } from '@utilarium/cardigantime';
|
|
40
|
+
* import { z } from 'zod';
|
|
41
|
+
*
|
|
42
|
+
* const MyConfigSchema = z.object({
|
|
43
|
+
* apiKey: z.string().min(1),
|
|
44
|
+
* timeout: z.number().default(5000),
|
|
45
|
+
* debug: z.boolean().default(false),
|
|
46
|
+
* contextDirectories: z.array(z.string()).optional(),
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* const cardigantime = create({
|
|
50
|
+
* defaults: {
|
|
51
|
+
* configDirectory: './config',
|
|
52
|
+
* configFile: 'myapp.yaml',
|
|
53
|
+
* // Resolve relative paths in contextDirectories relative to config file location
|
|
54
|
+
* pathResolution: {
|
|
55
|
+
* pathFields: ['contextDirectories'],
|
|
56
|
+
* resolvePathArray: ['contextDirectories']
|
|
57
|
+
* },
|
|
58
|
+
* // Configure how array fields are merged in hierarchical mode
|
|
59
|
+
* fieldOverlaps: {
|
|
60
|
+
* 'features': 'append', // Accumulate features from all levels
|
|
61
|
+
* 'excludePatterns': 'prepend' // Higher precedence patterns come first
|
|
62
|
+
* }
|
|
63
|
+
* },
|
|
64
|
+
* configShape: MyConfigSchema.shape,
|
|
65
|
+
* features: ['config', 'hierarchical'], // Enable hierarchical discovery
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* // If config file is at ../config/myapp.yaml and contains:
|
|
69
|
+
* // contextDirectories: ['./context', './data']
|
|
70
|
+
* // These paths will be resolved relative to ../config/ directory
|
|
71
|
+
* ```
|
|
72
|
+
*/ const create = (pOptions)=>{
|
|
73
|
+
// Validate that configDirectory is a string
|
|
74
|
+
if (!pOptions.defaults.configDirectory || typeof pOptions.defaults.configDirectory !== 'string') {
|
|
75
|
+
throw new Error(`Configuration directory must be a string, received: ${typeof pOptions.defaults.configDirectory} (${JSON.stringify(pOptions.defaults.configDirectory)})`);
|
|
76
|
+
}
|
|
77
|
+
const defaults = {
|
|
78
|
+
...DEFAULT_OPTIONS,
|
|
79
|
+
...pOptions.defaults
|
|
80
|
+
};
|
|
81
|
+
const features = pOptions.features || DEFAULT_FEATURES;
|
|
82
|
+
const configShape = pOptions.configShape;
|
|
83
|
+
let logger = pOptions.logger || DEFAULT_LOGGER;
|
|
84
|
+
const options = {
|
|
85
|
+
defaults,
|
|
86
|
+
features,
|
|
87
|
+
configShape,
|
|
88
|
+
logger
|
|
89
|
+
};
|
|
90
|
+
const setLogger = (pLogger)=>{
|
|
91
|
+
logger = pLogger;
|
|
92
|
+
options.logger = pLogger;
|
|
93
|
+
};
|
|
94
|
+
const generateConfig = async (configDirectory)=>{
|
|
95
|
+
const targetDir = configDirectory || options.defaults.configDirectory;
|
|
96
|
+
const configFile = options.defaults.configFile;
|
|
97
|
+
const encoding = options.defaults.encoding;
|
|
98
|
+
// Validate that targetDir is a string
|
|
99
|
+
if (!targetDir || typeof targetDir !== 'string') {
|
|
100
|
+
throw new Error(`Configuration directory must be a string, received: ${typeof targetDir} (${JSON.stringify(targetDir)})`);
|
|
101
|
+
}
|
|
102
|
+
logger.verbose(`Generating configuration file in: ${targetDir}`);
|
|
103
|
+
// Create storage utility
|
|
104
|
+
const storage = create$1({
|
|
105
|
+
log: logger.debug
|
|
106
|
+
});
|
|
107
|
+
// Ensure the target directory exists
|
|
108
|
+
const dirExists = await storage.exists(targetDir);
|
|
109
|
+
if (!dirExists) {
|
|
110
|
+
logger.info(`Creating configuration directory: ${targetDir}`);
|
|
111
|
+
try {
|
|
112
|
+
await storage.createDirectory(targetDir);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw FileSystemError.directoryCreationFailed(targetDir, error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Check if directory is writable
|
|
118
|
+
const isWritable = await storage.isDirectoryWritable(targetDir);
|
|
119
|
+
if (!isWritable) {
|
|
120
|
+
throw new FileSystemError('not_writable', 'Configuration directory is not writable', targetDir, 'directory_write');
|
|
121
|
+
}
|
|
122
|
+
// Build the full config file path
|
|
123
|
+
const configFilePath = path.join(targetDir, configFile);
|
|
124
|
+
// Generate default configuration
|
|
125
|
+
logger.debug(`Generating defaults for schema with keys: ${Object.keys(options.configShape).join(', ')}`);
|
|
126
|
+
const defaultConfig = generateDefaultConfig(options.configShape);
|
|
127
|
+
logger.debug(`Generated default config: ${JSON.stringify(defaultConfig, null, 2)}`);
|
|
128
|
+
// Convert to YAML with nice formatting
|
|
129
|
+
const yamlContent = yaml.dump(defaultConfig, {
|
|
130
|
+
indent: 2,
|
|
131
|
+
lineWidth: 120,
|
|
132
|
+
noRefs: true,
|
|
133
|
+
sortKeys: true
|
|
134
|
+
});
|
|
135
|
+
// Add header comment to the YAML file
|
|
136
|
+
const header = `# Configuration file generated by Cardigantime
|
|
137
|
+
# This file contains default values for your application configuration.
|
|
138
|
+
# Modify the values below to customize your application's behavior.
|
|
139
|
+
#
|
|
140
|
+
# For more information about Cardigantime configuration:
|
|
141
|
+
# https://utilarium.github.io/cardigantime/
|
|
142
|
+
|
|
143
|
+
`;
|
|
144
|
+
const finalContent = header + yamlContent;
|
|
145
|
+
// Check if config file already exists
|
|
146
|
+
const configExists = await storage.exists(configFilePath);
|
|
147
|
+
if (configExists) {
|
|
148
|
+
logger.warn(`Configuration file already exists: ${configFilePath}`);
|
|
149
|
+
logger.warn('This file was not overwritten, but here is what the default configuration looks like if you want to copy it:');
|
|
150
|
+
logger.info('\n' + '='.repeat(60));
|
|
151
|
+
logger.info(finalContent.trim());
|
|
152
|
+
logger.info('='.repeat(60));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Write the configuration file
|
|
156
|
+
try {
|
|
157
|
+
await storage.writeFile(configFilePath, finalContent, encoding);
|
|
158
|
+
logger.info(`Configuration file generated successfully: ${configFilePath}`);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw FileSystemError.operationFailed('write configuration file', configFilePath, error);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
setLogger,
|
|
165
|
+
configure: (command)=>configure(command, options),
|
|
166
|
+
validate: (config)=>validate(config, options),
|
|
167
|
+
read: (args)=>read(args, options),
|
|
168
|
+
generateConfig,
|
|
169
|
+
checkConfig: (args)=>checkConfig(args, options)
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Type-safe helper for defining configuration in TypeScript/JavaScript files.
|
|
174
|
+
*
|
|
175
|
+
* This is a simple identity function that provides type checking and
|
|
176
|
+
* autocomplete for configuration objects when using TypeScript config files.
|
|
177
|
+
*
|
|
178
|
+
* @template T - The configuration type
|
|
179
|
+
* @param config - The configuration object
|
|
180
|
+
* @returns The same configuration object (identity function)
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* // config.ts
|
|
185
|
+
* import { defineConfig } from '@utilarium/cardigantime';
|
|
186
|
+
*
|
|
187
|
+
* export default defineConfig({
|
|
188
|
+
* apiKey: process.env.API_KEY || 'default-key',
|
|
189
|
+
* timeout: 5000,
|
|
190
|
+
* debug: process.env.NODE_ENV === 'development'
|
|
191
|
+
* });
|
|
192
|
+
* ```
|
|
193
|
+
*/ function defineConfig(config) {
|
|
194
|
+
return config;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export { FileSystemError, create, defineConfig };
|
|
198
|
+
//# sourceMappingURL=cardigantime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cardigantime.js","sources":["../src/cardigantime.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { Args, DefaultOptions, Feature, Cardigantime, Logger, Options } from 'types';\nimport { z, ZodObject } from 'zod';\nimport { configure } from './configure';\nimport { DEFAULT_FEATURES, DEFAULT_LOGGER, DEFAULT_OPTIONS } from './constants';\nimport { read, checkConfig } from './read';\nimport { ConfigSchema } from 'types';\nimport { validate } from './validate';\nimport * as yaml from 'js-yaml';\nimport * as path from 'node:path';\nimport { generateDefaultConfig } from './util/schema-defaults';\nimport * as Storage from './util/storage';\nimport { FileSystemError } from './error/FileSystemError';\n\nexport * from './types';\nexport { ArgumentError, ConfigurationError, FileSystemError } from './validate';\nexport { VERSION, PROGRAM_NAME } from './constants';\n\n/**\n * Creates a new Cardigantime instance for configuration management.\n * \n * Cardigantime handles the complete configuration lifecycle including:\n * - Reading configuration from YAML files\n * - Validating configuration against Zod schemas\n * - Merging CLI arguments with file configuration and defaults\n * - Providing type-safe configuration objects\n * \n * @template T - The Zod schema shape type for your configuration\n * @param pOptions - Configuration options for the Cardigantime instance\n * @param pOptions.defaults - Default configuration settings\n * @param pOptions.defaults.configDirectory - Directory to search for configuration files (required)\n * @param pOptions.defaults.configFile - Name of the configuration file (optional, defaults to 'config.yaml')\n * @param pOptions.defaults.isRequired - Whether the config directory must exist (optional, defaults to false)\n * @param pOptions.defaults.encoding - File encoding for reading config files (optional, defaults to 'utf8')\n * @param pOptions.defaults.pathResolution - Configuration for resolving relative paths in config values relative to the config file's directory (optional)\n * @param pOptions.features - Array of features to enable (optional, defaults to ['config'])\n * @param pOptions.configShape - Zod schema shape defining your configuration structure (required)\n * @param pOptions.logger - Custom logger implementation (optional, defaults to console logger)\n * @returns A Cardigantime instance with methods for configure, read, validate, and setLogger\n * \n * @example\n * ```typescript\n * import { create } from '@utilarium/cardigantime';\n * import { z } from 'zod';\n * \n * const MyConfigSchema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().default(5000),\n * debug: z.boolean().default(false),\n * contextDirectories: z.array(z.string()).optional(),\n * });\n * \n * const cardigantime = create({\n * defaults: {\n * configDirectory: './config',\n * configFile: 'myapp.yaml',\n * // Resolve relative paths in contextDirectories relative to config file location\n * pathResolution: {\n * pathFields: ['contextDirectories'],\n * resolvePathArray: ['contextDirectories']\n * },\n * // Configure how array fields are merged in hierarchical mode\n * fieldOverlaps: {\n * 'features': 'append', // Accumulate features from all levels\n * 'excludePatterns': 'prepend' // Higher precedence patterns come first\n * }\n * },\n * configShape: MyConfigSchema.shape,\n * features: ['config', 'hierarchical'], // Enable hierarchical discovery\n * });\n * \n * // If config file is at ../config/myapp.yaml and contains:\n * // contextDirectories: ['./context', './data']\n * // These paths will be resolved relative to ../config/ directory\n * ```\n */\nexport const create = <T extends z.ZodRawShape>(pOptions: {\n defaults: Pick<DefaultOptions, 'configDirectory'> & Partial<Omit<DefaultOptions, 'configDirectory'>>,\n features?: Feature[],\n configShape: T, // Make configShape mandatory\n logger?: Logger,\n}): Cardigantime<T> => {\n\n // Validate that configDirectory is a string\n if (!pOptions.defaults.configDirectory || typeof pOptions.defaults.configDirectory !== 'string') {\n throw new Error(`Configuration directory must be a string, received: ${typeof pOptions.defaults.configDirectory} (${JSON.stringify(pOptions.defaults.configDirectory)})`);\n }\n\n const defaults: DefaultOptions = { ...DEFAULT_OPTIONS, ...pOptions.defaults } as DefaultOptions;\n const features = pOptions.features || DEFAULT_FEATURES;\n const configShape = pOptions.configShape;\n let logger = pOptions.logger || DEFAULT_LOGGER;\n\n const options: Options<T> = {\n defaults,\n features,\n configShape, // Store the shape\n logger,\n }\n\n const setLogger = (pLogger: Logger) => {\n logger = pLogger;\n options.logger = pLogger;\n }\n\n const generateConfig = async (configDirectory?: string): Promise<void> => {\n const targetDir = configDirectory || options.defaults.configDirectory;\n const configFile = options.defaults.configFile;\n const encoding = options.defaults.encoding;\n\n // Validate that targetDir is a string\n if (!targetDir || typeof targetDir !== 'string') {\n throw new Error(`Configuration directory must be a string, received: ${typeof targetDir} (${JSON.stringify(targetDir)})`);\n }\n\n logger.verbose(`Generating configuration file in: ${targetDir}`);\n\n // Create storage utility\n const storage = Storage.create({ log: logger.debug });\n\n // Ensure the target directory exists\n const dirExists = await storage.exists(targetDir);\n if (!dirExists) {\n logger.info(`Creating configuration directory: ${targetDir}`);\n try {\n await storage.createDirectory(targetDir);\n } catch (error: any) {\n throw FileSystemError.directoryCreationFailed(targetDir, error);\n }\n }\n\n // Check if directory is writable\n const isWritable = await storage.isDirectoryWritable(targetDir);\n if (!isWritable) {\n throw new FileSystemError('not_writable', 'Configuration directory is not writable', targetDir, 'directory_write');\n }\n\n // Build the full config file path\n const configFilePath = path.join(targetDir, configFile);\n\n // Generate default configuration\n logger.debug(`Generating defaults for schema with keys: ${Object.keys(options.configShape).join(', ')}`);\n const defaultConfig = generateDefaultConfig(options.configShape, targetDir);\n logger.debug(`Generated default config: ${JSON.stringify(defaultConfig, null, 2)}`);\n\n // Convert to YAML with nice formatting\n const yamlContent = yaml.dump(defaultConfig, {\n indent: 2,\n lineWidth: 120,\n noRefs: true,\n sortKeys: true\n });\n\n // Add header comment to the YAML file\n const header = `# Configuration file generated by Cardigantime\n# This file contains default values for your application configuration.\n# Modify the values below to customize your application's behavior.\n#\n# For more information about Cardigantime configuration:\n# https://utilarium.github.io/cardigantime/\n\n`;\n\n const finalContent = header + yamlContent;\n\n // Check if config file already exists\n const configExists = await storage.exists(configFilePath);\n if (configExists) {\n logger.warn(`Configuration file already exists: ${configFilePath}`);\n logger.warn('This file was not overwritten, but here is what the default configuration looks like if you want to copy it:');\n logger.info('\\n' + '='.repeat(60));\n logger.info(finalContent.trim());\n logger.info('='.repeat(60));\n return;\n }\n\n // Write the configuration file\n try {\n await storage.writeFile(configFilePath, finalContent, encoding);\n logger.info(`Configuration file generated successfully: ${configFilePath}`);\n } catch (error: any) {\n throw FileSystemError.operationFailed('write configuration file', configFilePath, error);\n }\n };\n\n return {\n setLogger,\n configure: (command: Command) => configure(command, options),\n validate: (config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>) => validate(config, options),\n read: (args: Args) => read(args, options),\n generateConfig,\n checkConfig: (args: Args) => checkConfig(args, options),\n }\n}\n\n/**\n * Type-safe helper for defining configuration in TypeScript/JavaScript files.\n * \n * This is a simple identity function that provides type checking and\n * autocomplete for configuration objects when using TypeScript config files.\n * \n * @template T - The configuration type\n * @param config - The configuration object\n * @returns The same configuration object (identity function)\n * \n * @example\n * ```typescript\n * // config.ts\n * import { defineConfig } from '@utilarium/cardigantime';\n * \n * export default defineConfig({\n * apiKey: process.env.API_KEY || 'default-key',\n * timeout: 5000,\n * debug: process.env.NODE_ENV === 'development'\n * });\n * ```\n */\nexport function defineConfig<T>(config: T): T {\n return config;\n}\n\n"],"names":["create","pOptions","defaults","configDirectory","Error","JSON","stringify","DEFAULT_OPTIONS","features","DEFAULT_FEATURES","configShape","logger","DEFAULT_LOGGER","options","setLogger","pLogger","generateConfig","targetDir","configFile","encoding","verbose","storage","Storage","log","debug","dirExists","exists","info","createDirectory","error","FileSystemError","directoryCreationFailed","isWritable","isDirectoryWritable","configFilePath","path","join","Object","keys","defaultConfig","generateDefaultConfig","yamlContent","yaml","dump","indent","lineWidth","noRefs","sortKeys","header","finalContent","configExists","warn","repeat","trim","writeFile","operationFailed","configure","command","validate","config","read","args","checkConfig","defineConfig"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ConfigFormat, Logger } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Security error thrown when executable config is detected but not allowed.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ExecutableConfigSecurityError extends Error {
|
|
6
|
+
readonly format: ConfigFormat;
|
|
7
|
+
readonly filePath: string;
|
|
8
|
+
constructor(format: ConfigFormat, filePath: string);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Checks if a configuration format is executable (JavaScript or TypeScript).
|
|
12
|
+
*
|
|
13
|
+
* @param format - The configuration format to check
|
|
14
|
+
* @returns True if the format is executable
|
|
15
|
+
*/
|
|
16
|
+
export declare function isExecutableFormat(format: ConfigFormat): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Validates that executable configuration is allowed before loading.
|
|
19
|
+
*
|
|
20
|
+
* @param format - The configuration format being loaded
|
|
21
|
+
* @param filePath - Path to the configuration file
|
|
22
|
+
* @param allowExecutableConfig - Whether executable configs are allowed
|
|
23
|
+
* @param logger - Optional logger for warnings
|
|
24
|
+
* @throws {ExecutableConfigSecurityError} When executable config is not allowed
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateExecutableConfig(format: ConfigFormat, filePath: string, allowExecutableConfig?: boolean, logger?: Logger): void;
|
|
27
|
+
/**
|
|
28
|
+
* Gets a user-friendly description of the security model for executable configs.
|
|
29
|
+
*
|
|
30
|
+
* @returns Security model description
|
|
31
|
+
*/
|
|
32
|
+
export declare function getExecutableConfigSecurityModel(): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ConfigFormat, Logger } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Options for format detection.
|
|
4
|
+
*/
|
|
5
|
+
export interface FormatDetectorOptions {
|
|
6
|
+
/** Base configuration file name without extension (e.g., 'config') */
|
|
7
|
+
configFileName: string;
|
|
8
|
+
/** Directory to search for configuration files */
|
|
9
|
+
configDirectory: string;
|
|
10
|
+
/** Optional format override to force a specific format */
|
|
11
|
+
formatOverride?: ConfigFormat;
|
|
12
|
+
/** Logger for debugging */
|
|
13
|
+
logger?: Logger;
|
|
14
|
+
/** Storage interface for file system operations */
|
|
15
|
+
storage: {
|
|
16
|
+
exists(filePath: string): Promise<boolean>;
|
|
17
|
+
isFileReadable(filePath: string): Promise<boolean>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Result of format detection.
|
|
22
|
+
*/
|
|
23
|
+
export interface DetectedFormat {
|
|
24
|
+
/** The detected or overridden format */
|
|
25
|
+
format: ConfigFormat;
|
|
26
|
+
/** Full path to the configuration file */
|
|
27
|
+
filePath: string;
|
|
28
|
+
/** Whether this was an override or auto-detected */
|
|
29
|
+
wasOverridden: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Detects the configuration file format to use.
|
|
33
|
+
*
|
|
34
|
+
* Searches for configuration files in priority order:
|
|
35
|
+
* 1. TypeScript (.ts, .mts, .cts)
|
|
36
|
+
* 2. JavaScript (.js, .mjs, .cjs)
|
|
37
|
+
* 3. JSON (.json)
|
|
38
|
+
* 4. YAML (.yaml, .yml)
|
|
39
|
+
*
|
|
40
|
+
* If a format override is provided, only that format is checked.
|
|
41
|
+
*
|
|
42
|
+
* @param options - Format detection options
|
|
43
|
+
* @returns Promise resolving to detected format info, or null if no config found
|
|
44
|
+
*/
|
|
45
|
+
export declare function detectConfigFormat(options: FormatDetectorOptions): Promise<DetectedFormat | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Gets the priority value for a format (higher = more priority).
|
|
48
|
+
*
|
|
49
|
+
* @param format - The configuration format
|
|
50
|
+
* @returns Priority value (0-based index)
|
|
51
|
+
*/
|
|
52
|
+
export declare function getFormatPriority(format: ConfigFormat): number;
|
|
53
|
+
/**
|
|
54
|
+
* Gets all file extensions for a given format.
|
|
55
|
+
*
|
|
56
|
+
* @param format - The configuration format
|
|
57
|
+
* @returns Array of file extensions (e.g., ['.yaml', '.yml'])
|
|
58
|
+
*/
|
|
59
|
+
export declare function getFormatExtensions(format: ConfigFormat): string[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ArgumentError } from './error/ArgumentError';
|
|
4
|
+
import { Options } from './types';
|
|
5
|
+
export { ArgumentError };
|
|
6
|
+
/**
|
|
7
|
+
* Validates a configuration directory path to ensure it's safe and valid.
|
|
8
|
+
*
|
|
9
|
+
* Performs security and safety checks including:
|
|
10
|
+
* - Non-empty string validation
|
|
11
|
+
* - Null byte injection prevention
|
|
12
|
+
* - Path length validation
|
|
13
|
+
* - Type checking
|
|
14
|
+
*
|
|
15
|
+
* @param configDirectory - The configuration directory path to validate
|
|
16
|
+
* @param _testThrowNonArgumentError - Internal testing parameter to simulate non-ArgumentError exceptions
|
|
17
|
+
* @returns The trimmed and validated configuration directory path
|
|
18
|
+
* @throws {ArgumentError} When the directory path is invalid
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const validDir = validateConfigDirectory('./config'); // Returns './config'
|
|
23
|
+
* const invalidDir = validateConfigDirectory(''); // Throws ArgumentError
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateConfigDirectory(configDirectory: string, _testThrowNonArgumentError?: boolean): string;
|
|
27
|
+
/**
|
|
28
|
+
* Configures a Commander.js command with Cardigantime's CLI options.
|
|
29
|
+
*
|
|
30
|
+
* This function adds command-line options that allow users to override
|
|
31
|
+
* configuration settings at runtime, such as:
|
|
32
|
+
* - --config-directory: Override the default configuration directory
|
|
33
|
+
*
|
|
34
|
+
* The function validates both the command object and the options to ensure
|
|
35
|
+
* they meet the requirements for proper integration.
|
|
36
|
+
*
|
|
37
|
+
* @template T - The Zod schema shape type for configuration validation
|
|
38
|
+
* @param command - The Commander.js Command instance to configure
|
|
39
|
+
* @param options - Cardigantime options containing defaults and schema
|
|
40
|
+
* @param _testThrowNonArgumentError - Internal testing parameter
|
|
41
|
+
* @returns Promise resolving to the configured Command instance
|
|
42
|
+
* @throws {ArgumentError} When command or options are invalid
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { Command } from 'commander';
|
|
47
|
+
* import { configure } from './configure';
|
|
48
|
+
*
|
|
49
|
+
* const program = new Command();
|
|
50
|
+
* const configuredProgram = await configure(program, options);
|
|
51
|
+
*
|
|
52
|
+
* // Now the program accepts: --config-directory <path>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare const configure: <T extends z.ZodRawShape>(command: Command, options: Options<T>, _testThrowNonArgumentError?: boolean) => Promise<Command>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { ArgumentError } from './error/ArgumentError.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates a configuration directory path to ensure it's safe and valid.
|
|
5
|
+
*
|
|
6
|
+
* Performs security and safety checks including:
|
|
7
|
+
* - Non-empty string validation
|
|
8
|
+
* - Null byte injection prevention
|
|
9
|
+
* - Path length validation
|
|
10
|
+
* - Type checking
|
|
11
|
+
*
|
|
12
|
+
* @param configDirectory - The configuration directory path to validate
|
|
13
|
+
* @param _testThrowNonArgumentError - Internal testing parameter to simulate non-ArgumentError exceptions
|
|
14
|
+
* @returns The trimmed and validated configuration directory path
|
|
15
|
+
* @throws {ArgumentError} When the directory path is invalid
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const validDir = validateConfigDirectory('./config'); // Returns './config'
|
|
20
|
+
* const invalidDir = validateConfigDirectory(''); // Throws ArgumentError
|
|
21
|
+
* ```
|
|
22
|
+
*/ function validateConfigDirectory(configDirectory, _testThrowNonArgumentError) {
|
|
23
|
+
if (!configDirectory) {
|
|
24
|
+
throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty');
|
|
25
|
+
}
|
|
26
|
+
if (typeof configDirectory !== 'string') {
|
|
27
|
+
throw new ArgumentError('configDirectory', 'Configuration directory must be a string');
|
|
28
|
+
}
|
|
29
|
+
const trimmed = configDirectory.trim();
|
|
30
|
+
if (trimmed.length === 0) {
|
|
31
|
+
throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty or whitespace only');
|
|
32
|
+
}
|
|
33
|
+
// Check for obviously invalid paths
|
|
34
|
+
if (trimmed.includes('\0')) {
|
|
35
|
+
throw new ArgumentError('configDirectory', 'Configuration directory contains invalid null character');
|
|
36
|
+
}
|
|
37
|
+
// Validate path length (reasonable limit)
|
|
38
|
+
if (trimmed.length > 1000) {
|
|
39
|
+
throw new ArgumentError('configDirectory', 'Configuration directory path is too long (max 1000 characters)');
|
|
40
|
+
}
|
|
41
|
+
return trimmed;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Configures a Commander.js command with Cardigantime's CLI options.
|
|
45
|
+
*
|
|
46
|
+
* This function adds command-line options that allow users to override
|
|
47
|
+
* configuration settings at runtime, such as:
|
|
48
|
+
* - --config-directory: Override the default configuration directory
|
|
49
|
+
*
|
|
50
|
+
* The function validates both the command object and the options to ensure
|
|
51
|
+
* they meet the requirements for proper integration.
|
|
52
|
+
*
|
|
53
|
+
* @template T - The Zod schema shape type for configuration validation
|
|
54
|
+
* @param command - The Commander.js Command instance to configure
|
|
55
|
+
* @param options - Cardigantime options containing defaults and schema
|
|
56
|
+
* @param _testThrowNonArgumentError - Internal testing parameter
|
|
57
|
+
* @returns Promise resolving to the configured Command instance
|
|
58
|
+
* @throws {ArgumentError} When command or options are invalid
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* import { Command } from 'commander';
|
|
63
|
+
* import { configure } from './configure';
|
|
64
|
+
*
|
|
65
|
+
* const program = new Command();
|
|
66
|
+
* const configuredProgram = await configure(program, options);
|
|
67
|
+
*
|
|
68
|
+
* // Now the program accepts: --config-directory <path>
|
|
69
|
+
* ```
|
|
70
|
+
*/ const configure = async (command, options, _testThrowNonArgumentError)=>{
|
|
71
|
+
// Validate the command object
|
|
72
|
+
if (!command) {
|
|
73
|
+
throw new ArgumentError('command', 'Command instance is required');
|
|
74
|
+
}
|
|
75
|
+
if (typeof command.option !== 'function') {
|
|
76
|
+
throw new ArgumentError('command', 'Command must be a valid Commander.js Command instance');
|
|
77
|
+
}
|
|
78
|
+
// Validate options
|
|
79
|
+
if (!options) {
|
|
80
|
+
throw new ArgumentError('options', 'Options object is required');
|
|
81
|
+
}
|
|
82
|
+
if (!options.defaults) {
|
|
83
|
+
throw new ArgumentError('options.defaults', 'Options must include defaults configuration');
|
|
84
|
+
}
|
|
85
|
+
if (!options.defaults.configDirectory) {
|
|
86
|
+
throw new ArgumentError('options.defaults.configDirectory', 'Default config directory is required');
|
|
87
|
+
}
|
|
88
|
+
// Validate the default config directory
|
|
89
|
+
const validatedDefaultDir = validateConfigDirectory(options.defaults.configDirectory);
|
|
90
|
+
let retCommand = command;
|
|
91
|
+
// Add the config directory option with validation
|
|
92
|
+
retCommand = retCommand.option('-c, --config-directory <configDirectory>', 'Configuration directory path', (value)=>{
|
|
93
|
+
try {
|
|
94
|
+
return validateConfigDirectory(value, _testThrowNonArgumentError);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (error instanceof ArgumentError) {
|
|
97
|
+
// Re-throw with more specific context for CLI usage
|
|
98
|
+
throw new ArgumentError('config-directory', `Invalid --config-directory: ${error.message}`);
|
|
99
|
+
}
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}, validatedDefaultDir);
|
|
103
|
+
// Add the init config option
|
|
104
|
+
retCommand = retCommand.option('--init-config', 'Generate initial configuration file and exit');
|
|
105
|
+
// Add the check config option
|
|
106
|
+
retCommand = retCommand.option('--check-config', 'Display resolved configuration with source tracking and exit');
|
|
107
|
+
// Add the config format option
|
|
108
|
+
retCommand = retCommand.option('--config-format <format>', 'Force a specific configuration file format (yaml, json, javascript, typescript)', (value)=>{
|
|
109
|
+
const validFormats = [
|
|
110
|
+
'yaml',
|
|
111
|
+
'json',
|
|
112
|
+
'javascript',
|
|
113
|
+
'typescript'
|
|
114
|
+
];
|
|
115
|
+
const normalized = value.toLowerCase();
|
|
116
|
+
if (!validFormats.includes(normalized)) {
|
|
117
|
+
throw new ArgumentError('config-format', `Invalid --config-format: must be one of ${validFormats.join(', ')}`);
|
|
118
|
+
}
|
|
119
|
+
return normalized;
|
|
120
|
+
});
|
|
121
|
+
return retCommand;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export { ArgumentError, configure, validateConfigDirectory };
|
|
125
|
+
//# sourceMappingURL=configure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.js","sources":["../src/configure.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { z } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { Options } from \"./types\";\nexport { ArgumentError };\n\n/**\n * Validates a configuration directory path to ensure it's safe and valid.\n * \n * Performs security and safety checks including:\n * - Non-empty string validation\n * - Null byte injection prevention\n * - Path length validation\n * - Type checking\n * \n * @param configDirectory - The configuration directory path to validate\n * @param _testThrowNonArgumentError - Internal testing parameter to simulate non-ArgumentError exceptions\n * @returns The trimmed and validated configuration directory path\n * @throws {ArgumentError} When the directory path is invalid\n * \n * @example\n * ```typescript\n * const validDir = validateConfigDirectory('./config'); // Returns './config'\n * const invalidDir = validateConfigDirectory(''); // Throws ArgumentError\n * ```\n */\nexport function validateConfigDirectory(configDirectory: string, _testThrowNonArgumentError?: boolean): string {\n if (_testThrowNonArgumentError) {\n throw new Error('Test non-ArgumentError for coverage');\n }\n\n if (!configDirectory) {\n throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty');\n }\n\n if (typeof configDirectory !== 'string') {\n throw new ArgumentError('configDirectory', 'Configuration directory must be a string');\n }\n\n const trimmed = configDirectory.trim();\n if (trimmed.length === 0) {\n throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty or whitespace only');\n }\n\n // Check for obviously invalid paths\n if (trimmed.includes('\\0')) {\n throw new ArgumentError('configDirectory', 'Configuration directory contains invalid null character');\n }\n\n // Validate path length (reasonable limit)\n if (trimmed.length > 1000) {\n throw new ArgumentError('configDirectory', 'Configuration directory path is too long (max 1000 characters)');\n }\n\n return trimmed;\n}\n\n/**\n * Configures a Commander.js command with Cardigantime's CLI options.\n * \n * This function adds command-line options that allow users to override\n * configuration settings at runtime, such as:\n * - --config-directory: Override the default configuration directory\n * \n * The function validates both the command object and the options to ensure\n * they meet the requirements for proper integration.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param command - The Commander.js Command instance to configure\n * @param options - Cardigantime options containing defaults and schema\n * @param _testThrowNonArgumentError - Internal testing parameter\n * @returns Promise resolving to the configured Command instance\n * @throws {ArgumentError} When command or options are invalid\n * \n * @example\n * ```typescript\n * import { Command } from 'commander';\n * import { configure } from './configure';\n * \n * const program = new Command();\n * const configuredProgram = await configure(program, options);\n * \n * // Now the program accepts: --config-directory <path>\n * ```\n */\nexport const configure = async <T extends z.ZodRawShape>(\n command: Command,\n options: Options<T>,\n _testThrowNonArgumentError?: boolean\n): Promise<Command> => {\n // Validate the command object\n if (!command) {\n throw new ArgumentError('command', 'Command instance is required');\n }\n\n if (typeof command.option !== 'function') {\n throw new ArgumentError('command', 'Command must be a valid Commander.js Command instance');\n }\n\n // Validate options\n if (!options) {\n throw new ArgumentError('options', 'Options object is required');\n }\n\n if (!options.defaults) {\n throw new ArgumentError('options.defaults', 'Options must include defaults configuration');\n }\n\n if (!options.defaults.configDirectory) {\n throw new ArgumentError('options.defaults.configDirectory', 'Default config directory is required');\n }\n\n // Validate the default config directory\n const validatedDefaultDir = validateConfigDirectory(options.defaults.configDirectory, _testThrowNonArgumentError);\n\n let retCommand = command;\n\n // Add the config directory option with validation\n retCommand = retCommand.option(\n '-c, --config-directory <configDirectory>',\n 'Configuration directory path',\n (value: string) => {\n try {\n return validateConfigDirectory(value, _testThrowNonArgumentError);\n } catch (error) {\n if (error instanceof ArgumentError) {\n // Re-throw with more specific context for CLI usage\n throw new ArgumentError('config-directory', `Invalid --config-directory: ${error.message}`);\n }\n throw error;\n }\n },\n validatedDefaultDir\n );\n\n // Add the init config option\n retCommand = retCommand.option(\n '--init-config',\n 'Generate initial configuration file and exit'\n );\n\n // Add the check config option\n retCommand = retCommand.option(\n '--check-config',\n 'Display resolved configuration with source tracking and exit'\n );\n\n // Add the config format option\n retCommand = retCommand.option(\n '--config-format <format>',\n 'Force a specific configuration file format (yaml, json, javascript, typescript)',\n (value: string) => {\n const validFormats = ['yaml', 'json', 'javascript', 'typescript'];\n const normalized = value.toLowerCase();\n if (!validFormats.includes(normalized)) {\n throw new ArgumentError(\n 'config-format',\n `Invalid --config-format: must be one of ${validFormats.join(', ')}`\n );\n }\n return normalized;\n }\n );\n\n return retCommand;\n}\n\n\n\n\n"],"names":["validateConfigDirectory","configDirectory","_testThrowNonArgumentError","ArgumentError","trimmed","trim","length","includes","configure","command","options","option","defaults","validatedDefaultDir","retCommand","value","error","message","validFormats","normalized","toLowerCase","join"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DefaultOptions, Feature, Logger } from './types';
|
|
2
|
+
/** Version string populated at build time with git and system information */
|
|
3
|
+
export declare const VERSION = "__VERSION__ (__GIT_BRANCH__/__GIT_COMMIT__ __GIT_TAGS__ __GIT_COMMIT_DATE__) __SYSTEM_INFO__";
|
|
4
|
+
/** The program name used in CLI help and error messages */
|
|
5
|
+
export declare const PROGRAM_NAME = "cardigantime";
|
|
6
|
+
/** Default file encoding for reading configuration files */
|
|
7
|
+
export declare const DEFAULT_ENCODING = "utf8";
|
|
8
|
+
/** Default configuration file name to look for in the config directory */
|
|
9
|
+
export declare const DEFAULT_CONFIG_FILE = "config.yaml";
|
|
10
|
+
/**
|
|
11
|
+
* Default configuration options applied when creating a Cardigantime instance.
|
|
12
|
+
* These provide sensible defaults that work for most use cases.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_OPTIONS: Partial<DefaultOptions>;
|
|
15
|
+
/**
|
|
16
|
+
* Default features enabled when creating a Cardigantime instance.
|
|
17
|
+
* Currently includes only the 'config' feature for configuration file support.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_FEATURES: Feature[];
|
|
20
|
+
/**
|
|
21
|
+
* Default logger implementation using console methods.
|
|
22
|
+
* Provides basic logging functionality when no custom logger is specified.
|
|
23
|
+
* The verbose and silly methods are no-ops to avoid excessive output.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DEFAULT_LOGGER: Logger;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Version string populated at build time with git and system information */ const VERSION = '0.0.24-dev.0 (working/2325c27 2026-01-30 13:16:50 -0800) darwin arm64 v24.8.0';
|
|
2
|
+
/** The program name used in CLI help and error messages */ const PROGRAM_NAME = 'cardigantime';
|
|
3
|
+
/** Default file encoding for reading configuration files */ const DEFAULT_ENCODING = 'utf8';
|
|
4
|
+
/** Default configuration file name to look for in the config directory */ const DEFAULT_CONFIG_FILE = 'config.yaml';
|
|
5
|
+
/**
|
|
6
|
+
* Default configuration options applied when creating a Cardigantime instance.
|
|
7
|
+
* These provide sensible defaults that work for most use cases.
|
|
8
|
+
*/ const DEFAULT_OPTIONS = {
|
|
9
|
+
configFile: DEFAULT_CONFIG_FILE,
|
|
10
|
+
isRequired: false,
|
|
11
|
+
encoding: DEFAULT_ENCODING,
|
|
12
|
+
pathResolution: undefined
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Default features enabled when creating a Cardigantime instance.
|
|
16
|
+
* Currently includes only the 'config' feature for configuration file support.
|
|
17
|
+
*/ const DEFAULT_FEATURES = [
|
|
18
|
+
'config'
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Default logger implementation using console methods.
|
|
22
|
+
* Provides basic logging functionality when no custom logger is specified.
|
|
23
|
+
* The verbose and silly methods are no-ops to avoid excessive output.
|
|
24
|
+
*/ const DEFAULT_LOGGER = {
|
|
25
|
+
// eslint-disable-next-line no-console
|
|
26
|
+
debug: console.debug,
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
info: console.info,
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
warn: console.warn,
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
error: console.error,
|
|
33
|
+
verbose: ()=>{},
|
|
34
|
+
silly: ()=>{}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { DEFAULT_CONFIG_FILE, DEFAULT_ENCODING, DEFAULT_FEATURES, DEFAULT_LOGGER, DEFAULT_OPTIONS, PROGRAM_NAME, VERSION };
|
|
38
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sources":["../src/constants.ts"],"sourcesContent":["import { DefaultOptions, Feature, Logger } from \"./types\";\n\n/** Version string populated at build time with git and system information */\nexport const VERSION = '__VERSION__ (__GIT_BRANCH__/__GIT_COMMIT__ __GIT_TAGS__ __GIT_COMMIT_DATE__) __SYSTEM_INFO__';\n\n/** The program name used in CLI help and error messages */\nexport const PROGRAM_NAME = 'cardigantime';\n\n/** Default file encoding for reading configuration files */\nexport const DEFAULT_ENCODING = 'utf8';\n\n/** Default configuration file name to look for in the config directory */\nexport const DEFAULT_CONFIG_FILE = 'config.yaml';\n\n/**\n * Default configuration options applied when creating a Cardigantime instance.\n * These provide sensible defaults that work for most use cases.\n */\nexport const DEFAULT_OPTIONS: Partial<DefaultOptions> = {\n configFile: DEFAULT_CONFIG_FILE,\n isRequired: false,\n encoding: DEFAULT_ENCODING,\n pathResolution: undefined, // No path resolution by default\n}\n\n/**\n * Default features enabled when creating a Cardigantime instance.\n * Currently includes only the 'config' feature for configuration file support.\n */\nexport const DEFAULT_FEATURES: Feature[] = ['config'];\n\n/**\n * Default logger implementation using console methods.\n * Provides basic logging functionality when no custom logger is specified.\n * The verbose and silly methods are no-ops to avoid excessive output.\n */\nexport const DEFAULT_LOGGER: Logger = {\n // eslint-disable-next-line no-console\n debug: console.debug,\n // eslint-disable-next-line no-console\n info: console.info,\n // eslint-disable-next-line no-console\n warn: console.warn,\n // eslint-disable-next-line no-console\n error: console.error,\n\n verbose: () => { },\n\n silly: () => { },\n}\n"],"names":["VERSION","PROGRAM_NAME","DEFAULT_ENCODING","DEFAULT_CONFIG_FILE","DEFAULT_OPTIONS","configFile","isRequired","encoding","pathResolution","undefined","DEFAULT_FEATURES","DEFAULT_LOGGER","debug","console","info","warn","error","verbose","silly"],"mappings":"AAEA,8EACO,MAAMA,OAAAA,GAAU;AAEvB,4DACO,MAAMC,YAAAA,GAAe;AAE5B,6DACO,MAAMC,gBAAAA,GAAmB;AAEhC,2EACO,MAAMC,mBAAAA,GAAsB;AAEnC;;;UAIaC,eAAAA,GAA2C;IACpDC,UAAAA,EAAYF,mBAAAA;IACZG,UAAAA,EAAY,KAAA;IACZC,QAAAA,EAAUL,gBAAAA;IACVM,cAAAA,EAAgBC;AACpB;AAEA;;;UAIaC,gBAAAA,GAA8B;AAAC,IAAA;;AAE5C;;;;UAKaC,cAAAA,GAAyB;;AAElCC,IAAAA,KAAAA,EAAOC,QAAQD,KAAK;;AAEpBE,IAAAA,IAAAA,EAAMD,QAAQC,IAAI;;AAElBC,IAAAA,IAAAA,EAAMF,QAAQE,IAAI;;AAElBC,IAAAA,KAAAA,EAAOH,QAAQG,KAAK;AAEpBC,IAAAA,OAAAA,EAAS,IAAA,CAAQ,CAAA;AAEjBC,IAAAA,KAAAA,EAAO,IAAA,CAAQ;AACnB;;;;"}
|