micro-contracts 0.9.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 +22 -0
- package/README.md +351 -0
- package/dist/cli/templates.d.ts +16 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +377 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +978 -0
- package/dist/cli.js.map +1 -0
- package/dist/generator/dependencyGenerator.d.ts +43 -0
- package/dist/generator/dependencyGenerator.d.ts.map +1 -0
- package/dist/generator/dependencyGenerator.js +159 -0
- package/dist/generator/dependencyGenerator.js.map +1 -0
- package/dist/generator/domainGenerator.d.ts +16 -0
- package/dist/generator/domainGenerator.d.ts.map +1 -0
- package/dist/generator/domainGenerator.js +212 -0
- package/dist/generator/domainGenerator.js.map +1 -0
- package/dist/generator/index.d.ts +37 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +747 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/linter.d.ts +24 -0
- package/dist/generator/linter.d.ts.map +1 -0
- package/dist/generator/linter.js +202 -0
- package/dist/generator/linter.js.map +1 -0
- package/dist/generator/overlayProcessor.d.ts +90 -0
- package/dist/generator/overlayProcessor.d.ts.map +1 -0
- package/dist/generator/overlayProcessor.js +532 -0
- package/dist/generator/overlayProcessor.js.map +1 -0
- package/dist/generator/schemaGenerator.d.ts +10 -0
- package/dist/generator/schemaGenerator.d.ts.map +1 -0
- package/dist/generator/schemaGenerator.js +299 -0
- package/dist/generator/schemaGenerator.js.map +1 -0
- package/dist/generator/templateProcessor.d.ts +178 -0
- package/dist/generator/templateProcessor.d.ts.map +1 -0
- package/dist/generator/templateProcessor.js +607 -0
- package/dist/generator/templateProcessor.js.map +1 -0
- package/dist/generator/typeGenerator.d.ts +9 -0
- package/dist/generator/typeGenerator.d.ts.map +1 -0
- package/dist/generator/typeGenerator.js +395 -0
- package/dist/generator/typeGenerator.js.map +1 -0
- package/dist/guardrails/allowlist.d.ts +45 -0
- package/dist/guardrails/allowlist.d.ts.map +1 -0
- package/dist/guardrails/allowlist.js +261 -0
- package/dist/guardrails/allowlist.js.map +1 -0
- package/dist/guardrails/config.d.ts +40 -0
- package/dist/guardrails/config.d.ts.map +1 -0
- package/dist/guardrails/config.js +174 -0
- package/dist/guardrails/config.js.map +1 -0
- package/dist/guardrails/docs.d.ts +24 -0
- package/dist/guardrails/docs.d.ts.map +1 -0
- package/dist/guardrails/docs.js +138 -0
- package/dist/guardrails/docs.js.map +1 -0
- package/dist/guardrails/drift.d.ts +23 -0
- package/dist/guardrails/drift.d.ts.map +1 -0
- package/dist/guardrails/drift.js +127 -0
- package/dist/guardrails/drift.js.map +1 -0
- package/dist/guardrails/index.d.ts +19 -0
- package/dist/guardrails/index.d.ts.map +1 -0
- package/dist/guardrails/index.js +25 -0
- package/dist/guardrails/index.js.map +1 -0
- package/dist/guardrails/lint.d.ts +20 -0
- package/dist/guardrails/lint.d.ts.map +1 -0
- package/dist/guardrails/lint.js +274 -0
- package/dist/guardrails/lint.js.map +1 -0
- package/dist/guardrails/manifest.d.ts +43 -0
- package/dist/guardrails/manifest.d.ts.map +1 -0
- package/dist/guardrails/manifest.js +231 -0
- package/dist/guardrails/manifest.js.map +1 -0
- package/dist/guardrails/runner.d.ts +31 -0
- package/dist/guardrails/runner.d.ts.map +1 -0
- package/dist/guardrails/runner.js +268 -0
- package/dist/guardrails/runner.js.map +1 -0
- package/dist/guardrails/security.d.ts +31 -0
- package/dist/guardrails/security.d.ts.map +1 -0
- package/dist/guardrails/security.js +181 -0
- package/dist/guardrails/security.js.map +1 -0
- package/dist/guardrails/typecheck.d.ts +15 -0
- package/dist/guardrails/typecheck.d.ts.map +1 -0
- package/dist/guardrails/typecheck.js +104 -0
- package/dist/guardrails/typecheck.js.map +1 -0
- package/dist/guardrails/types.d.ts +196 -0
- package/dist/guardrails/types.d.ts.map +1 -0
- package/dist/guardrails/types.js +8 -0
- package/dist/guardrails/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +489 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +297 -0
- package/dist/types.js.map +1 -0
- package/docs/architecture.svg +226 -0
- package/docs/development-guardrails.md +541 -0
- package/docs/guardrails-concept.svg +252 -0
- package/docs/overlays-deep-dive.md +298 -0
- package/package.json +66 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowlist verification for guardrails
|
|
3
|
+
*
|
|
4
|
+
* Verifies that changed files are within allowed boundaries.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { loadGuardrailsConfigWithPath } from './config.js';
|
|
10
|
+
/**
|
|
11
|
+
* Simple glob pattern matching with negation support
|
|
12
|
+
* Patterns are evaluated top-to-bottom; last match wins.
|
|
13
|
+
* - "!pattern" → if matched, result becomes false (exclude)
|
|
14
|
+
* - "pattern" → if matched, result becomes true (include)
|
|
15
|
+
*/
|
|
16
|
+
export function matchWithNegation(patterns, file) {
|
|
17
|
+
if (!patterns || patterns.length === 0)
|
|
18
|
+
return false;
|
|
19
|
+
let matched = false;
|
|
20
|
+
for (const raw of patterns) {
|
|
21
|
+
const neg = raw.startsWith('!');
|
|
22
|
+
const pattern = neg ? raw.slice(1) : raw;
|
|
23
|
+
if (!pattern)
|
|
24
|
+
continue;
|
|
25
|
+
if (matchGlob(file, pattern)) {
|
|
26
|
+
matched = !neg;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return matched;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Simple glob pattern matching
|
|
33
|
+
* Supports:
|
|
34
|
+
* - * matches any characters except /
|
|
35
|
+
* - ** matches any characters including / (zero or more path segments)
|
|
36
|
+
* - ? matches single character
|
|
37
|
+
*/
|
|
38
|
+
export function matchGlob(file, pattern) {
|
|
39
|
+
// Normalize paths
|
|
40
|
+
const normalizedFile = file.replace(/\\/g, '/');
|
|
41
|
+
const normalizedPattern = pattern.replace(/\\/g, '/');
|
|
42
|
+
return matchGlobInternal(normalizedFile, normalizedPattern);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Internal glob matching implementation using regex
|
|
46
|
+
*/
|
|
47
|
+
function matchGlobInternal(file, pattern) {
|
|
48
|
+
// Convert glob pattern to regex step by step
|
|
49
|
+
let regex = '';
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < pattern.length) {
|
|
52
|
+
const char = pattern[i];
|
|
53
|
+
const nextChar = pattern[i + 1];
|
|
54
|
+
if (char === '*' && nextChar === '*') {
|
|
55
|
+
// ** - matches any path segments
|
|
56
|
+
const afterStars = pattern[i + 2];
|
|
57
|
+
if (afterStars === '/') {
|
|
58
|
+
// **/ at start or middle - matches zero or more path segments including nothing
|
|
59
|
+
regex += '(?:.*/)?';
|
|
60
|
+
i += 3; // skip **/
|
|
61
|
+
}
|
|
62
|
+
else if (i + 2 === pattern.length || afterStars === undefined) {
|
|
63
|
+
// ** at end - matches everything
|
|
64
|
+
regex += '.*';
|
|
65
|
+
i += 2;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// ** without trailing / - matches any characters
|
|
69
|
+
regex += '.*';
|
|
70
|
+
i += 2;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (char === '*') {
|
|
74
|
+
// * - matches any characters except /
|
|
75
|
+
regex += '[^/]*';
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
else if (char === '?') {
|
|
79
|
+
// ? - matches single character except /
|
|
80
|
+
regex += '[^/]';
|
|
81
|
+
i++;
|
|
82
|
+
}
|
|
83
|
+
else if (char === '/') {
|
|
84
|
+
regex += '/';
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
else if ('.+^${}()|[]\\'.includes(char)) {
|
|
88
|
+
// Escape regex special chars
|
|
89
|
+
regex += '\\' + char;
|
|
90
|
+
i++;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
regex += char;
|
|
94
|
+
i++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Anchor the pattern
|
|
98
|
+
regex = `^${regex}$`;
|
|
99
|
+
try {
|
|
100
|
+
return new RegExp(regex).test(file);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get list of changed files, filtered to a specific base directory
|
|
108
|
+
*/
|
|
109
|
+
export function getChangedFiles(options) {
|
|
110
|
+
const { changedFilesPath, baseRef, baseDir } = options;
|
|
111
|
+
let files;
|
|
112
|
+
if (changedFilesPath) {
|
|
113
|
+
// Read from file (CI mode)
|
|
114
|
+
if (!fs.existsSync(changedFilesPath)) {
|
|
115
|
+
throw new Error(`Changed files list not found: ${changedFilesPath}`);
|
|
116
|
+
}
|
|
117
|
+
files = fs.readFileSync(changedFilesPath, 'utf-8')
|
|
118
|
+
.trim()
|
|
119
|
+
.split('\n')
|
|
120
|
+
.filter(Boolean);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Use git diff (local mode)
|
|
124
|
+
try {
|
|
125
|
+
const ref = baseRef || 'HEAD';
|
|
126
|
+
// Try staged files first
|
|
127
|
+
let out = execSync('git diff --name-only --cached', { encoding: 'utf8' });
|
|
128
|
+
files = out.trim().split('\n').filter(Boolean);
|
|
129
|
+
// If no staged files, try unstaged
|
|
130
|
+
if (files.length === 0) {
|
|
131
|
+
out = execSync('git diff --name-only', { encoding: 'utf8' });
|
|
132
|
+
files = out.trim().split('\n').filter(Boolean);
|
|
133
|
+
}
|
|
134
|
+
// If still no files, try diff against base ref
|
|
135
|
+
if (files.length === 0 && ref !== 'HEAD') {
|
|
136
|
+
out = execSync(`git diff --name-only ${ref}...HEAD`, { encoding: 'utf8' });
|
|
137
|
+
files = out.trim().split('\n').filter(Boolean);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// Git not available or not in a git repo
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Filter and convert paths relative to baseDir
|
|
146
|
+
if (baseDir) {
|
|
147
|
+
// Get git root to resolve absolute paths
|
|
148
|
+
let gitRoot;
|
|
149
|
+
try {
|
|
150
|
+
gitRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
gitRoot = process.cwd();
|
|
154
|
+
}
|
|
155
|
+
const absoluteBaseDir = path.resolve(baseDir);
|
|
156
|
+
// Filter to only files under baseDir and convert to relative paths
|
|
157
|
+
files = files
|
|
158
|
+
.map(f => path.resolve(gitRoot, f)) // Convert to absolute
|
|
159
|
+
.filter(f => f.startsWith(absoluteBaseDir + path.sep) || f === absoluteBaseDir) // Filter to baseDir
|
|
160
|
+
.map(f => path.relative(absoluteBaseDir, f)); // Convert to relative from baseDir
|
|
161
|
+
}
|
|
162
|
+
return files;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Verify changed files against allowlist
|
|
166
|
+
*/
|
|
167
|
+
export function verifyAllowlist(changedFiles, config) {
|
|
168
|
+
const violations = [];
|
|
169
|
+
for (const file of changedFiles) {
|
|
170
|
+
// 1. Check if protected (not allowed in normal PRs)
|
|
171
|
+
if (matchWithNegation(config.protected, file)) {
|
|
172
|
+
violations.push({ file, reason: 'protected' });
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
// 2. Check if generated (allowed, but must pass drift/manifest checks)
|
|
176
|
+
if (matchWithNegation(config.generated, file)) {
|
|
177
|
+
// Generated files are allowed to change, but we don't add a violation
|
|
178
|
+
// The drift/manifest checks will verify integrity
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// 3. Must be in allowed list
|
|
182
|
+
if (!matchWithNegation(config.allowed, file)) {
|
|
183
|
+
violations.push({ file, reason: 'not-in-allowlist' });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
valid: violations.length === 0,
|
|
188
|
+
violations,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Run allowlist check
|
|
193
|
+
*/
|
|
194
|
+
export async function runAllowlistCheck(options) {
|
|
195
|
+
const start = Date.now();
|
|
196
|
+
try {
|
|
197
|
+
// Load config with path information
|
|
198
|
+
const { config, baseDir, configPath } = loadGuardrailsConfigWithPath(options.guardrailsPath);
|
|
199
|
+
// Get changed files relative to guardrails config directory
|
|
200
|
+
const changedFiles = getChangedFiles({
|
|
201
|
+
changedFilesPath: options.changedFilesPath,
|
|
202
|
+
baseDir, // Filter to files under guardrails.yaml directory
|
|
203
|
+
});
|
|
204
|
+
if (changedFiles.length === 0) {
|
|
205
|
+
return {
|
|
206
|
+
name: 'allowlist',
|
|
207
|
+
status: 'pass',
|
|
208
|
+
duration: Date.now() - start,
|
|
209
|
+
message: configPath
|
|
210
|
+
? `No changed files under ${path.basename(path.dirname(configPath))}/`
|
|
211
|
+
: 'No changed files to check',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Verify allowlist
|
|
215
|
+
const result = verifyAllowlist(changedFiles, config);
|
|
216
|
+
if (result.valid) {
|
|
217
|
+
return {
|
|
218
|
+
name: 'allowlist',
|
|
219
|
+
status: 'pass',
|
|
220
|
+
duration: Date.now() - start,
|
|
221
|
+
message: `All ${changedFiles.length} changed files are within allowed boundaries`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// Build error details
|
|
225
|
+
const details = result.violations.map(v => ` - ${v.file} (${v.reason})`);
|
|
226
|
+
return {
|
|
227
|
+
name: 'allowlist',
|
|
228
|
+
status: 'fail',
|
|
229
|
+
duration: Date.now() - start,
|
|
230
|
+
message: `${result.violations.length} file(s) are not allowed to be modified`,
|
|
231
|
+
details,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
return {
|
|
236
|
+
name: 'allowlist',
|
|
237
|
+
status: 'fail',
|
|
238
|
+
duration: Date.now() - start,
|
|
239
|
+
message: error instanceof Error ? error.message : String(error),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Format allowlist result for CLI output
|
|
245
|
+
*/
|
|
246
|
+
export function formatAllowlistResult(result) {
|
|
247
|
+
const lines = [];
|
|
248
|
+
if (result.valid) {
|
|
249
|
+
lines.push('✅ All changed files are within allowed boundaries');
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
lines.push('❌ The following files are not allowed to be modified in a normal PR:\n');
|
|
253
|
+
for (const { file, reason } of result.violations) {
|
|
254
|
+
lines.push(` - ${file} (${reason})`);
|
|
255
|
+
}
|
|
256
|
+
lines.push('\n💡 If this is a generated artifact, run the pinned generator and pass drift/manifest checks.');
|
|
257
|
+
lines.push('💡 If this should be editable, update guardrails.yaml (allowed/protected/generated).');
|
|
258
|
+
}
|
|
259
|
+
return lines.join('\n');
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=allowlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowlist.js","sourceRoot":"","sources":["../../src/guardrails/allowlist.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrails configuration parser
|
|
3
|
+
*
|
|
4
|
+
* Handles loading and validation of guardrails.yaml configuration files.
|
|
5
|
+
*/
|
|
6
|
+
import type { GuardrailsConfig } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Default guardrails configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_GUARDRAILS: GuardrailsConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Find guardrails config file in current directory or ancestors
|
|
13
|
+
*/
|
|
14
|
+
export declare function findGuardrailsConfig(startDir?: string): string | null;
|
|
15
|
+
export interface LoadedGuardrailsConfig {
|
|
16
|
+
config: GuardrailsConfig;
|
|
17
|
+
/** Directory containing the guardrails config file (for relative path resolution) */
|
|
18
|
+
baseDir: string;
|
|
19
|
+
/** Full path to the config file (null if using defaults) */
|
|
20
|
+
configPath: string | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load guardrails configuration from file
|
|
24
|
+
* Falls back to defaults if file not found
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadGuardrailsConfig(configPath?: string): GuardrailsConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Load guardrails configuration with path information
|
|
29
|
+
* Returns both the config and the base directory for relative path resolution
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadGuardrailsConfigWithPath(configPath?: string): LoadedGuardrailsConfig;
|
|
32
|
+
/**
|
|
33
|
+
* Create a guardrails.yaml template
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateGuardrailsTemplate(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Write guardrails template to file
|
|
38
|
+
*/
|
|
39
|
+
export declare function createGuardrailsConfig(outputPath?: string): void;
|
|
40
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/guardrails/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBAyBhC,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBrE;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,qFAAqF;IACrF,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAG1E;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,sBAAsB,CAiCxF;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CA4DnD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,GAAE,MAA0C,GAAG,IAAI,CAGnG"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrails configuration parser
|
|
3
|
+
*
|
|
4
|
+
* Handles loading and validation of guardrails.yaml configuration files.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import yaml from 'js-yaml';
|
|
9
|
+
/**
|
|
10
|
+
* Default guardrails configuration
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_GUARDRAILS = {
|
|
13
|
+
allowed: [
|
|
14
|
+
'spec/**/*.yaml',
|
|
15
|
+
'spec/**/*.yml',
|
|
16
|
+
'spec/**/*.hbs',
|
|
17
|
+
'server/src/**/domains/**/*.ts',
|
|
18
|
+
'server/src/**/container.ts',
|
|
19
|
+
'server/src/server.ts',
|
|
20
|
+
'docs/**/*.md',
|
|
21
|
+
'README.md',
|
|
22
|
+
'package.json',
|
|
23
|
+
'tsconfig.json',
|
|
24
|
+
],
|
|
25
|
+
protected: [
|
|
26
|
+
'spec/spectral.yaml',
|
|
27
|
+
'spec/_shared/overlays/**',
|
|
28
|
+
'guardrails.yaml',
|
|
29
|
+
'.github/**',
|
|
30
|
+
],
|
|
31
|
+
generated: [
|
|
32
|
+
'packages/**',
|
|
33
|
+
'**/*.generated.ts',
|
|
34
|
+
'**/*.generated.yaml',
|
|
35
|
+
'**/*.generated.yml',
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Find guardrails config file in current directory or ancestors
|
|
40
|
+
*/
|
|
41
|
+
export function findGuardrailsConfig(startDir) {
|
|
42
|
+
const dir = startDir || process.cwd();
|
|
43
|
+
// Support multiple naming conventions
|
|
44
|
+
const candidates = [
|
|
45
|
+
'micro-contracts.guardrails.yaml',
|
|
46
|
+
'micro-contracts.guardrails.yml',
|
|
47
|
+
'guardrails.yaml', // Legacy name
|
|
48
|
+
'guardrails.yml',
|
|
49
|
+
];
|
|
50
|
+
let current = path.resolve(dir);
|
|
51
|
+
const root = path.parse(current).root;
|
|
52
|
+
while (current !== root) {
|
|
53
|
+
for (const candidate of candidates) {
|
|
54
|
+
const configPath = path.join(current, candidate);
|
|
55
|
+
if (fs.existsSync(configPath)) {
|
|
56
|
+
return configPath;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
current = path.dirname(current);
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Load guardrails configuration from file
|
|
65
|
+
* Falls back to defaults if file not found
|
|
66
|
+
*/
|
|
67
|
+
export function loadGuardrailsConfig(configPath) {
|
|
68
|
+
const result = loadGuardrailsConfigWithPath(configPath);
|
|
69
|
+
return result.config;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Load guardrails configuration with path information
|
|
73
|
+
* Returns both the config and the base directory for relative path resolution
|
|
74
|
+
*/
|
|
75
|
+
export function loadGuardrailsConfigWithPath(configPath) {
|
|
76
|
+
const resolvedPath = configPath || findGuardrailsConfig();
|
|
77
|
+
if (!resolvedPath || !fs.existsSync(resolvedPath)) {
|
|
78
|
+
// Return default config with current directory as base
|
|
79
|
+
return {
|
|
80
|
+
config: DEFAULT_GUARDRAILS,
|
|
81
|
+
baseDir: process.cwd(),
|
|
82
|
+
configPath: null,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
86
|
+
const config = yaml.load(content);
|
|
87
|
+
// Validate basic structure
|
|
88
|
+
if (typeof config !== 'object' || config === null) {
|
|
89
|
+
throw new Error(`Invalid guardrails config: ${resolvedPath}`);
|
|
90
|
+
}
|
|
91
|
+
// Merge with defaults for any missing sections
|
|
92
|
+
const mergedConfig = {
|
|
93
|
+
allowed: config.allowed ?? DEFAULT_GUARDRAILS.allowed,
|
|
94
|
+
protected: config.protected ?? DEFAULT_GUARDRAILS.protected,
|
|
95
|
+
generated: config.generated ?? DEFAULT_GUARDRAILS.generated,
|
|
96
|
+
checks: config.checks, // Pass through checks config
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
config: mergedConfig,
|
|
100
|
+
baseDir: path.dirname(resolvedPath),
|
|
101
|
+
configPath: resolvedPath,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a guardrails.yaml template
|
|
106
|
+
*/
|
|
107
|
+
export function generateGuardrailsTemplate() {
|
|
108
|
+
const template = `# AI-Driven Development Guardrails Configuration
|
|
109
|
+
#
|
|
110
|
+
# This file defines which files can be modified in normal development PRs.
|
|
111
|
+
# Patterns use gitignore-style matching with glob patterns.
|
|
112
|
+
# Prefix with ! to negate (exclude) a pattern.
|
|
113
|
+
|
|
114
|
+
# Files that can be edited in normal development PRs
|
|
115
|
+
allowed:
|
|
116
|
+
# OpenAPI specs (source of truth)
|
|
117
|
+
- spec/**/openapi/*.yaml
|
|
118
|
+
- spec/**/templates/*.hbs
|
|
119
|
+
|
|
120
|
+
# Domain implementations (human-written)
|
|
121
|
+
- server/src/**/domains/**/*.ts
|
|
122
|
+
- server/src/**/container.ts
|
|
123
|
+
- server/src/server.ts
|
|
124
|
+
|
|
125
|
+
# Module-specific overlays (NOT _shared)
|
|
126
|
+
- server/src/*/overlays/**/*.ts
|
|
127
|
+
- "!server/src/_shared/overlays/**"
|
|
128
|
+
|
|
129
|
+
# Configuration
|
|
130
|
+
- micro-contracts.config.yaml
|
|
131
|
+
- package.json
|
|
132
|
+
- tsconfig.json
|
|
133
|
+
|
|
134
|
+
# Documentation
|
|
135
|
+
- docs/**/*.md
|
|
136
|
+
- README.md
|
|
137
|
+
|
|
138
|
+
# Files that require special approval
|
|
139
|
+
protected:
|
|
140
|
+
# Spectral lint rules
|
|
141
|
+
- spec/spectral.yaml
|
|
142
|
+
|
|
143
|
+
# Shared overlay definitions
|
|
144
|
+
- spec/_shared/overlays/**
|
|
145
|
+
|
|
146
|
+
# Shared security overlay implementations
|
|
147
|
+
- server/src/_shared/overlays/**
|
|
148
|
+
|
|
149
|
+
# This guardrails configuration
|
|
150
|
+
- micro-contracts.guardrails.yaml
|
|
151
|
+
|
|
152
|
+
# CI/workflow definitions
|
|
153
|
+
- .github/**
|
|
154
|
+
|
|
155
|
+
# Generated artifacts (committed, but only modified via generate)
|
|
156
|
+
generated:
|
|
157
|
+
# Contract packages
|
|
158
|
+
- packages/**
|
|
159
|
+
|
|
160
|
+
# Generated files
|
|
161
|
+
- "**/*.generated.ts"
|
|
162
|
+
- "**/*.generated.yaml"
|
|
163
|
+
- "**/*.generated.yml"
|
|
164
|
+
`;
|
|
165
|
+
return template;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Write guardrails template to file
|
|
169
|
+
*/
|
|
170
|
+
export function createGuardrailsConfig(outputPath = 'micro-contracts.guardrails.yaml') {
|
|
171
|
+
const template = generateGuardrailsTemplate();
|
|
172
|
+
fs.writeFileSync(outputPath, template);
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/guardrails/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation consistency check for guardrails
|
|
3
|
+
*
|
|
4
|
+
* Verifies that documentation references (links, file paths, images) are valid.
|
|
5
|
+
*/
|
|
6
|
+
import type { CheckResult, CheckOptions } from './types.js';
|
|
7
|
+
export interface DocIssue {
|
|
8
|
+
file: string;
|
|
9
|
+
line: number;
|
|
10
|
+
issue: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Check a single markdown file for broken references
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkMarkdownFile(docFile: string): DocIssue[];
|
|
16
|
+
/**
|
|
17
|
+
* Find all markdown files to check
|
|
18
|
+
*/
|
|
19
|
+
export declare function findMarkdownFiles(): Promise<string[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Run docs consistency check
|
|
22
|
+
*/
|
|
23
|
+
export declare function runDocsCheck(options: CheckOptions): Promise<CheckResult>;
|
|
24
|
+
//# sourceMappingURL=docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/guardrails/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAoBD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAsD7D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAe3D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAkD9E"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation consistency check for guardrails
|
|
3
|
+
*
|
|
4
|
+
* Verifies that documentation references (links, file paths, images) are valid.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { glob } from 'glob';
|
|
9
|
+
/**
|
|
10
|
+
* Check if a path exists relative to a document file
|
|
11
|
+
*/
|
|
12
|
+
function existsRelativeToDoc(docFile, refPath) {
|
|
13
|
+
// Skip empty, anchor-only, or URL
|
|
14
|
+
if (!refPath || refPath.startsWith('#') || refPath.includes('://')) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
// Root-relative (starts with /) → resolve from cwd (repo root)
|
|
18
|
+
// Relative (including ./) → resolve from doc's directory
|
|
19
|
+
const resolved = refPath.startsWith('/')
|
|
20
|
+
? path.resolve(process.cwd(), refPath.slice(1))
|
|
21
|
+
: path.resolve(path.dirname(docFile), refPath);
|
|
22
|
+
return fs.existsSync(resolved);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check a single markdown file for broken references
|
|
26
|
+
*/
|
|
27
|
+
export function checkMarkdownFile(docFile) {
|
|
28
|
+
const issues = [];
|
|
29
|
+
if (!fs.existsSync(docFile)) {
|
|
30
|
+
return issues;
|
|
31
|
+
}
|
|
32
|
+
const lines = fs.readFileSync(docFile, 'utf-8').split('\n');
|
|
33
|
+
lines.forEach((line, idx) => {
|
|
34
|
+
const lineNum = idx + 1;
|
|
35
|
+
// 1. Check file path references like [examples/...] or `examples/...`
|
|
36
|
+
const pathRefs = line.matchAll(/(?:\[|`)([a-zA-Z0-9_\-./]+\/[a-zA-Z0-9_\-./]+\.[a-zA-Z]+)(?:\]|`)/g);
|
|
37
|
+
for (const match of pathRefs) {
|
|
38
|
+
const refPath = match[1];
|
|
39
|
+
if (!existsRelativeToDoc(docFile, refPath)) {
|
|
40
|
+
issues.push({
|
|
41
|
+
file: docFile,
|
|
42
|
+
line: lineNum,
|
|
43
|
+
issue: `Referenced file does not exist: ${refPath}`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 2. Check markdown link targets like ](path#anchor)
|
|
48
|
+
const linkRefs = line.matchAll(/\]\((?!http)([^)]+)\)/g);
|
|
49
|
+
for (const match of linkRefs) {
|
|
50
|
+
const raw = match[1];
|
|
51
|
+
const linkPath = raw.split('#')[0]; // Remove anchor
|
|
52
|
+
if (linkPath && !existsRelativeToDoc(docFile, linkPath)) {
|
|
53
|
+
issues.push({
|
|
54
|
+
file: docFile,
|
|
55
|
+
line: lineNum,
|
|
56
|
+
issue: `Broken link: ${linkPath}`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// 3. Check image references like 
|
|
61
|
+
const imgRefs = line.matchAll(/!\[[^\]]*\]\((?!http)([^)]+)\)/g);
|
|
62
|
+
for (const match of imgRefs) {
|
|
63
|
+
const imgPath = match[1].split('#')[0];
|
|
64
|
+
if (imgPath && !existsRelativeToDoc(docFile, imgPath)) {
|
|
65
|
+
issues.push({
|
|
66
|
+
file: docFile,
|
|
67
|
+
line: lineNum,
|
|
68
|
+
issue: `Image not found: ${imgPath}`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return issues;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Find all markdown files to check
|
|
77
|
+
*/
|
|
78
|
+
export async function findMarkdownFiles() {
|
|
79
|
+
const files = [];
|
|
80
|
+
// Check README.md at root
|
|
81
|
+
if (fs.existsSync('README.md')) {
|
|
82
|
+
files.push('README.md');
|
|
83
|
+
}
|
|
84
|
+
// Check docs directory
|
|
85
|
+
const docsFiles = await glob('docs/**/*.md', {
|
|
86
|
+
ignore: ['**/node_modules/**'],
|
|
87
|
+
});
|
|
88
|
+
files.push(...docsFiles);
|
|
89
|
+
return files;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Run docs consistency check
|
|
93
|
+
*/
|
|
94
|
+
export async function runDocsCheck(options) {
|
|
95
|
+
const start = Date.now();
|
|
96
|
+
try {
|
|
97
|
+
// Find all markdown files
|
|
98
|
+
const docFiles = await findMarkdownFiles();
|
|
99
|
+
if (docFiles.length === 0) {
|
|
100
|
+
return {
|
|
101
|
+
name: 'docs',
|
|
102
|
+
status: 'skip',
|
|
103
|
+
duration: Date.now() - start,
|
|
104
|
+
message: 'No documentation files found',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const allIssues = [];
|
|
108
|
+
for (const docFile of docFiles) {
|
|
109
|
+
const issues = checkMarkdownFile(docFile);
|
|
110
|
+
allIssues.push(...issues);
|
|
111
|
+
}
|
|
112
|
+
if (allIssues.length > 0) {
|
|
113
|
+
const details = allIssues.map(i => ` ${i.file}:${i.line}: ${i.issue}`);
|
|
114
|
+
return {
|
|
115
|
+
name: 'docs',
|
|
116
|
+
status: 'fail',
|
|
117
|
+
duration: Date.now() - start,
|
|
118
|
+
message: `${allIssues.length} broken reference(s) in ${docFiles.length} file(s)`,
|
|
119
|
+
details,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
name: 'docs',
|
|
124
|
+
status: 'pass',
|
|
125
|
+
duration: Date.now() - start,
|
|
126
|
+
message: `${docFiles.length} documentation file(s) verified`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return {
|
|
131
|
+
name: 'docs',
|
|
132
|
+
status: 'fail',
|
|
133
|
+
duration: Date.now() - start,
|
|
134
|
+
message: error instanceof Error ? error.message : String(error),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/guardrails/docs.ts"],"names":[],"mappings":"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"}
|