@theunwalked/cardigantime 0.0.21 → 0.0.23
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 +197 -4
- package/dist/cardigantime.cjs +107 -19
- package/dist/cardigantime.cjs.map +1 -1
- package/dist/cardigantime.d.ts +24 -0
- package/dist/cardigantime.js +28 -3
- package/dist/cardigantime.js.map +1 -1
- package/dist/config/executable-security.d.ts +32 -0
- package/dist/config/format-detector.d.ts +59 -0
- package/dist/configure.js +14 -0
- package/dist/configure.js.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- 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/error/ConfigParseError.d.ts +26 -0
- package/dist/error/index.d.ts +1 -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 +127 -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 +171 -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.js +1 -1
- package/dist/read.js.map +1 -1
- 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 +431 -0
- package/dist/types.js +44 -1
- package/dist/types.js.map +1 -1
- package/dist/util/hierarchical.js +14 -14
- package/dist/util/hierarchical.js.map +1 -1
- package/dist/util/storage.d.ts +1 -1
- package/dist/util/storage.js +4 -5
- package/dist/util/storage.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ A robust TypeScript library for configuration management in command-line applica
|
|
|
6
6
|
|
|
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
|
-
- **Read configuration from
|
|
9
|
+
- **Read configuration from multiple formats** (YAML, JSON, JavaScript, TypeScript) with intelligent file discovery
|
|
10
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
|
|
@@ -24,6 +24,35 @@ Without Cardigantime, you need to manually handle:
|
|
|
24
24
|
|
|
25
25
|
Cardigantime provides a complete, battle-tested solution for all of this complexity.
|
|
26
26
|
|
|
27
|
+
### One Schema, Multiple Formats
|
|
28
|
+
|
|
29
|
+
Define your configuration schema once with Zod, and Cardigantime automatically supports:
|
|
30
|
+
- **YAML** (`.yaml`, `.yml`) - Human-readable, great for hand-edited configs
|
|
31
|
+
- **JSON** (`.json`) - Strict syntax, ideal for programmatic generation
|
|
32
|
+
- **JavaScript** (`.js`, `.mjs`, `.cjs`) - Dynamic configs with environment logic
|
|
33
|
+
- **TypeScript** (`.ts`, `.mts`, `.cts`) - Type-safe configs with IDE support
|
|
34
|
+
|
|
35
|
+
No additional code or schema definitions needed per format. Your users choose their preferred format, and Cardigantime handles parsing, validation, and merging automatically.
|
|
36
|
+
|
|
37
|
+
## Who Uses Cardigantime?
|
|
38
|
+
|
|
39
|
+
Cardigantime serves two distinct audiences:
|
|
40
|
+
|
|
41
|
+
### Tool Developers
|
|
42
|
+
Developers building CLI applications who want robust configuration management without the boilerplate. You integrate Cardigantime once, define your Zod schema, and get:
|
|
43
|
+
- Multi-format config file support out of the box
|
|
44
|
+
- Automatic CLI option generation
|
|
45
|
+
- Deep merging with proper precedence
|
|
46
|
+
- Hierarchical config discovery (like `.eslintrc` or `.gitignore`)
|
|
47
|
+
- Type safety throughout your codebase
|
|
48
|
+
|
|
49
|
+
### End Users
|
|
50
|
+
Users of tools built with Cardigantime benefit from a consistent, powerful configuration experience without needing to know Cardigantime exists. They get:
|
|
51
|
+
- Freedom to use their preferred config format (YAML, JSON, JS, or TS)
|
|
52
|
+
- Consistent CLI options across tools
|
|
53
|
+
- Clear, actionable error messages
|
|
54
|
+
- Built-in config generation (`--init-config`) and analysis (`--check-config`)
|
|
55
|
+
|
|
27
56
|
## Installation
|
|
28
57
|
|
|
29
58
|
```bash
|
|
@@ -102,8 +131,84 @@ async function main() {
|
|
|
102
131
|
main().catch(console.error);
|
|
103
132
|
```
|
|
104
133
|
|
|
105
|
-
###
|
|
134
|
+
### Version Information with Git Details
|
|
135
|
+
|
|
136
|
+
Cardigantime exports its own version information including git commit details. You can also set up the same pattern in your own project.
|
|
137
|
+
|
|
138
|
+
#### Using Cardigantime's Version
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { VERSION, PROGRAM_NAME } from '@theunwalked/cardigantime';
|
|
142
|
+
|
|
143
|
+
console.log(`Using ${PROGRAM_NAME}: ${VERSION}`);
|
|
144
|
+
// Output: Using cardigantime: 0.0.22-dev.0 (working/a1b2c3d 2026-01-27 11:11:46 -0800) darwin arm64 v24.8.0
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Setting Up Version Info in Your Own Project
|
|
148
|
+
|
|
149
|
+
To add the same detailed version format to your own CLI application, add this to your `vite.config.ts`:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { defineConfig } from 'vite';
|
|
153
|
+
import replace from '@rollup/plugin-replace';
|
|
154
|
+
import { execSync } from 'node:child_process';
|
|
155
|
+
|
|
156
|
+
// Extract git information at build time
|
|
157
|
+
let gitInfo = {
|
|
158
|
+
branch: '',
|
|
159
|
+
commit: '',
|
|
160
|
+
tags: '',
|
|
161
|
+
commitDate: '',
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
gitInfo = {
|
|
166
|
+
branch: execSync('git rev-parse --abbrev-ref HEAD').toString().trim(),
|
|
167
|
+
commit: execSync('git rev-parse --short HEAD').toString().trim(),
|
|
168
|
+
tags: '',
|
|
169
|
+
commitDate: execSync('git log -1 --format=%cd --date=iso').toString().trim(),
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
gitInfo.tags = execSync('git tag --points-at HEAD | paste -sd "," -').toString().trim();
|
|
174
|
+
} catch {
|
|
175
|
+
gitInfo.tags = '';
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
console.log('Directory does not have a Git repository, skipping git info');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export default defineConfig({
|
|
182
|
+
plugins: [
|
|
183
|
+
replace({
|
|
184
|
+
'__VERSION__': process.env.npm_package_version,
|
|
185
|
+
'__GIT_BRANCH__': gitInfo.branch,
|
|
186
|
+
'__GIT_COMMIT__': gitInfo.commit,
|
|
187
|
+
'__GIT_TAGS__': gitInfo.tags === '' ? '' : `T:${gitInfo.tags}`,
|
|
188
|
+
'__GIT_COMMIT_DATE__': gitInfo.commitDate,
|
|
189
|
+
'__SYSTEM_INFO__': `${process.platform} ${process.arch} ${process.version}`,
|
|
190
|
+
preventAssignment: true,
|
|
191
|
+
}),
|
|
192
|
+
// ... your other plugins
|
|
193
|
+
],
|
|
194
|
+
// ... rest of your config
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Then in your constants file:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
export const VERSION = '__VERSION__ (__GIT_BRANCH__/__GIT_COMMIT__ __GIT_TAGS__ __GIT_COMMIT_DATE__) __SYSTEM_INFO__';
|
|
202
|
+
export const PROGRAM_NAME = 'myapp';
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The placeholders will be replaced at build time with actual values. This is particularly useful for CLI applications where you need visibility into exactly which build is running.
|
|
206
|
+
|
|
207
|
+
### Configuration File Examples
|
|
106
208
|
|
|
209
|
+
Cardigantime supports multiple configuration formats. Choose the one that best fits your workflow:
|
|
210
|
+
|
|
211
|
+
#### YAML (`config/myapp.yaml`)
|
|
107
212
|
```yaml
|
|
108
213
|
apiKey: "your-secret-api-key"
|
|
109
214
|
timeout: 10000
|
|
@@ -111,6 +216,47 @@ retries: 5
|
|
|
111
216
|
debug: true
|
|
112
217
|
```
|
|
113
218
|
|
|
219
|
+
#### JSON (`config/myapp.json`)
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"apiKey": "your-secret-api-key",
|
|
223
|
+
"timeout": 10000,
|
|
224
|
+
"retries": 5,
|
|
225
|
+
"debug": true
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### JavaScript (`config/myapp.js`)
|
|
230
|
+
```javascript
|
|
231
|
+
module.exports = {
|
|
232
|
+
apiKey: process.env.API_KEY || "your-secret-api-key",
|
|
233
|
+
timeout: 10000,
|
|
234
|
+
retries: 5,
|
|
235
|
+
debug: process.env.NODE_ENV === 'development'
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### TypeScript (`config/myapp.ts`)
|
|
240
|
+
```typescript
|
|
241
|
+
export default {
|
|
242
|
+
apiKey: process.env.API_KEY || "your-secret-api-key",
|
|
243
|
+
timeout: 10000,
|
|
244
|
+
retries: 5,
|
|
245
|
+
debug: process.env.NODE_ENV === 'development'
|
|
246
|
+
} as const;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Format Priority:** When multiple config files exist, Cardigantime uses this priority order:
|
|
250
|
+
1. TypeScript (`.ts`, `.mts`, `.cts`) - Highest priority
|
|
251
|
+
2. JavaScript (`.js`, `.mjs`, `.cjs`)
|
|
252
|
+
3. JSON (`.json`)
|
|
253
|
+
4. YAML (`.yaml`, `.yml`) - Lowest priority
|
|
254
|
+
|
|
255
|
+
You can override automatic detection with `--config-format`:
|
|
256
|
+
```bash
|
|
257
|
+
./myapp --config-format yaml # Force YAML even if JSON exists
|
|
258
|
+
```
|
|
259
|
+
|
|
114
260
|
### Example Usage
|
|
115
261
|
|
|
116
262
|
```bash
|
|
@@ -138,12 +284,57 @@ Merges configuration from multiple sources in order of precedence:
|
|
|
138
284
|
2. **Configuration file(s)** (medium priority)
|
|
139
285
|
3. **Default values** (lowest priority)
|
|
140
286
|
|
|
141
|
-
###
|
|
142
|
-
Supports
|
|
287
|
+
### Multi-Format Configuration
|
|
288
|
+
Supports YAML (`.yaml`, `.yml`), JSON (`.json`), JavaScript (`.js`, `.mjs`, `.cjs`), and TypeScript (`.ts`, `.mts`, `.cts`) configuration files. When multiple formats exist, Cardigantime uses automatic format detection with configurable priority.
|
|
289
|
+
|
|
290
|
+
### Configuration Discovery
|
|
291
|
+
|
|
292
|
+
Cardigantime automatically searches for configuration files using multiple naming conventions, similar to how tools like Vite, ESLint, and TypeScript work:
|
|
293
|
+
|
|
294
|
+
| Priority | Pattern | Example |
|
|
295
|
+
|----------|---------|---------|
|
|
296
|
+
| 1 | `{app}.config.{ext}` | `myapp.config.yaml` |
|
|
297
|
+
| 2 | `{app}.conf.{ext}` | `myapp.conf.yaml` |
|
|
298
|
+
| 3 | `.{app}/config.{ext}` | `.myapp/config.yaml` |
|
|
299
|
+
| 4 | `.{app}rc.{ext}` | `.myapprc.yaml` |
|
|
300
|
+
| 5 | `.{app}rc` | `.myapprc` |
|
|
301
|
+
|
|
302
|
+
Modern visible config files (like `myapp.config.yaml`) are checked first for better discoverability.
|
|
143
303
|
|
|
144
304
|
### Hierarchical Configuration Discovery
|
|
145
305
|
Supports hierarchical configuration discovery, similar to how `.gitignore`, `.eslintrc`, or `package.json` work - searching up the directory tree for configuration directories.
|
|
146
306
|
|
|
307
|
+
**Hierarchical Modes:**
|
|
308
|
+
- `enabled` (default) - Merge configs from parent directories
|
|
309
|
+
- `disabled` - Use only the config in the starting directory
|
|
310
|
+
- `root-only` - Find first config, no merging
|
|
311
|
+
- `explicit` - Only merge explicitly referenced configs
|
|
312
|
+
|
|
313
|
+
```yaml
|
|
314
|
+
# Disable hierarchical for isolated projects
|
|
315
|
+
hierarchical:
|
|
316
|
+
mode: disabled
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### MCP Integration
|
|
320
|
+
First-class support for Model Context Protocol (MCP), enabling AI assistants to configure tools directly through MCP invocations. Includes:
|
|
321
|
+
- **MCP Configuration Priority** - MCP config takes exclusive precedence when provided
|
|
322
|
+
- **File-Based Fallback** - Automatic discovery from target file or working directory
|
|
323
|
+
- **CheckConfig Tool** - Built-in diagnostic tool for all MCP tools
|
|
324
|
+
- **Integration Helpers** - Simple APIs for adding MCP support to your tools
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { createMCPIntegration } from '@theunwalked/cardigantime/mcp';
|
|
328
|
+
|
|
329
|
+
const integration = createMCPIntegration({
|
|
330
|
+
appName: 'myapp',
|
|
331
|
+
configSchema: myConfigSchema,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// CheckConfig tool is automatically available
|
|
335
|
+
// Config resolution handles MCP and file-based sources
|
|
336
|
+
```
|
|
337
|
+
|
|
147
338
|
### Type Safety & Validation
|
|
148
339
|
Full TypeScript support with Zod schema validation for robust, type-safe configuration management.
|
|
149
340
|
|
|
@@ -157,6 +348,8 @@ Comprehensive error handling with detailed, actionable error messages to help us
|
|
|
157
348
|
**Quick Links:**
|
|
158
349
|
- [Getting Started Guide](https://utilarium.github.io/cardigantime/#getting-started) - Detailed setup and basic concepts
|
|
159
350
|
- [Core Concepts](https://utilarium.github.io/cardigantime/#core-concepts) - Configuration sources, hierarchical discovery
|
|
351
|
+
- [MCP Integration](https://utilarium.github.io/cardigantime/#mcp-integration) - Model Context Protocol support for AI assistants
|
|
352
|
+
- [CheckConfig Tool](https://utilarium.github.io/cardigantime/#check-config-tool) - Built-in diagnostic tool
|
|
160
353
|
- [API Reference](https://utilarium.github.io/cardigantime/#api-reference) - Complete API documentation
|
|
161
354
|
- [Configuration Options](https://utilarium.github.io/cardigantime/#configuration-options) - All available options
|
|
162
355
|
- [Debugging & Analysis](https://utilarium.github.io/cardigantime/#debugging-analysis) - Tools for analyzing config
|
package/dist/cardigantime.cjs
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
4
|
|
|
5
5
|
const yaml = require('js-yaml');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const fs = require('fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const fs = require('node:fs');
|
|
8
8
|
const glob = require('glob');
|
|
9
|
-
const crypto = require('crypto');
|
|
9
|
+
const crypto = require('node:crypto');
|
|
10
10
|
const zod = require('zod');
|
|
11
11
|
|
|
12
12
|
function _interopNamespaceDefault(e) {
|
|
@@ -29,6 +29,7 @@ function _interopNamespaceDefault(e) {
|
|
|
29
29
|
const yaml__namespace = /*#__PURE__*/_interopNamespaceDefault(yaml);
|
|
30
30
|
const path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
31
31
|
const fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
|
|
32
|
+
const crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
|
|
32
33
|
|
|
33
34
|
function _define_property$2(obj, key, value) {
|
|
34
35
|
if (key in obj) {
|
|
@@ -180,9 +181,25 @@ function _define_property$2(obj, key, value) {
|
|
|
180
181
|
retCommand = retCommand.option('--init-config', 'Generate initial configuration file and exit');
|
|
181
182
|
// Add the check config option
|
|
182
183
|
retCommand = retCommand.option('--check-config', 'Display resolved configuration with source tracking and exit');
|
|
184
|
+
// Add the config format option
|
|
185
|
+
retCommand = retCommand.option('--config-format <format>', 'Force a specific configuration file format (yaml, json, javascript, typescript)', (value)=>{
|
|
186
|
+
const validFormats = [
|
|
187
|
+
'yaml',
|
|
188
|
+
'json',
|
|
189
|
+
'javascript',
|
|
190
|
+
'typescript'
|
|
191
|
+
];
|
|
192
|
+
const normalized = value.toLowerCase();
|
|
193
|
+
if (!validFormats.includes(normalized)) {
|
|
194
|
+
throw new ArgumentError('config-format', `Invalid --config-format: must be one of ${validFormats.join(', ')}`);
|
|
195
|
+
}
|
|
196
|
+
return normalized;
|
|
197
|
+
});
|
|
183
198
|
return retCommand;
|
|
184
199
|
};
|
|
185
200
|
|
|
201
|
+
/** Version string populated at build time with git and system information */ const VERSION = '0.0.23 (HEAD/b3ed796 T:v0.0.23 2026-01-27 14:30:06 -0800) linux x64 v24.13.0';
|
|
202
|
+
/** The program name used in CLI help and error messages */ const PROGRAM_NAME = 'cardigantime';
|
|
186
203
|
/** Default file encoding for reading configuration files */ const DEFAULT_ENCODING = 'utf8';
|
|
187
204
|
/** Default configuration file name to look for in the config directory */ const DEFAULT_CONFIG_FILE = 'config.yaml';
|
|
188
205
|
/**
|
|
@@ -273,7 +290,6 @@ function _define_property$1(obj, key, value) {
|
|
|
273
290
|
}
|
|
274
291
|
}
|
|
275
292
|
|
|
276
|
-
// eslint-disable-next-line no-restricted-imports
|
|
277
293
|
const create$1 = (params)=>{
|
|
278
294
|
// eslint-disable-next-line no-console
|
|
279
295
|
const log = params.log || console.log;
|
|
@@ -385,7 +401,7 @@ const create$1 = (params)=>{
|
|
|
385
401
|
nodir: true
|
|
386
402
|
});
|
|
387
403
|
for (const file of files){
|
|
388
|
-
await callback(
|
|
404
|
+
await callback(path__namespace.join(directory, file));
|
|
389
405
|
}
|
|
390
406
|
} catch (err) {
|
|
391
407
|
throw FileSystemError.operationFailed(`glob pattern ${options.pattern}`, directory, err);
|
|
@@ -396,7 +412,7 @@ const create$1 = (params)=>{
|
|
|
396
412
|
};
|
|
397
413
|
const hashFile = async (path, length)=>{
|
|
398
414
|
const file = await readFile(path, 'utf8');
|
|
399
|
-
return
|
|
415
|
+
return crypto__namespace.createHash('sha256').update(file).digest('hex').slice(0, length);
|
|
400
416
|
};
|
|
401
417
|
const listFiles = async (directory)=>{
|
|
402
418
|
return await fs__namespace.promises.readdir(directory);
|
|
@@ -482,10 +498,10 @@ function setNestedValue$1(obj, path, value) {
|
|
|
482
498
|
/**
|
|
483
499
|
* Resolves a single path string relative to the config directory if it's a relative path.
|
|
484
500
|
*/ function resolveSinglePath$1(pathStr, configDir) {
|
|
485
|
-
if (!pathStr ||
|
|
501
|
+
if (!pathStr || path__namespace.isAbsolute(pathStr)) {
|
|
486
502
|
return pathStr;
|
|
487
503
|
}
|
|
488
|
-
return
|
|
504
|
+
return path__namespace.resolve(configDir, pathStr);
|
|
489
505
|
}
|
|
490
506
|
/**
|
|
491
507
|
* Discovers configuration directories by traversing up the directory tree.
|
|
@@ -516,19 +532,19 @@ function setNestedValue$1(obj, path, value) {
|
|
|
516
532
|
log: (logger === null || logger === void 0 ? void 0 : logger.debug) || (()=>{})
|
|
517
533
|
});
|
|
518
534
|
const discoveredDirs = [];
|
|
519
|
-
let currentDir =
|
|
535
|
+
let currentDir = path__namespace.resolve(startingDir);
|
|
520
536
|
let level = 0;
|
|
521
537
|
const visited = new Set(); // Prevent infinite loops with symlinks
|
|
522
538
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Starting hierarchical discovery from: ${currentDir}`);
|
|
523
539
|
while(level < maxLevels){
|
|
524
540
|
// Prevent infinite loops with symlinks
|
|
525
|
-
const realPath =
|
|
541
|
+
const realPath = path__namespace.resolve(currentDir);
|
|
526
542
|
if (visited.has(realPath)) {
|
|
527
543
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Already visited ${realPath}, stopping discovery`);
|
|
528
544
|
break;
|
|
529
545
|
}
|
|
530
546
|
visited.add(realPath);
|
|
531
|
-
const configDirPath =
|
|
547
|
+
const configDirPath = path__namespace.join(currentDir, configDirName);
|
|
532
548
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Checking for config directory: ${configDirPath}`);
|
|
533
549
|
try {
|
|
534
550
|
const exists = await storage.exists(configDirPath);
|
|
@@ -546,7 +562,7 @@ function setNestedValue$1(obj, path, value) {
|
|
|
546
562
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Error checking config directory ${configDirPath}: ${error.message}`);
|
|
547
563
|
}
|
|
548
564
|
// Move up one directory level
|
|
549
|
-
const parentDir =
|
|
565
|
+
const parentDir = path__namespace.dirname(currentDir);
|
|
550
566
|
// Check if we've reached the root directory
|
|
551
567
|
if (parentDir === currentDir) {
|
|
552
568
|
logger === null || logger === void 0 ? void 0 : logger.debug('Reached filesystem root, stopping discovery');
|
|
@@ -567,7 +583,7 @@ function setNestedValue$1(obj, path, value) {
|
|
|
567
583
|
* @param logger Optional logger for debugging
|
|
568
584
|
* @returns Promise resolving to the found config file path or null if not found
|
|
569
585
|
*/ async function findConfigFileWithExtension$1(storage, configDir, configFileName, logger) {
|
|
570
|
-
const configFilePath =
|
|
586
|
+
const configFilePath = path__namespace.join(configDir, configFileName);
|
|
571
587
|
// First try the exact filename as specified
|
|
572
588
|
const exists = await storage.exists(configFilePath);
|
|
573
589
|
if (exists) {
|
|
@@ -578,11 +594,11 @@ function setNestedValue$1(obj, path, value) {
|
|
|
578
594
|
}
|
|
579
595
|
// If the exact filename doesn't exist or isn't readable, try alternative extensions
|
|
580
596
|
// Only do this if the filename has a .yaml or .yml extension
|
|
581
|
-
const ext =
|
|
597
|
+
const ext = path__namespace.extname(configFileName);
|
|
582
598
|
if (ext === '.yaml' || ext === '.yml') {
|
|
583
|
-
const baseName =
|
|
599
|
+
const baseName = path__namespace.basename(configFileName, ext);
|
|
584
600
|
const alternativeExt = ext === '.yaml' ? '.yml' : '.yaml';
|
|
585
|
-
const alternativePath =
|
|
601
|
+
const alternativePath = path__namespace.join(configDir, baseName + alternativeExt);
|
|
586
602
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file not found at ${configFilePath}, trying alternative: ${alternativePath}`);
|
|
587
603
|
const altExists = await storage.exists(alternativePath);
|
|
588
604
|
if (altExists) {
|
|
@@ -610,11 +626,11 @@ function setNestedValue$1(obj, path, value) {
|
|
|
610
626
|
log: (logger === null || logger === void 0 ? void 0 : logger.debug) || (()=>{})
|
|
611
627
|
});
|
|
612
628
|
try {
|
|
613
|
-
logger === null || logger === void 0 ? void 0 : logger.verbose(`Attempting to load config file: ${
|
|
629
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Attempting to load config file: ${path__namespace.join(configDir, configFileName)}`);
|
|
614
630
|
// Try to find the config file with alternative extensions
|
|
615
631
|
const configFilePath = await findConfigFileWithExtension$1(storage, configDir, configFileName, logger);
|
|
616
632
|
if (!configFilePath) {
|
|
617
|
-
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file does not exist: ${
|
|
633
|
+
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file does not exist: ${path__namespace.join(configDir, configFileName)}`);
|
|
618
634
|
return null;
|
|
619
635
|
}
|
|
620
636
|
const yamlContent = await storage.readFile(configFilePath, encoding);
|
|
@@ -632,7 +648,7 @@ function setNestedValue$1(obj, path, value) {
|
|
|
632
648
|
return null;
|
|
633
649
|
}
|
|
634
650
|
} catch (error) {
|
|
635
|
-
logger === null || logger === void 0 ? void 0 : logger.debug(`Error loading config from ${
|
|
651
|
+
logger === null || logger === void 0 ? void 0 : logger.debug(`Error loading config from ${path__namespace.join(configDir, configFileName)}: ${error.message}`);
|
|
636
652
|
return null;
|
|
637
653
|
}
|
|
638
654
|
}
|
|
@@ -1540,6 +1556,20 @@ function _define_property(obj, key, value) {
|
|
|
1540
1556
|
}
|
|
1541
1557
|
}
|
|
1542
1558
|
|
|
1559
|
+
/**
|
|
1560
|
+
* Supported configuration file formats.
|
|
1561
|
+
*
|
|
1562
|
+
* - 'yaml': YAML format (.yaml, .yml)
|
|
1563
|
+
* - 'json': JSON format (.json)
|
|
1564
|
+
* - 'javascript': JavaScript module (.js, .mjs, .cjs)
|
|
1565
|
+
* - 'typescript': TypeScript module (.ts, .mts, .cts)
|
|
1566
|
+
*/ var ConfigFormat = /*#__PURE__*/ function(ConfigFormat) {
|
|
1567
|
+
ConfigFormat["YAML"] = "yaml";
|
|
1568
|
+
ConfigFormat["JSON"] = "json";
|
|
1569
|
+
ConfigFormat["JavaScript"] = "javascript";
|
|
1570
|
+
ConfigFormat["TypeScript"] = "typescript";
|
|
1571
|
+
return ConfigFormat;
|
|
1572
|
+
}({});
|
|
1543
1573
|
/**
|
|
1544
1574
|
* Base Zod schema for core Cardigantime configuration.
|
|
1545
1575
|
* Contains the minimum required configuration fields.
|
|
@@ -1548,6 +1578,35 @@ function _define_property(obj, key, value) {
|
|
|
1548
1578
|
/** Array of all directory paths that were discovered during hierarchical search */ discoveredConfigDirs: zod.z.array(zod.z.string()),
|
|
1549
1579
|
/** Array of directory paths that actually contained valid configuration files */ resolvedConfigDirs: zod.z.array(zod.z.string())
|
|
1550
1580
|
});
|
|
1581
|
+
/**
|
|
1582
|
+
* Default root markers used when none are specified.
|
|
1583
|
+
* These indicate common project root boundaries.
|
|
1584
|
+
*/ const DEFAULT_ROOT_MARKERS = [
|
|
1585
|
+
{
|
|
1586
|
+
type: 'file',
|
|
1587
|
+
name: 'package.json'
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
type: 'directory',
|
|
1591
|
+
name: '.git'
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
type: 'file',
|
|
1595
|
+
name: 'pnpm-workspace.yaml'
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
type: 'file',
|
|
1599
|
+
name: 'lerna.json'
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
type: 'file',
|
|
1603
|
+
name: 'nx.json'
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
type: 'file',
|
|
1607
|
+
name: 'rush.json'
|
|
1608
|
+
}
|
|
1609
|
+
];
|
|
1551
1610
|
|
|
1552
1611
|
/**
|
|
1553
1612
|
* Recursively extracts all keys from a Zod schema in dot notation.
|
|
@@ -2072,10 +2131,39 @@ function _define_property(obj, key, value) {
|
|
|
2072
2131
|
checkConfig: (args)=>checkConfig(args, options)
|
|
2073
2132
|
};
|
|
2074
2133
|
};
|
|
2134
|
+
/**
|
|
2135
|
+
* Type-safe helper for defining configuration in TypeScript/JavaScript files.
|
|
2136
|
+
*
|
|
2137
|
+
* This is a simple identity function that provides type checking and
|
|
2138
|
+
* autocomplete for configuration objects when using TypeScript config files.
|
|
2139
|
+
*
|
|
2140
|
+
* @template T - The configuration type
|
|
2141
|
+
* @param config - The configuration object
|
|
2142
|
+
* @returns The same configuration object (identity function)
|
|
2143
|
+
*
|
|
2144
|
+
* @example
|
|
2145
|
+
* ```typescript
|
|
2146
|
+
* // config.ts
|
|
2147
|
+
* import { defineConfig } from '@theunwalked/cardigantime';
|
|
2148
|
+
*
|
|
2149
|
+
* export default defineConfig({
|
|
2150
|
+
* apiKey: process.env.API_KEY || 'default-key',
|
|
2151
|
+
* timeout: 5000,
|
|
2152
|
+
* debug: process.env.NODE_ENV === 'development'
|
|
2153
|
+
* });
|
|
2154
|
+
* ```
|
|
2155
|
+
*/ function defineConfig(config) {
|
|
2156
|
+
return config;
|
|
2157
|
+
}
|
|
2075
2158
|
|
|
2076
2159
|
exports.ArgumentError = ArgumentError;
|
|
2160
|
+
exports.ConfigFormat = ConfigFormat;
|
|
2077
2161
|
exports.ConfigSchema = ConfigSchema;
|
|
2078
2162
|
exports.ConfigurationError = ConfigurationError;
|
|
2163
|
+
exports.DEFAULT_ROOT_MARKERS = DEFAULT_ROOT_MARKERS;
|
|
2079
2164
|
exports.FileSystemError = FileSystemError;
|
|
2165
|
+
exports.PROGRAM_NAME = PROGRAM_NAME;
|
|
2166
|
+
exports.VERSION = VERSION;
|
|
2080
2167
|
exports.create = create;
|
|
2168
|
+
exports.defineConfig = defineConfig;
|
|
2081
2169
|
//# sourceMappingURL=cardigantime.cjs.map
|