coderifts 1.5.0 → 1.7.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 +20 -6
- package/dist/cli.js +91858 -0
- package/package.json +21 -8
- package/bin/coderifts.js +0 -45
- package/src/cloud.js +0 -51
- package/src/commands/diff.js +0 -237
- package/src/commands/init.js +0 -223
- package/src/commands/login.js +0 -38
- package/src/config.js +0 -65
- package/src/output/json.js +0 -10
- package/src/output/terminal.js +0 -156
package/package.json
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coderifts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
5
5
|
"author": "CodeRifts <hello@coderifts.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
|
-
"coderifts
|
|
8
|
+
"coderifts": "./dist/cli.js"
|
|
9
9
|
},
|
|
10
|
-
"main": "
|
|
10
|
+
"main": "dist/cli.js",
|
|
11
11
|
"files": [
|
|
12
|
-
"
|
|
13
|
-
"src/",
|
|
12
|
+
"dist/",
|
|
14
13
|
"README.md"
|
|
15
14
|
],
|
|
16
15
|
"keywords": [
|
|
@@ -21,7 +20,8 @@
|
|
|
21
20
|
"governance",
|
|
22
21
|
"cli",
|
|
23
22
|
"ci-cd",
|
|
24
|
-
"swagger"
|
|
23
|
+
"swagger",
|
|
24
|
+
"devtools"
|
|
25
25
|
],
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
@@ -31,7 +31,13 @@
|
|
|
31
31
|
"homepage": "https://coderifts.com",
|
|
32
32
|
"bugs": "https://github.com/coderifts/app/issues",
|
|
33
33
|
"engines": {
|
|
34
|
-
"node": ">=
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "esbuild bin/coderifts.js --bundle --platform=node --target=node18 --outfile=dist/cli.js",
|
|
38
|
+
"prepublishOnly": "npm run build",
|
|
39
|
+
"postinstall": "node scripts/postinstall.js",
|
|
40
|
+
"test": "node --test test/*.test.js"
|
|
35
41
|
},
|
|
36
42
|
"dependencies": {
|
|
37
43
|
"chalk": "^4.1.2",
|
|
@@ -39,7 +45,14 @@
|
|
|
39
45
|
"commander": "^12.0.0",
|
|
40
46
|
"inquirer": "^8.2.6",
|
|
41
47
|
"js-yaml": "^4.1.0",
|
|
42
|
-
"openapi-diff": "^0.
|
|
48
|
+
"openapi-diff": "^0.24.1",
|
|
43
49
|
"ora": "^5.4.1"
|
|
50
|
+
},
|
|
51
|
+
"overrides": {
|
|
52
|
+
"z-schema": "^12.0.2",
|
|
53
|
+
"json-schema-ref-parser": "npm:@apidevtools/json-schema-ref-parser@^11.7.3"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"esbuild": "^0.27.3"
|
|
44
57
|
}
|
|
45
58
|
}
|
package/bin/coderifts.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const { program } = require('commander');
|
|
6
|
-
const pkg = require('../package.json');
|
|
7
|
-
|
|
8
|
-
program
|
|
9
|
-
.name('coderifts')
|
|
10
|
-
.description('Detect breaking API changes between OpenAPI specs')
|
|
11
|
-
.version(pkg.version, '-v, --version');
|
|
12
|
-
|
|
13
|
-
// ── diff command ──
|
|
14
|
-
program
|
|
15
|
-
.command('diff <old-spec> <new-spec>')
|
|
16
|
-
.description('Compare two OpenAPI specs and report breaking changes')
|
|
17
|
-
.option('-f, --format <format>', 'Output format: terminal (default), json, markdown', 'terminal')
|
|
18
|
-
.option('--ci', 'CI mode — exit with code 1 if breaking changes exceed threshold')
|
|
19
|
-
.option('--threshold <number>', 'Risk score threshold for CI mode (0-100)', '50')
|
|
20
|
-
.option('--cloud', 'Use the CodeRifts cloud API instead of local analysis')
|
|
21
|
-
.option('-c, --config <path>', 'Path to .coderifts.yml config file')
|
|
22
|
-
.action(async (oldSpec, newSpec, options) => {
|
|
23
|
-
const { diff } = require('../src/commands/diff');
|
|
24
|
-
await diff(oldSpec, newSpec, options);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// ── init command ──
|
|
28
|
-
program
|
|
29
|
-
.command('init')
|
|
30
|
-
.description('Generate a .coderifts.yml configuration file')
|
|
31
|
-
.action(async () => {
|
|
32
|
-
const { init } = require('../src/commands/init');
|
|
33
|
-
await init();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// ── login command ──
|
|
37
|
-
program
|
|
38
|
-
.command('login')
|
|
39
|
-
.description('Save your API key for cloud features')
|
|
40
|
-
.action(async () => {
|
|
41
|
-
const { login } = require('../src/commands/login');
|
|
42
|
-
await login();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
program.parse();
|
package/src/cloud.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const https = require('https');
|
|
4
|
-
|
|
5
|
-
const API_BASE = 'https://app.coderifts.com';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Send specs to the CodeRifts cloud API for analysis.
|
|
9
|
-
*/
|
|
10
|
-
function cloudDiff(oldSpec, newSpec, apiKey) {
|
|
11
|
-
return new Promise((resolve, reject) => {
|
|
12
|
-
const body = JSON.stringify({ old_spec: oldSpec, new_spec: newSpec });
|
|
13
|
-
|
|
14
|
-
const url = new URL('/api/v1/diff', API_BASE);
|
|
15
|
-
const options = {
|
|
16
|
-
hostname: url.hostname,
|
|
17
|
-
port: 443,
|
|
18
|
-
path: url.pathname,
|
|
19
|
-
method: 'POST',
|
|
20
|
-
headers: {
|
|
21
|
-
'Content-Type': 'application/json',
|
|
22
|
-
'Content-Length': Buffer.byteLength(body),
|
|
23
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
24
|
-
'User-Agent': '@coderifts/cli',
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const req = https.request(options, (res) => {
|
|
29
|
-
let data = '';
|
|
30
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
31
|
-
res.on('end', () => {
|
|
32
|
-
try {
|
|
33
|
-
const parsed = JSON.parse(data);
|
|
34
|
-
if (res.statusCode >= 400) {
|
|
35
|
-
reject(new Error(parsed.message || parsed.error || `HTTP ${res.statusCode}`));
|
|
36
|
-
} else {
|
|
37
|
-
resolve(parsed);
|
|
38
|
-
}
|
|
39
|
-
} catch {
|
|
40
|
-
reject(new Error(`Invalid JSON response (HTTP ${res.statusCode})`));
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
req.on('error', reject);
|
|
46
|
-
req.write(body);
|
|
47
|
-
req.end();
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = { cloudDiff };
|
package/src/commands/diff.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
const ora = require('ora');
|
|
7
|
-
const yaml = require('js-yaml');
|
|
8
|
-
const { loadProjectConfig, getApiKey } = require('../config');
|
|
9
|
-
const { cloudDiff } = require('../cloud');
|
|
10
|
-
const { renderTerminal } = require('../output/terminal');
|
|
11
|
-
const { renderJson } = require('../output/json');
|
|
12
|
-
|
|
13
|
-
// Respect NO_COLOR
|
|
14
|
-
if (process.env.NO_COLOR) chalk.level = 0;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Read and parse an OpenAPI spec file.
|
|
18
|
-
* Returns the raw string content (for cloud mode) and parsed object (for local mode).
|
|
19
|
-
*/
|
|
20
|
-
function readSpec(filePath) {
|
|
21
|
-
const resolved = path.resolve(filePath);
|
|
22
|
-
if (!fs.existsSync(resolved)) {
|
|
23
|
-
console.error(chalk.red(`Error: File not found: ${filePath}`));
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
const raw = fs.readFileSync(resolved, 'utf-8');
|
|
27
|
-
return raw;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Run local analysis using the bundled core analyzer.
|
|
32
|
-
*/
|
|
33
|
-
async function localAnalyze(oldSpecRaw, newSpecRaw, config) {
|
|
34
|
-
// Parse specs
|
|
35
|
-
let oldSpec, newSpec;
|
|
36
|
-
try {
|
|
37
|
-
oldSpec = yaml.load(oldSpecRaw);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
throw new Error(`Failed to parse old spec: ${e.message}`);
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
newSpec = yaml.load(newSpecRaw);
|
|
43
|
-
} catch (e) {
|
|
44
|
-
throw new Error(`Failed to parse new spec: ${e.message}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Use openapi-diff for the raw diff
|
|
48
|
-
const { diffSpecs } = require('openapi-diff');
|
|
49
|
-
|
|
50
|
-
// Convert specs to JSON strings for openapi-diff
|
|
51
|
-
const oldJson = JSON.stringify(oldSpec);
|
|
52
|
-
const newJson = JSON.stringify(newSpec);
|
|
53
|
-
|
|
54
|
-
let diffResult;
|
|
55
|
-
try {
|
|
56
|
-
diffResult = await diffSpecs({
|
|
57
|
-
sourceSpec: { content: oldJson, location: 'old-spec.json', format: 'openapi3' },
|
|
58
|
-
destinationSpec: { content: newJson, location: 'new-spec.json', format: 'openapi3' },
|
|
59
|
-
});
|
|
60
|
-
} catch (e) {
|
|
61
|
-
throw new Error(`Diff engine error: ${e.message}`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Classify breaking changes
|
|
65
|
-
const breakingChanges = [];
|
|
66
|
-
const nonBreakingChanges = [];
|
|
67
|
-
|
|
68
|
-
if (diffResult.breakingDifferences) {
|
|
69
|
-
for (const diff of diffResult.breakingDifferences) {
|
|
70
|
-
breakingChanges.push({
|
|
71
|
-
type: diff.code || 'unknown',
|
|
72
|
-
path: diff.sourceSpecEntityDetails?.[0]?.location || diff.entity || '',
|
|
73
|
-
method: '',
|
|
74
|
-
field: '',
|
|
75
|
-
severity: 'high',
|
|
76
|
-
description: diff.action || diff.code || '',
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (diffResult.nonBreakingDifferences) {
|
|
82
|
-
for (const diff of diffResult.nonBreakingDifferences) {
|
|
83
|
-
nonBreakingChanges.push({
|
|
84
|
-
type: diff.code || 'unknown',
|
|
85
|
-
path: diff.sourceSpecEntityDetails?.[0]?.location || diff.entity || '',
|
|
86
|
-
method: '',
|
|
87
|
-
field: '',
|
|
88
|
-
severity: 'low',
|
|
89
|
-
description: diff.action || diff.code || '',
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Calculate risk score
|
|
95
|
-
const breakingCount = breakingChanges.length;
|
|
96
|
-
const baseScore = Math.min(breakingCount * 15, 80);
|
|
97
|
-
const riskScore = Math.min(baseScore + (breakingCount > 3 ? 20 : 0), 100);
|
|
98
|
-
|
|
99
|
-
let riskLevel = 'minimal';
|
|
100
|
-
if (riskScore >= 80) riskLevel = 'critical';
|
|
101
|
-
else if (riskScore >= 60) riskLevel = 'high';
|
|
102
|
-
else if (riskScore >= 40) riskLevel = 'moderate';
|
|
103
|
-
else if (riskScore >= 20) riskLevel = 'low';
|
|
104
|
-
|
|
105
|
-
// Semver suggestion
|
|
106
|
-
let semverSuggestion = 'patch';
|
|
107
|
-
if (breakingCount > 0) semverSuggestion = 'major';
|
|
108
|
-
else if (nonBreakingChanges.length > 0) semverSuggestion = 'minor';
|
|
109
|
-
|
|
110
|
-
// Policy check
|
|
111
|
-
const policyViolations = [];
|
|
112
|
-
const blockOn = config?.rules?.block_on || [];
|
|
113
|
-
const threshold = config?.rules?.risk_threshold ?? 50;
|
|
114
|
-
|
|
115
|
-
for (const change of breakingChanges) {
|
|
116
|
-
for (const rule of blockOn) {
|
|
117
|
-
if (change.type.includes(rule) || change.description.includes(rule)) {
|
|
118
|
-
policyViolations.push({
|
|
119
|
-
rule: rule,
|
|
120
|
-
message: `Blocked by policy: ${rule} — ${change.path}`,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const shouldBlock = riskScore >= threshold || policyViolations.length > 0;
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
risk_score: riskScore,
|
|
130
|
-
risk_level: riskLevel,
|
|
131
|
-
risk_dimensions: {
|
|
132
|
-
revenue_impact: Math.min(breakingCount * 3, 10),
|
|
133
|
-
blast_radius: Math.min(breakingCount * 2, 10),
|
|
134
|
-
app_compatibility: Math.min(breakingCount * 2, 10),
|
|
135
|
-
security: 0,
|
|
136
|
-
},
|
|
137
|
-
semver_suggestion: semverSuggestion,
|
|
138
|
-
breaking_changes: breakingChanges,
|
|
139
|
-
non_breaking_changes: nonBreakingChanges,
|
|
140
|
-
security_findings: [],
|
|
141
|
-
changelog: {
|
|
142
|
-
breaking: breakingChanges.map(c => `**${c.type}** ${c.path}`),
|
|
143
|
-
added: [],
|
|
144
|
-
changed: nonBreakingChanges.map(c => `${c.type} ${c.path}`),
|
|
145
|
-
deprecated: [],
|
|
146
|
-
},
|
|
147
|
-
policy_violations: policyViolations,
|
|
148
|
-
should_block: shouldBlock,
|
|
149
|
-
stats: {
|
|
150
|
-
total_changes: breakingCount + nonBreakingChanges.length,
|
|
151
|
-
breaking_count: breakingCount,
|
|
152
|
-
non_breaking_count: nonBreakingChanges.length,
|
|
153
|
-
security_count: 0,
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Main diff command handler.
|
|
160
|
-
*/
|
|
161
|
-
async function diff(oldSpecPath, newSpecPath, options) {
|
|
162
|
-
const spinner = ora('Analyzing specs...').start();
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
// Read specs
|
|
166
|
-
const oldSpecRaw = readSpec(oldSpecPath);
|
|
167
|
-
const newSpecRaw = readSpec(newSpecPath);
|
|
168
|
-
|
|
169
|
-
// Load config
|
|
170
|
-
const projectConfig = loadProjectConfig(options.config);
|
|
171
|
-
|
|
172
|
-
// Determine mode: cloud or local
|
|
173
|
-
const useCloud = options.cloud || false;
|
|
174
|
-
const apiKey = getApiKey();
|
|
175
|
-
|
|
176
|
-
let result;
|
|
177
|
-
|
|
178
|
-
if (useCloud) {
|
|
179
|
-
if (!apiKey) {
|
|
180
|
-
spinner.fail('Cloud mode requires an API key. Run `coderifts login` first.');
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
183
|
-
spinner.text = 'Sending to CodeRifts cloud API...';
|
|
184
|
-
result = await cloudDiff(oldSpecRaw, newSpecRaw, apiKey);
|
|
185
|
-
} else {
|
|
186
|
-
spinner.text = 'Running local analysis...';
|
|
187
|
-
result = await localAnalyze(oldSpecRaw, newSpecRaw, projectConfig);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
spinner.stop();
|
|
191
|
-
|
|
192
|
-
// Output based on format
|
|
193
|
-
const format = options.format || 'terminal';
|
|
194
|
-
|
|
195
|
-
if (format === 'json') {
|
|
196
|
-
console.log(renderJson(result));
|
|
197
|
-
} else if (format === 'markdown') {
|
|
198
|
-
// Simple markdown output
|
|
199
|
-
console.log(`# CodeRifts Report\n`);
|
|
200
|
-
console.log(`**Risk Score:** ${result.risk_score}/100 (${result.risk_level})`);
|
|
201
|
-
console.log(`**Semver:** ${result.semver_suggestion}`);
|
|
202
|
-
console.log(`**Breaking Changes:** ${result.stats?.breaking_count || 0}`);
|
|
203
|
-
console.log(`**Non-Breaking:** ${result.stats?.non_breaking_count || 0}\n`);
|
|
204
|
-
if (result.breaking_changes?.length > 0) {
|
|
205
|
-
console.log('## Breaking Changes\n');
|
|
206
|
-
console.log('| Type | Path | Severity |');
|
|
207
|
-
console.log('|------|------|----------|');
|
|
208
|
-
for (const c of result.breaking_changes) {
|
|
209
|
-
console.log(`| ${c.type} | ${c.path} | ${c.severity} |`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
console.log(renderTerminal(result));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Save JSON report
|
|
217
|
-
const reportPath = path.join(process.cwd(), 'coderifts-report.json');
|
|
218
|
-
fs.writeFileSync(reportPath, JSON.stringify(result, null, 2) + '\n');
|
|
219
|
-
if (format === 'terminal') {
|
|
220
|
-
console.log(chalk.dim(` Full report: ${reportPath} (saved)`));
|
|
221
|
-
console.log('');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// CI mode: exit with code 1 if should_block
|
|
225
|
-
if (options.ci) {
|
|
226
|
-
const threshold = parseInt(options.threshold, 10) || 50;
|
|
227
|
-
if (result.risk_score >= threshold || result.should_block) {
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
} catch (err) {
|
|
232
|
-
spinner.fail(`Analysis failed: ${err.message}`);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
module.exports = { diff };
|
package/src/commands/init.js
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
const inquirer = require('inquirer');
|
|
7
|
-
|
|
8
|
-
if (process.env.NO_COLOR) chalk.level = 0;
|
|
9
|
-
|
|
10
|
-
const TEMPLATES = {
|
|
11
|
-
'Default (recommended)': `# CodeRifts Configuration — Recommended Default
|
|
12
|
-
version: 1
|
|
13
|
-
|
|
14
|
-
rules:
|
|
15
|
-
block_on:
|
|
16
|
-
- removed-endpoint
|
|
17
|
-
- auth-removed
|
|
18
|
-
|
|
19
|
-
risk_threshold: 50
|
|
20
|
-
|
|
21
|
-
semver:
|
|
22
|
-
enforce: false # Suggest but don't block
|
|
23
|
-
|
|
24
|
-
changelog:
|
|
25
|
-
auto_generate: true
|
|
26
|
-
`,
|
|
27
|
-
|
|
28
|
-
'Fintech / Payments': `# CodeRifts Configuration — Fintech / Payment APIs
|
|
29
|
-
version: 1
|
|
30
|
-
preset: fintech
|
|
31
|
-
|
|
32
|
-
rules:
|
|
33
|
-
block_on:
|
|
34
|
-
- removed-endpoint
|
|
35
|
-
- required-field-added
|
|
36
|
-
- field-type-changed
|
|
37
|
-
- auth-scope-removed
|
|
38
|
-
|
|
39
|
-
protected_fields:
|
|
40
|
-
- amount
|
|
41
|
-
- currency
|
|
42
|
-
- transaction_id
|
|
43
|
-
- payment_method
|
|
44
|
-
- card_number
|
|
45
|
-
- account_id
|
|
46
|
-
- routing_number
|
|
47
|
-
- iban
|
|
48
|
-
- swift_code
|
|
49
|
-
|
|
50
|
-
security:
|
|
51
|
-
sensitive_fields:
|
|
52
|
-
- card_number
|
|
53
|
-
- cvv
|
|
54
|
-
- pin
|
|
55
|
-
- account_number
|
|
56
|
-
- ssn
|
|
57
|
-
- tax_id
|
|
58
|
-
alert_on_exposure: true
|
|
59
|
-
|
|
60
|
-
risk_threshold: 30
|
|
61
|
-
|
|
62
|
-
semver:
|
|
63
|
-
enforce: true
|
|
64
|
-
block_on_missing_bump: true
|
|
65
|
-
|
|
66
|
-
deprecation:
|
|
67
|
-
require_sunset_header: true
|
|
68
|
-
minimum_deprecation_period_days: 90
|
|
69
|
-
`,
|
|
70
|
-
|
|
71
|
-
'Healthcare / HIPAA': `# CodeRifts Configuration — Healthcare / HIPAA
|
|
72
|
-
version: 1
|
|
73
|
-
preset: healthcare
|
|
74
|
-
|
|
75
|
-
rules:
|
|
76
|
-
block_on:
|
|
77
|
-
- removed-endpoint
|
|
78
|
-
- required-field-added
|
|
79
|
-
- field-type-changed
|
|
80
|
-
- auth-scope-removed
|
|
81
|
-
- auth-removed
|
|
82
|
-
|
|
83
|
-
protected_fields:
|
|
84
|
-
- patient_id
|
|
85
|
-
- medical_record_number
|
|
86
|
-
- diagnosis_code
|
|
87
|
-
- provider_id
|
|
88
|
-
- insurance_id
|
|
89
|
-
- date_of_birth
|
|
90
|
-
- prescription_id
|
|
91
|
-
|
|
92
|
-
security:
|
|
93
|
-
sensitive_fields:
|
|
94
|
-
- ssn
|
|
95
|
-
- date_of_birth
|
|
96
|
-
- medical_record_number
|
|
97
|
-
- diagnosis
|
|
98
|
-
- medication
|
|
99
|
-
- insurance_num
|
|
100
|
-
alert_on_exposure: true
|
|
101
|
-
require_auth_on_phi_endpoints: true
|
|
102
|
-
|
|
103
|
-
risk_threshold: 20
|
|
104
|
-
|
|
105
|
-
semver:
|
|
106
|
-
enforce: true
|
|
107
|
-
block_on_missing_bump: true
|
|
108
|
-
|
|
109
|
-
deprecation:
|
|
110
|
-
require_sunset_header: true
|
|
111
|
-
minimum_deprecation_period_days: 180
|
|
112
|
-
`,
|
|
113
|
-
|
|
114
|
-
'Platform / API-First': `# CodeRifts Configuration — Platform / API-First Teams
|
|
115
|
-
version: 1
|
|
116
|
-
preset: platform
|
|
117
|
-
|
|
118
|
-
rules:
|
|
119
|
-
block_on:
|
|
120
|
-
- removed-endpoint
|
|
121
|
-
- required-field-added
|
|
122
|
-
- field-type-changed
|
|
123
|
-
- response-field-removed
|
|
124
|
-
|
|
125
|
-
semver:
|
|
126
|
-
enforce: true
|
|
127
|
-
block_on_missing_bump: true
|
|
128
|
-
auto_label_pr: true
|
|
129
|
-
|
|
130
|
-
deprecation:
|
|
131
|
-
require_sunset_header: true
|
|
132
|
-
require_deprecation_notice: true
|
|
133
|
-
minimum_deprecation_period_days: 60
|
|
134
|
-
|
|
135
|
-
changelog:
|
|
136
|
-
auto_generate: true
|
|
137
|
-
format: keep-a-changelog
|
|
138
|
-
|
|
139
|
-
risk_threshold: 40
|
|
140
|
-
|
|
141
|
-
governance:
|
|
142
|
-
require_review_on_breaking: true
|
|
143
|
-
breaking_change_label: "api:breaking"
|
|
144
|
-
notify_channels: []
|
|
145
|
-
`,
|
|
146
|
-
|
|
147
|
-
'E-commerce': `# CodeRifts Configuration — E-commerce
|
|
148
|
-
version: 1
|
|
149
|
-
preset: ecommerce
|
|
150
|
-
|
|
151
|
-
rules:
|
|
152
|
-
block_on:
|
|
153
|
-
- removed-endpoint
|
|
154
|
-
- required-field-added
|
|
155
|
-
- field-type-changed
|
|
156
|
-
|
|
157
|
-
protected_fields:
|
|
158
|
-
- product_id
|
|
159
|
-
- sku
|
|
160
|
-
- price
|
|
161
|
-
- inventory_count
|
|
162
|
-
- cart_id
|
|
163
|
-
- order_id
|
|
164
|
-
- checkout_session
|
|
165
|
-
- shipping_address
|
|
166
|
-
- tracking_number
|
|
167
|
-
|
|
168
|
-
security:
|
|
169
|
-
sensitive_fields:
|
|
170
|
-
- card_number
|
|
171
|
-
- billing_address
|
|
172
|
-
- email
|
|
173
|
-
- phone
|
|
174
|
-
alert_on_exposure: true
|
|
175
|
-
|
|
176
|
-
risk_threshold: 35
|
|
177
|
-
|
|
178
|
-
semver:
|
|
179
|
-
enforce: true
|
|
180
|
-
|
|
181
|
-
deprecation:
|
|
182
|
-
minimum_deprecation_period_days: 30
|
|
183
|
-
`,
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
async function init() {
|
|
187
|
-
console.log('');
|
|
188
|
-
console.log(chalk.bold(' CodeRifts — Configuration Generator'));
|
|
189
|
-
console.log('');
|
|
190
|
-
|
|
191
|
-
const outputPath = path.join(process.cwd(), '.coderifts.yml');
|
|
192
|
-
|
|
193
|
-
// Check if config already exists
|
|
194
|
-
if (fs.existsSync(outputPath)) {
|
|
195
|
-
const { overwrite } = await inquirer.prompt([{
|
|
196
|
-
type: 'confirm',
|
|
197
|
-
name: 'overwrite',
|
|
198
|
-
message: '.coderifts.yml already exists. Overwrite?',
|
|
199
|
-
default: false,
|
|
200
|
-
}]);
|
|
201
|
-
if (!overwrite) {
|
|
202
|
-
console.log(chalk.dim(' Cancelled.'));
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const { industry } = await inquirer.prompt([{
|
|
208
|
-
type: 'list',
|
|
209
|
-
name: 'industry',
|
|
210
|
-
message: 'Select your industry:',
|
|
211
|
-
choices: Object.keys(TEMPLATES),
|
|
212
|
-
}]);
|
|
213
|
-
|
|
214
|
-
const template = TEMPLATES[industry];
|
|
215
|
-
fs.writeFileSync(outputPath, template);
|
|
216
|
-
|
|
217
|
-
console.log('');
|
|
218
|
-
console.log(chalk.green(` ✓ Created .coderifts.yml with ${industry.toLowerCase()} preset`));
|
|
219
|
-
console.log(chalk.dim(` ${outputPath}`));
|
|
220
|
-
console.log('');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
module.exports = { init };
|
package/src/commands/login.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
const inquirer = require('inquirer');
|
|
5
|
-
const { saveUserConfig, loadUserConfig, CONFIG_FILE } = require('../config');
|
|
6
|
-
|
|
7
|
-
if (process.env.NO_COLOR) chalk.level = 0;
|
|
8
|
-
|
|
9
|
-
async function login() {
|
|
10
|
-
console.log('');
|
|
11
|
-
console.log(chalk.bold(' CodeRifts — API Key Setup'));
|
|
12
|
-
console.log('');
|
|
13
|
-
console.log(chalk.dim(' Get a free API key at: https://app.coderifts.com/api/signup'));
|
|
14
|
-
console.log('');
|
|
15
|
-
|
|
16
|
-
const { apiKey } = await inquirer.prompt([{
|
|
17
|
-
type: 'password',
|
|
18
|
-
name: 'apiKey',
|
|
19
|
-
message: 'Paste your API key:',
|
|
20
|
-
mask: '*',
|
|
21
|
-
validate: (input) => {
|
|
22
|
-
if (!input || input.trim().length === 0) return 'API key is required';
|
|
23
|
-
if (!input.startsWith('cr_live_')) return 'Invalid key format — keys start with cr_live_';
|
|
24
|
-
return true;
|
|
25
|
-
},
|
|
26
|
-
}]);
|
|
27
|
-
|
|
28
|
-
const config = loadUserConfig();
|
|
29
|
-
config.api_key = apiKey.trim();
|
|
30
|
-
saveUserConfig(config);
|
|
31
|
-
|
|
32
|
-
console.log('');
|
|
33
|
-
console.log(chalk.green(` ✓ API key saved to ${CONFIG_FILE}`));
|
|
34
|
-
console.log(chalk.dim(' Use --cloud flag with diff command to use the cloud API.'));
|
|
35
|
-
console.log('');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = { login };
|