@kb-labs/core-config 1.0.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/README.md +338 -0
- package/dist/chunk-A2WE2JZW.js +80 -0
- package/dist/chunk-A2WE2JZW.js.map +1 -0
- package/dist/fs-atomic-EKSZ6G5A.js +3 -0
- package/dist/fs-atomic-EKSZ6G5A.js.map +1 -0
- package/dist/index.d.ts +1016 -0
- package/dist/index.js +1534 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# @kb-labs/core-config
|
|
2
|
+
|
|
3
|
+
> **Core configuration system for KB Labs products with 6-layer merging, YAML/JSON support, and comprehensive tracing.** Provides deterministic configuration resolution with detailed merge traces for debugging.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://pnpm.io/)
|
|
8
|
+
|
|
9
|
+
## 🎯 Vision & Purpose
|
|
10
|
+
|
|
11
|
+
**@kb-labs/core-config** is the foundation of KB Labs configuration system. It implements a sophisticated 6-layer configuration merging strategy that allows products to have defaults, profile overrides, preset configurations, workspace settings, local overrides, and CLI arguments - all merged deterministically with full traceability.
|
|
12
|
+
|
|
13
|
+
## 🏗️ Architecture
|
|
14
|
+
|
|
15
|
+
### Core Components
|
|
16
|
+
|
|
17
|
+
#### `getProductConfig()` - Main Configuration Resolver
|
|
18
|
+
|
|
19
|
+
- **Purpose**: Resolves product configuration by merging 6 layers
|
|
20
|
+
- **Responsibilities**:
|
|
21
|
+
- Find workspace root
|
|
22
|
+
- Load workspace config
|
|
23
|
+
- Resolve profile defaults
|
|
24
|
+
- Load preset defaults
|
|
25
|
+
- Load local config
|
|
26
|
+
- Merge with CLI overrides
|
|
27
|
+
- Return merged config with trace
|
|
28
|
+
- **Dependencies**: `core-types`, `yaml`, `zod`, `ajv`
|
|
29
|
+
- **Exports**: `ProductConfigResult<T>` with config and trace
|
|
30
|
+
|
|
31
|
+
#### `layeredMergeWithTrace()` - Merge Engine
|
|
32
|
+
|
|
33
|
+
- **Purpose**: Merges configuration layers with detailed tracing
|
|
34
|
+
- **Responsibilities**:
|
|
35
|
+
- Deep merge objects
|
|
36
|
+
- Track merge operations
|
|
37
|
+
- Generate merge trace
|
|
38
|
+
- **Dependencies**: None (pure function)
|
|
39
|
+
- **Exports**: `LayeredMergeResult` with merged config and trace
|
|
40
|
+
|
|
41
|
+
#### Profile Resolution System
|
|
42
|
+
|
|
43
|
+
- **Purpose**: Resolves Profiles v2 with scope selection
|
|
44
|
+
- **Responsibilities**:
|
|
45
|
+
- Load profile manifests
|
|
46
|
+
- Resolve profile extends
|
|
47
|
+
- Select scopes
|
|
48
|
+
- Extract profile defaults
|
|
49
|
+
- **Dependencies**: `zod` for validation
|
|
50
|
+
- **Exports**: `resolveProfile()`, `selectProfileScope()`
|
|
51
|
+
|
|
52
|
+
### Design Patterns
|
|
53
|
+
|
|
54
|
+
- **Layered Architecture**: 6-layer configuration system
|
|
55
|
+
- **Strategy Pattern**: Different merge strategies for different layer types
|
|
56
|
+
- **Cache Pattern**: LRU cache for file system reads
|
|
57
|
+
- **Builder Pattern**: Configuration built layer by layer
|
|
58
|
+
|
|
59
|
+
### Data Flow
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
getProductConfig({ product, ... })
|
|
63
|
+
│
|
|
64
|
+
├──► findNearestConfig() → workspace root
|
|
65
|
+
├──► readWorkspaceConfig() → workspace config
|
|
66
|
+
├──► resolveProfile() → profile defaults
|
|
67
|
+
├──► resolvePreset() → preset defaults
|
|
68
|
+
├──► readLocalConfig() → local config
|
|
69
|
+
├──► layeredMergeWithTrace() → merged config + trace
|
|
70
|
+
└──► return { config, trace }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### State Management
|
|
74
|
+
|
|
75
|
+
- **State Type**: Local (per function call) + cached (LRU)
|
|
76
|
+
- **State Storage**: Memory (LRU cache for file reads)
|
|
77
|
+
- **State Lifecycle**: Created per call, cached for performance
|
|
78
|
+
- **State Persistence**: No persistence (stateless API)
|
|
79
|
+
|
|
80
|
+
### Concurrency Model
|
|
81
|
+
|
|
82
|
+
- **Single-threaded**: All operations are async but single-threaded
|
|
83
|
+
- **Thread Safety**: N/A (Node.js single-threaded event loop)
|
|
84
|
+
- **Race Conditions**: None (stateless API)
|
|
85
|
+
- **Deadlocks**: None
|
|
86
|
+
|
|
87
|
+
### Error Handling Strategy
|
|
88
|
+
|
|
89
|
+
- **Error Types**: `KbError` with error codes and hints
|
|
90
|
+
- **Error Propagation**: Errors thrown and caught by caller
|
|
91
|
+
- **Error Recovery**: No automatic recovery (caller must handle)
|
|
92
|
+
- **Error Logging**: Diagnostics array for non-fatal issues
|
|
93
|
+
|
|
94
|
+
## 🚀 Quick Start
|
|
95
|
+
|
|
96
|
+
### Installation
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pnpm add @kb-labs/core-config
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Basic Usage
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { getProductConfig } from '@kb-labs/core-config';
|
|
106
|
+
|
|
107
|
+
const result = await getProductConfig({
|
|
108
|
+
cwd: process.cwd(),
|
|
109
|
+
product: 'aiReview',
|
|
110
|
+
cli: { debug: true }
|
|
111
|
+
}, schema);
|
|
112
|
+
|
|
113
|
+
console.log(result.config); // Merged configuration
|
|
114
|
+
console.log(result.trace); // Detailed merge trace
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## ✨ Features
|
|
118
|
+
|
|
119
|
+
- **YAML/JSON Support**: Automatic format detection and parsing
|
|
120
|
+
- **Find-up Resolution**: Walks up directory tree to find config files
|
|
121
|
+
- **Layered Merge**: Deterministic configuration merging with detailed trace
|
|
122
|
+
- **Product Normalization**: Consistent kebab-case (FS) ↔ camelCase (code) mapping
|
|
123
|
+
- **LRU Caching**: Filesystem read caching with invalidation
|
|
124
|
+
- **Error Handling**: Standardized errors with helpful hints
|
|
125
|
+
- **Schema Versioning**: All configs include `schemaVersion: "1.0"`
|
|
126
|
+
|
|
127
|
+
## API
|
|
128
|
+
|
|
129
|
+
### Core Functions
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import {
|
|
133
|
+
getProductConfig,
|
|
134
|
+
explainProductConfig,
|
|
135
|
+
readConfigFile,
|
|
136
|
+
findNearestConfig,
|
|
137
|
+
clearCaches
|
|
138
|
+
} from '@kb-labs/core-config';
|
|
139
|
+
|
|
140
|
+
// Get product configuration with trace
|
|
141
|
+
const result = await getProductConfig({
|
|
142
|
+
cwd: '/path/to/project',
|
|
143
|
+
product: 'aiReview',
|
|
144
|
+
cli: { debug: true }
|
|
145
|
+
}, schema);
|
|
146
|
+
|
|
147
|
+
console.log(result.config); // Merged configuration
|
|
148
|
+
console.log(result.trace); // Detailed merge trace
|
|
149
|
+
|
|
150
|
+
// Explain configuration without resolving
|
|
151
|
+
const explanation = await explainProductConfig({
|
|
152
|
+
cwd: '/path/to/project',
|
|
153
|
+
product: 'aiReview'
|
|
154
|
+
}, schema);
|
|
155
|
+
|
|
156
|
+
// Read config file with format detection
|
|
157
|
+
const config = await readConfigFile('kb-labs.config.yaml');
|
|
158
|
+
|
|
159
|
+
// Find nearest config file
|
|
160
|
+
const { path, tried } = await findNearestConfig('/path/to/project');
|
|
161
|
+
|
|
162
|
+
// Clear caches (useful for tests)
|
|
163
|
+
clearCaches();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Product Normalization
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { toFsProduct, toConfigProduct, ProductId } from '@kb-labs/core-config';
|
|
170
|
+
|
|
171
|
+
// Convert to filesystem format
|
|
172
|
+
toFsProduct('aiReview'); // 'ai-review'
|
|
173
|
+
toFsProduct('aiDocs'); // 'ai-docs'
|
|
174
|
+
|
|
175
|
+
// Convert to code format
|
|
176
|
+
toConfigProduct('ai-review'); // 'aiReview'
|
|
177
|
+
toConfigProduct('ai-docs'); // 'aiDocs'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Error Handling
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { KbError, getExitCode } from '@kb-labs/core-config';
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const config = await getProductConfig(opts, schema);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error instanceof KbError) {
|
|
189
|
+
console.error(error.message);
|
|
190
|
+
console.error('Hint:', error.hint);
|
|
191
|
+
process.exit(getExitCode(error));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Configuration Layers
|
|
197
|
+
|
|
198
|
+
The system merges configuration in this order (later layers override earlier ones):
|
|
199
|
+
|
|
200
|
+
1. **Runtime defaults** - Built-in defaults for each product
|
|
201
|
+
2. **Profile defaults** - From profile manifest defaults
|
|
202
|
+
3. **Preset defaults** - From org preset package
|
|
203
|
+
4. **Workspace config** - From `kb-labs.config.(json|yaml)`
|
|
204
|
+
5. **Local config** - From `.kb/<product>/<product>.config.json`
|
|
205
|
+
6. **CLI overrides** - From command line arguments
|
|
206
|
+
|
|
207
|
+
## Configuration Files
|
|
208
|
+
|
|
209
|
+
### Workspace Config (`kb-labs.config.yaml`)
|
|
210
|
+
|
|
211
|
+
```yaml
|
|
212
|
+
schemaVersion: "1.0"
|
|
213
|
+
profiles:
|
|
214
|
+
default: "node-ts-lib@1.2.0"
|
|
215
|
+
backend: "node-ts-lib@1.2.0"
|
|
216
|
+
products:
|
|
217
|
+
ai-review:
|
|
218
|
+
enabled: true
|
|
219
|
+
rules: ["security", "performance"]
|
|
220
|
+
devlink:
|
|
221
|
+
watch: true
|
|
222
|
+
build: true
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Local Config (`.kb/ai-review/ai-review.config.json`)
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"$schema": "https://schemas.kb-labs.dev/config.schema.json",
|
|
230
|
+
"schemaVersion": "1.0",
|
|
231
|
+
"enabled": true,
|
|
232
|
+
"rules": ["custom-rule"],
|
|
233
|
+
"settings": {
|
|
234
|
+
"debug": true
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Error Codes
|
|
240
|
+
|
|
241
|
+
| Code | Exit Code | Description |
|
|
242
|
+
|------|-----------|-------------|
|
|
243
|
+
| `ERR_CONFIG_NOT_FOUND` | 2 | Config file not found |
|
|
244
|
+
| `ERR_CONFIG_INVALID` | 1 | Invalid config syntax |
|
|
245
|
+
| `ERR_PRESET_NOT_RESOLVED` | 1 | Preset package not found |
|
|
246
|
+
| `ERR_PROFILE_INCOMPATIBLE` | 1 | Profile version incompatible |
|
|
247
|
+
|
|
248
|
+
## Caching
|
|
249
|
+
|
|
250
|
+
The system uses LRU caching for filesystem reads:
|
|
251
|
+
|
|
252
|
+
- **Key**: `absPath|mtime|size`
|
|
253
|
+
- **Max Size**: 100 entries
|
|
254
|
+
- **Invalidation**: Automatic on file change
|
|
255
|
+
- **Clear**: Use `clearCaches()` for tests
|
|
256
|
+
|
|
257
|
+
## Examples
|
|
258
|
+
|
|
259
|
+
### Basic Usage
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { getProductConfig } from '@kb-labs/core-config';
|
|
263
|
+
|
|
264
|
+
const config = await getProductConfig({
|
|
265
|
+
cwd: process.cwd(),
|
|
266
|
+
product: 'aiReview',
|
|
267
|
+
cli: { debug: true }
|
|
268
|
+
}, aiReviewSchema);
|
|
269
|
+
|
|
270
|
+
console.log('AI Review config:', config.config);
|
|
271
|
+
console.log('Merge trace:', config.trace);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### CLI Integration
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { getProductConfig, KbError, getExitCode } from '@kb-labs/core-config';
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const result = await getProductConfig({
|
|
281
|
+
cwd: process.cwd(),
|
|
282
|
+
product: 'aiReview',
|
|
283
|
+
cli: cliArgs
|
|
284
|
+
}, schema);
|
|
285
|
+
|
|
286
|
+
console.log(JSON.stringify(result.config, null, 2));
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error instanceof KbError) {
|
|
289
|
+
console.error(`Error: ${error.message}`);
|
|
290
|
+
if (error.hint) {
|
|
291
|
+
console.error(`Hint: ${error.hint}`);
|
|
292
|
+
}
|
|
293
|
+
process.exit(getExitCode(error));
|
|
294
|
+
}
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Testing
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { clearCaches } from '@kb-labs/core-config';
|
|
303
|
+
|
|
304
|
+
beforeEach(() => {
|
|
305
|
+
clearCaches(); // Clear caches between tests
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## 🔧 Configuration
|
|
310
|
+
|
|
311
|
+
### Configuration Options
|
|
312
|
+
|
|
313
|
+
The 6-layer configuration system:
|
|
314
|
+
|
|
315
|
+
1. **Runtime defaults** - Built-in defaults for each product
|
|
316
|
+
2. **Profile defaults** - From profile manifest defaults
|
|
317
|
+
3. **Preset defaults** - From org preset package (`$extends`)
|
|
318
|
+
4. **Workspace config** - From `kb-labs.config.(json|yaml)`
|
|
319
|
+
5. **Local config** - From `.kb/<product>/<product>.config.json`
|
|
320
|
+
6. **CLI overrides** - From command line arguments
|
|
321
|
+
|
|
322
|
+
### Environment Variables
|
|
323
|
+
|
|
324
|
+
- None (all configuration via files and options)
|
|
325
|
+
|
|
326
|
+
### Default Values
|
|
327
|
+
|
|
328
|
+
- **Cache Size**: 100 entries (LRU)
|
|
329
|
+
- **Config Format**: Auto-detected (YAML/JSON)
|
|
330
|
+
- **Schema Version**: "1.0"
|
|
331
|
+
|
|
332
|
+
## 🤝 Contributing
|
|
333
|
+
|
|
334
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for development guidelines.
|
|
335
|
+
|
|
336
|
+
## 📄 License
|
|
337
|
+
|
|
338
|
+
KB Public License v1.1 © KB Labs
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { promises } from 'fs';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
// src/utils/fs-atomic.ts
|
|
5
|
+
|
|
6
|
+
// src/errors/kb-error.ts
|
|
7
|
+
var KbError = class extends Error {
|
|
8
|
+
constructor(code, message, hint, meta) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.hint = hint;
|
|
12
|
+
this.meta = meta;
|
|
13
|
+
this.name = "KbError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function getExitCode(err) {
|
|
17
|
+
if (err.code === "ERR_FORBIDDEN") {
|
|
18
|
+
return 3;
|
|
19
|
+
}
|
|
20
|
+
if (err.code === "ERR_CONFIG_NOT_FOUND") {
|
|
21
|
+
return 2;
|
|
22
|
+
}
|
|
23
|
+
if (err.code === "ERR_CONFIG_EXISTS_CONFLICT") {
|
|
24
|
+
return 2;
|
|
25
|
+
}
|
|
26
|
+
if (err.code === "ERR_PATH_OUTSIDE_WORKSPACE") {
|
|
27
|
+
return 2;
|
|
28
|
+
}
|
|
29
|
+
if (err.code.startsWith("ERR_")) {
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
var ERROR_HINTS = {
|
|
35
|
+
ERR_CONFIG_NOT_FOUND: "Create .kb/kb-labs.config.yaml or run: kb init",
|
|
36
|
+
ERR_CONFIG_INVALID: "Check configuration syntax and required fields",
|
|
37
|
+
ERR_CONFIG_EXISTS_CONFLICT: "Use --force to overwrite existing configuration",
|
|
38
|
+
ERR_PATH_OUTSIDE_WORKSPACE: "Check cwd or use a relative path within the workspace",
|
|
39
|
+
ERR_PRESET_NOT_RESOLVED: "Install the required preset package or check network connectivity",
|
|
40
|
+
ERR_PROFILE_INCOMPATIBLE: "Profile version is incompatible with current system",
|
|
41
|
+
ERR_PROFILE_NOT_DEFINED: "Add profiles[] to kb.config.json or pass --profile=<id>",
|
|
42
|
+
ERR_PROFILE_RESOLVE_FAILED: "Profile registry unavailable. Define profiles[] inside kb.config.json or use workspace presets.",
|
|
43
|
+
ERR_PROFILE_EXTENDS_FAILED: "Profile extends failed. Check extends reference or install required preset/package.",
|
|
44
|
+
ERR_PROFILE_SCOPE_NOT_FOUND: "Scope not found. Use --scope=<id> with one of the configured scopes.",
|
|
45
|
+
ERR_PROFILE_SCOPE_CONFLICT: "Multiple scopes matched. Specify --scope=<id> explicitly.",
|
|
46
|
+
ERR_PROFILE_INVALID_FORMAT: "Update profiles[] in kb.config.json (Profiles v2) or rerun your plugin setup command.",
|
|
47
|
+
ERR_ARTIFACT_LIMIT_EXCEEDED: "Remove or split large artifacts, or reduce artifact count",
|
|
48
|
+
ERR_FORBIDDEN: "Add role in policy.overrides or use preset..."
|
|
49
|
+
};
|
|
50
|
+
async function writeFileAtomic(path, data) {
|
|
51
|
+
const tmp = `${path}.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
52
|
+
try {
|
|
53
|
+
await promises.mkdir(dirname(path), { recursive: true });
|
|
54
|
+
await promises.mkdir(dirname(tmp), { recursive: true });
|
|
55
|
+
await promises.writeFile(tmp, data);
|
|
56
|
+
await promises.rename(tmp, path);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
try {
|
|
59
|
+
await promises.unlink(tmp);
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function ensureWithinWorkspace(targetPath, workspaceRoot) {
|
|
66
|
+
const absTarget = resolve(targetPath);
|
|
67
|
+
const absRoot = resolve(workspaceRoot);
|
|
68
|
+
if (!absTarget.startsWith(absRoot)) {
|
|
69
|
+
throw new KbError(
|
|
70
|
+
"ERR_PATH_OUTSIDE_WORKSPACE",
|
|
71
|
+
`Refusing to write outside workspace: ${targetPath}`,
|
|
72
|
+
"Check cwd or use a relative path within the workspace",
|
|
73
|
+
{ targetPath, workspaceRoot }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { ERROR_HINTS, KbError, ensureWithinWorkspace, getExitCode, writeFileAtomic };
|
|
79
|
+
//# sourceMappingURL=chunk-A2WE2JZW.js.map
|
|
80
|
+
//# sourceMappingURL=chunk-A2WE2JZW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/kb-error.ts","../src/utils/fs-atomic.ts"],"names":["fs"],"mappings":"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","file":"chunk-A2WE2JZW.js","sourcesContent":["/**\n * @module @kb-labs/core/config/errors\n * Standardized error class for KB Labs with exit code mapping\n */\n\nexport class KbError extends Error {\n constructor(\n public code: string,\n message: string,\n public hint?: string,\n public meta?: any\n ) {\n super(message);\n this.name = 'KbError';\n }\n}\n\n/**\n * Maps KbError codes to CLI exit codes\n */\nexport function getExitCode(err: KbError): number {\n if (err.code === 'ERR_FORBIDDEN') {return 3;}\n if (err.code === 'ERR_CONFIG_NOT_FOUND') {return 2;}\n if (err.code === 'ERR_CONFIG_EXISTS_CONFLICT') {return 2;}\n if (err.code === 'ERR_PATH_OUTSIDE_WORKSPACE') {return 2;}\n if (err.code.startsWith('ERR_')) {return 1;}\n return 1;\n}\n\n/**\n * Error codes with their standard hints\n */\nexport const ERROR_HINTS = {\n ERR_CONFIG_NOT_FOUND: 'Create .kb/kb-labs.config.yaml or run: kb init',\n ERR_CONFIG_INVALID: 'Check configuration syntax and required fields',\n ERR_CONFIG_EXISTS_CONFLICT: 'Use --force to overwrite existing configuration',\n ERR_PATH_OUTSIDE_WORKSPACE: 'Check cwd or use a relative path within the workspace',\n ERR_PRESET_NOT_RESOLVED: 'Install the required preset package or check network connectivity',\n ERR_PROFILE_INCOMPATIBLE: 'Profile version is incompatible with current system',\n ERR_PROFILE_NOT_DEFINED: 'Add profiles[] to kb.config.json or pass --profile=<id>',\n ERR_PROFILE_RESOLVE_FAILED: 'Profile registry unavailable. Define profiles[] inside kb.config.json or use workspace presets.',\n ERR_PROFILE_EXTENDS_FAILED: 'Profile extends failed. Check extends reference or install required preset/package.',\n ERR_PROFILE_SCOPE_NOT_FOUND: 'Scope not found. Use --scope=<id> with one of the configured scopes.',\n ERR_PROFILE_SCOPE_CONFLICT: 'Multiple scopes matched. Specify --scope=<id> explicitly.',\n ERR_PROFILE_INVALID_FORMAT: 'Update profiles[] in kb.config.json (Profiles v2) or rerun your plugin setup command.',\n ERR_ARTIFACT_LIMIT_EXCEEDED: 'Remove or split large artifacts, or reduce artifact count',\n ERR_FORBIDDEN: 'Add role in policy.overrides or use preset...',\n} as const;\n\nexport type ErrorCode = keyof typeof ERROR_HINTS;\n","/**\n * @module @kb-labs/core/config/utils/fs-atomic\n * Atomic file write operations using tmp+rename pattern\n */\n\nimport { promises as fs } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { KbError } from '../errors/kb-error';\nimport { dirname } from 'node:path';\n\n/**\n * Write file atomically using tmp+rename pattern\n * This ensures that the file is never partially written in case of crashes\n */\nexport async function writeFileAtomic(\n path: string,\n data: string | Uint8Array\n): Promise<void> {\n const tmp = `${path}.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n \n try {\n // Ensure parent directory exists\n await fs.mkdir(dirname(path), { recursive: true });\n await fs.mkdir(dirname(tmp), { recursive: true });\n \n // Write to temp file\n await fs.writeFile(tmp, data);\n \n // Atomic rename\n await fs.rename(tmp, path);\n } catch (error) {\n // Clean up temp file if it exists\n try {\n await fs.unlink(tmp);\n } catch {\n // Ignore cleanup errors\n }\n throw error;\n }\n}\n\n/**\n * Ensure a path is within the workspace root\n * Throws ERR_PATH_OUTSIDE_WORKSPACE if the path escapes the workspace\n */\nexport function ensureWithinWorkspace(targetPath: string, workspaceRoot: string): void {\n const absTarget = resolve(targetPath);\n const absRoot = resolve(workspaceRoot);\n \n if (!absTarget.startsWith(absRoot)) {\n throw new KbError(\n 'ERR_PATH_OUTSIDE_WORKSPACE',\n `Refusing to write outside workspace: ${targetPath}`,\n 'Check cwd or use a relative path within the workspace',\n { targetPath, workspaceRoot }\n );\n }\n}\n\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"fs-atomic-EKSZ6G5A.js"}
|