specshield 3.4.8 → 3.4.9
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/package.json +1 -1
- package/src/commands/compare.js +27 -2
- package/src/core/remoteUrl.js +20 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specshield",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.9",
|
|
4
4
|
"description": "Contract compatibility testing for APIs — catch breaking OpenAPI changes before they reach your consumers, with can-i-deploy deploy gating and GitHub PR checks. (a.k.a. bidirectional contract testing.)",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"homepage": "https://specshield.io",
|
package/src/commands/compare.js
CHANGED
|
@@ -12,6 +12,7 @@ const { classifyChanges, filterBySeverity } = require('../core/classifyChanges')
|
|
|
12
12
|
const { formatHuman, formatJson } = require('../core/outputFormatter');
|
|
13
13
|
const { loadConfig } = require('../core/configLoader');
|
|
14
14
|
const { resolveExitCode } = require('../core/exitCode');
|
|
15
|
+
const { resolveRemoteUrl } = require('../core/remoteUrl');
|
|
15
16
|
const { recordCompareAndMaybeRender } = require('../core/conversionPrompt');
|
|
16
17
|
const logger = require('../utils/logger');
|
|
17
18
|
const exitAfterFlush = require('../utils/exitAfterFlush');
|
|
@@ -168,7 +169,7 @@ async function runRemoteComparison(base, target, options, spinner) {
|
|
|
168
169
|
const baseRaw = await loadSpec(base);
|
|
169
170
|
const targetRaw = await loadSpec(target);
|
|
170
171
|
|
|
171
|
-
const url = options.remoteUrl
|
|
172
|
+
const url = resolveRemoteUrl(options.remoteUrl, HOSTED_API_URL);
|
|
172
173
|
const timeout = parseInt(options.timeout, 10) || 10000;
|
|
173
174
|
|
|
174
175
|
if (spinner) spinner.text = `Sending to hosted API...`;
|
|
@@ -186,7 +187,7 @@ async function runRemoteComparison(base, target, options, spinner) {
|
|
|
186
187
|
{ baseSpec: baseRaw, targetSpec: targetRaw },
|
|
187
188
|
{ timeout, headers }
|
|
188
189
|
);
|
|
189
|
-
return response.data;
|
|
190
|
+
return normalizeRemoteResult(response.data, url);
|
|
190
191
|
} catch (err) {
|
|
191
192
|
const msg = err.response
|
|
192
193
|
? `Remote API error ${err.response.status}: ${JSON.stringify(err.response.data)}`
|
|
@@ -195,6 +196,30 @@ async function runRemoteComparison(base, target, options, spinner) {
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
/**
|
|
200
|
+
* A remote response is untrusted input. Without this, a proxy error page or a
|
|
201
|
+
* redirect to the marketing site arrives as a 200 with an HTML body, and the
|
|
202
|
+
* first `.filter` downstream throws an unreadable TypeError at the user.
|
|
203
|
+
*/
|
|
204
|
+
function normalizeRemoteResult(data, url) {
|
|
205
|
+
if (!data || typeof data !== 'object' || !Array.isArray(data.breakingChanges)) {
|
|
206
|
+
const gotHtml = typeof data === 'string' && /<html|<!doctype/i.test(data);
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Unexpected response from ${url}` +
|
|
209
|
+
(gotHtml
|
|
210
|
+
? ' — that URL returned a web page, not the compare API. Pass the server base URL, e.g. --remote-url https://specshield.io'
|
|
211
|
+
: ' — the response was not a comparison result.')
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
// Downstream steps assume all four arrays exist.
|
|
215
|
+
return {
|
|
216
|
+
...data,
|
|
217
|
+
additions: data.additions || [],
|
|
218
|
+
modifications: data.modifications || [],
|
|
219
|
+
warnings: data.warnings || [],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
198
223
|
function applyIgnoreList(result, ignoreList) {
|
|
199
224
|
if (!ignoreList || ignoreList.length === 0) return result;
|
|
200
225
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the hosted compare endpoint from a base URL.
|
|
5
|
+
*
|
|
6
|
+
* `--remote-url` is documented as "Override the hosted API base URL", but the
|
|
7
|
+
* value used to be passed to axios verbatim — so `--remote-url https://host`
|
|
8
|
+
* POSTed to the site root instead of /compare. The root answers with a web page
|
|
9
|
+
* or a 405, and the CLI then died on `Cannot read properties of undefined
|
|
10
|
+
* (reading 'filter')` while trying to read breakingChanges off HTML.
|
|
11
|
+
*
|
|
12
|
+
* A URL that already ends in /compare is returned unchanged, so anyone who
|
|
13
|
+
* worked around the old behaviour by passing the full endpoint keeps working.
|
|
14
|
+
*/
|
|
15
|
+
function resolveRemoteUrl(remoteUrl, hostedDefault) {
|
|
16
|
+
const base = String(remoteUrl || hostedDefault).trim().replace(/\/+$/, '');
|
|
17
|
+
return /\/compare$/i.test(base) ? base : base + '/compare';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { resolveRemoteUrl };
|