norn-cli 1.6.0 → 1.6.2
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/AGENTS.md +9 -1
- package/CHANGELOG.md +23 -0
- package/dist/cli.js +246 -80
- package/package.json +1 -1
- package/out/assertionRunner.js +0 -537
- package/out/chatParticipant.js +0 -722
- package/out/cli/colors.js +0 -129
- package/out/cli/formatters/assertion.js +0 -75
- package/out/cli/formatters/index.js +0 -23
- package/out/cli/formatters/response.js +0 -106
- package/out/cli/formatters/summary.js +0 -187
- package/out/cli/redaction.js +0 -237
- package/out/cli/reporters/html.js +0 -634
- package/out/cli/reporters/index.js +0 -22
- package/out/cli/reporters/junit.js +0 -211
- package/out/cli.js +0 -989
- package/out/codeLensProvider.js +0 -248
- package/out/compareContentProvider.js +0 -85
- package/out/completionProvider.js +0 -2404
- package/out/contractDecorationProvider.js +0 -243
- package/out/coverageCalculator.js +0 -837
- package/out/coveragePanel.js +0 -545
- package/out/diagnosticProvider.js +0 -1113
- package/out/environmentProvider.js +0 -442
- package/out/extension.js +0 -1114
- package/out/httpClient.js +0 -269
- package/out/jsonFileReader.js +0 -320
- package/out/nornPrompt.js +0 -580
- package/out/nornapiParser.js +0 -326
- package/out/parser.js +0 -725
- package/out/responsePanel.js +0 -4674
- package/out/schemaGenerator.js +0 -393
- package/out/scriptRunner.js +0 -419
- package/out/sequenceRunner.js +0 -3046
- package/out/swaggerBodyIntellisenseCache.js +0 -147
- package/out/swaggerParser.js +0 -419
- package/out/test/coverageCalculator.test.js +0 -100
- package/out/test/extension.test.js +0 -48
- package/out/testProvider.js +0 -658
- package/out/validationCache.js +0 -245
package/out/schemaGenerator.js
DELETED
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Schema Generator - Generates JSON Schema from response bodies
|
|
4
|
-
* and validates data against JSON Schema
|
|
5
|
-
*/
|
|
6
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
-
if (k2 === undefined) k2 = k;
|
|
8
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
-
}
|
|
12
|
-
Object.defineProperty(o, k2, desc);
|
|
13
|
-
}) : (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
o[k2] = m[k];
|
|
16
|
-
}));
|
|
17
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
-
}) : function(o, v) {
|
|
20
|
-
o["default"] = v;
|
|
21
|
-
});
|
|
22
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
-
var ownKeys = function(o) {
|
|
24
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
-
var ar = [];
|
|
26
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
return ownKeys(o);
|
|
30
|
-
};
|
|
31
|
-
return function (mod) {
|
|
32
|
-
if (mod && mod.__esModule) return mod;
|
|
33
|
-
var result = {};
|
|
34
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
-
__setModuleDefault(result, mod);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
})();
|
|
39
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
-
};
|
|
42
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.resolveSchemaPath = resolveSchemaPath;
|
|
44
|
-
exports.generateJsonSchema = generateJsonSchema;
|
|
45
|
-
exports.validateAgainstSchemaDetailed = validateAgainstSchemaDetailed;
|
|
46
|
-
exports.validateAgainstSchema = validateAgainstSchema;
|
|
47
|
-
exports.generateSchemaFileName = generateSchemaFileName;
|
|
48
|
-
exports.saveSchema = saveSchema;
|
|
49
|
-
const ajv_1 = __importDefault(require("ajv"));
|
|
50
|
-
const fs = __importStar(require("fs"));
|
|
51
|
-
const path = __importStar(require("path"));
|
|
52
|
-
const ajv = new ajv_1.default({ allErrors: true, strict: false });
|
|
53
|
-
/**
|
|
54
|
-
* Path alias prefix - @/ resolves to contracts/ folder
|
|
55
|
-
*/
|
|
56
|
-
const PATH_ALIAS_PREFIX = '@/';
|
|
57
|
-
const DEFAULT_CONTRACTS_FOLDER = 'contracts';
|
|
58
|
-
/**
|
|
59
|
-
* Resolve a schema path, handling the @/ alias for contracts folder
|
|
60
|
-
* @param schemaPath The original schema path (may have @/ prefix)
|
|
61
|
-
* @param basePath Base path for relative paths (usually the source file's directory)
|
|
62
|
-
* @param workspaceRoot Optional workspace root for @/ alias resolution
|
|
63
|
-
* @returns Resolved absolute path
|
|
64
|
-
*/
|
|
65
|
-
function resolveSchemaPath(schemaPath, basePath, workspaceRoot) {
|
|
66
|
-
// Handle @/ alias
|
|
67
|
-
if (schemaPath.startsWith(PATH_ALIAS_PREFIX)) {
|
|
68
|
-
const relativePath = schemaPath.slice(PATH_ALIAS_PREFIX.length);
|
|
69
|
-
// First try workspace root, then fall back to basePath traversal
|
|
70
|
-
if (workspaceRoot) {
|
|
71
|
-
return path.resolve(workspaceRoot, DEFAULT_CONTRACTS_FOLDER, relativePath);
|
|
72
|
-
}
|
|
73
|
-
// If no workspace root, try to find contracts folder by walking up
|
|
74
|
-
if (basePath) {
|
|
75
|
-
let currentDir = basePath;
|
|
76
|
-
let attempts = 0;
|
|
77
|
-
while (attempts < 10) {
|
|
78
|
-
const contractsPath = path.join(currentDir, DEFAULT_CONTRACTS_FOLDER, relativePath);
|
|
79
|
-
if (fs.existsSync(path.dirname(contractsPath))) {
|
|
80
|
-
return contractsPath;
|
|
81
|
-
}
|
|
82
|
-
const parentDir = path.dirname(currentDir);
|
|
83
|
-
if (parentDir === currentDir)
|
|
84
|
-
break;
|
|
85
|
-
currentDir = parentDir;
|
|
86
|
-
attempts++;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// Fallback: just use contracts/ relative to basePath
|
|
90
|
-
if (basePath) {
|
|
91
|
-
return path.resolve(basePath, DEFAULT_CONTRACTS_FOLDER, relativePath);
|
|
92
|
-
}
|
|
93
|
-
return schemaPath; // Can't resolve
|
|
94
|
-
}
|
|
95
|
-
// Regular relative/absolute path handling
|
|
96
|
-
if (path.isAbsolute(schemaPath)) {
|
|
97
|
-
return schemaPath;
|
|
98
|
-
}
|
|
99
|
-
if (basePath) {
|
|
100
|
-
return path.resolve(basePath, schemaPath);
|
|
101
|
-
}
|
|
102
|
-
return schemaPath;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Generate a JSON Schema (draft-07) from a sample value
|
|
106
|
-
*/
|
|
107
|
-
function generateJsonSchema(value, title) {
|
|
108
|
-
const schema = {
|
|
109
|
-
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
110
|
-
};
|
|
111
|
-
if (title) {
|
|
112
|
-
schema.title = title;
|
|
113
|
-
}
|
|
114
|
-
const typeSchema = inferType(value);
|
|
115
|
-
Object.assign(schema, typeSchema);
|
|
116
|
-
return schema;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Infer JSON Schema type information from a value
|
|
120
|
-
*/
|
|
121
|
-
function inferType(value) {
|
|
122
|
-
if (value === null) {
|
|
123
|
-
return { type: 'null' };
|
|
124
|
-
}
|
|
125
|
-
if (Array.isArray(value)) {
|
|
126
|
-
if (value.length === 0) {
|
|
127
|
-
return { type: 'array', items: {} };
|
|
128
|
-
}
|
|
129
|
-
// Infer from first item (could be enhanced to merge schemas)
|
|
130
|
-
return {
|
|
131
|
-
type: 'array',
|
|
132
|
-
items: inferType(value[0])
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
if (typeof value === 'object') {
|
|
136
|
-
const properties = {};
|
|
137
|
-
const required = [];
|
|
138
|
-
for (const key of Object.keys(value)) {
|
|
139
|
-
properties[key] = inferType(value[key]);
|
|
140
|
-
// Mark all properties as required by default
|
|
141
|
-
required.push(key);
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
type: 'object',
|
|
145
|
-
properties,
|
|
146
|
-
required: required.length > 0 ? required : undefined,
|
|
147
|
-
additionalProperties: true
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
if (typeof value === 'string') {
|
|
151
|
-
return { type: 'string' };
|
|
152
|
-
}
|
|
153
|
-
if (typeof value === 'number') {
|
|
154
|
-
return Number.isInteger(value) ? { type: 'integer' } : { type: 'number' };
|
|
155
|
-
}
|
|
156
|
-
if (typeof value === 'boolean') {
|
|
157
|
-
return { type: 'boolean' };
|
|
158
|
-
}
|
|
159
|
-
return {};
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Convert Ajv ErrorObject to rich SchemaValidationError
|
|
163
|
-
*/
|
|
164
|
-
function convertAjvError(err, value) {
|
|
165
|
-
const instancePath = err.instancePath || '(root)';
|
|
166
|
-
// Determine expected value based on keyword
|
|
167
|
-
let expected;
|
|
168
|
-
let actual = undefined;
|
|
169
|
-
switch (err.keyword) {
|
|
170
|
-
case 'type':
|
|
171
|
-
expected = err.params.type;
|
|
172
|
-
actual = getValueAtPath(value, instancePath);
|
|
173
|
-
break;
|
|
174
|
-
case 'required':
|
|
175
|
-
expected = `property "${err.params.missingProperty}" to exist`;
|
|
176
|
-
break;
|
|
177
|
-
case 'enum':
|
|
178
|
-
expected = `one of: ${err.params.allowedValues?.join(', ')}`;
|
|
179
|
-
actual = getValueAtPath(value, instancePath);
|
|
180
|
-
break;
|
|
181
|
-
case 'pattern':
|
|
182
|
-
expected = `to match pattern "${err.params.pattern}"`;
|
|
183
|
-
actual = getValueAtPath(value, instancePath);
|
|
184
|
-
break;
|
|
185
|
-
case 'minLength':
|
|
186
|
-
expected = `length >= ${err.params.limit}`;
|
|
187
|
-
actual = getValueAtPath(value, instancePath);
|
|
188
|
-
break;
|
|
189
|
-
case 'maxLength':
|
|
190
|
-
expected = `length <= ${err.params.limit}`;
|
|
191
|
-
actual = getValueAtPath(value, instancePath);
|
|
192
|
-
break;
|
|
193
|
-
case 'minimum':
|
|
194
|
-
case 'exclusiveMinimum':
|
|
195
|
-
expected = `>= ${err.params.limit}`;
|
|
196
|
-
actual = getValueAtPath(value, instancePath);
|
|
197
|
-
break;
|
|
198
|
-
case 'maximum':
|
|
199
|
-
case 'exclusiveMaximum':
|
|
200
|
-
expected = `<= ${err.params.limit}`;
|
|
201
|
-
actual = getValueAtPath(value, instancePath);
|
|
202
|
-
break;
|
|
203
|
-
case 'additionalProperties':
|
|
204
|
-
expected = `no additional property "${err.params.additionalProperty}"`;
|
|
205
|
-
break;
|
|
206
|
-
case 'format':
|
|
207
|
-
expected = `format "${err.params.format}"`;
|
|
208
|
-
actual = getValueAtPath(value, instancePath);
|
|
209
|
-
break;
|
|
210
|
-
default:
|
|
211
|
-
if (err.params) {
|
|
212
|
-
expected = JSON.stringify(err.params);
|
|
213
|
-
}
|
|
214
|
-
actual = getValueAtPath(value, instancePath);
|
|
215
|
-
}
|
|
216
|
-
// Determine severity based on keyword
|
|
217
|
-
let severity = 'error';
|
|
218
|
-
if (err.keyword === 'additionalProperties') {
|
|
219
|
-
severity = 'warning'; // Extra properties are often okay
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
instancePath,
|
|
223
|
-
schemaPath: err.schemaPath,
|
|
224
|
-
keyword: err.keyword,
|
|
225
|
-
message: err.message || 'Validation failed',
|
|
226
|
-
params: err.params,
|
|
227
|
-
expected,
|
|
228
|
-
actual,
|
|
229
|
-
severity
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Get value at a JSON pointer path
|
|
234
|
-
*/
|
|
235
|
-
function getValueAtPath(obj, jsonPointer) {
|
|
236
|
-
if (jsonPointer === '(root)' || jsonPointer === '' || jsonPointer === '/') {
|
|
237
|
-
return obj;
|
|
238
|
-
}
|
|
239
|
-
// Remove leading slash and split
|
|
240
|
-
const parts = jsonPointer.replace(/^\//, '').split('/');
|
|
241
|
-
let current = obj;
|
|
242
|
-
for (const part of parts) {
|
|
243
|
-
if (current === null || current === undefined) {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
// Decode JSON Pointer escapes
|
|
247
|
-
const key = part.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
248
|
-
current = current[key];
|
|
249
|
-
}
|
|
250
|
-
return current;
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Validate a value against a JSON Schema file with detailed error information
|
|
254
|
-
* @param value The value to validate
|
|
255
|
-
* @param schemaPath Absolute or relative path to the schema file (supports @/ alias for contracts/)
|
|
256
|
-
* @param basePath Base path for resolving relative schema paths
|
|
257
|
-
* @param workspaceRoot Optional workspace root for @/ alias resolution
|
|
258
|
-
* @returns Detailed result with rich error objects for Contract View
|
|
259
|
-
*/
|
|
260
|
-
function validateAgainstSchemaDetailed(value, schemaPath, basePath, workspaceRoot) {
|
|
261
|
-
try {
|
|
262
|
-
// Resolve schema path (handles @/ alias for contracts folder)
|
|
263
|
-
const resolvedPath = resolveSchemaPath(schemaPath, basePath, workspaceRoot);
|
|
264
|
-
// Check if file exists
|
|
265
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
266
|
-
return {
|
|
267
|
-
valid: false,
|
|
268
|
-
errors: [{
|
|
269
|
-
instancePath: '(root)',
|
|
270
|
-
schemaPath: '',
|
|
271
|
-
keyword: 'file',
|
|
272
|
-
message: `Schema file not found: ${schemaPath}`,
|
|
273
|
-
params: { path: schemaPath },
|
|
274
|
-
severity: 'error'
|
|
275
|
-
}],
|
|
276
|
-
errorStrings: [`Schema file not found: ${schemaPath}`],
|
|
277
|
-
resolvedSchemaPath: resolvedPath
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
// Load and parse schema
|
|
281
|
-
const schemaContent = fs.readFileSync(resolvedPath, 'utf-8');
|
|
282
|
-
let schema;
|
|
283
|
-
try {
|
|
284
|
-
schema = JSON.parse(schemaContent);
|
|
285
|
-
}
|
|
286
|
-
catch (e) {
|
|
287
|
-
return {
|
|
288
|
-
valid: false,
|
|
289
|
-
errors: [{
|
|
290
|
-
instancePath: '(root)',
|
|
291
|
-
schemaPath: '',
|
|
292
|
-
keyword: 'parse',
|
|
293
|
-
message: `Invalid JSON in schema file: ${schemaPath}`,
|
|
294
|
-
params: { path: schemaPath },
|
|
295
|
-
severity: 'error'
|
|
296
|
-
}],
|
|
297
|
-
errorStrings: [`Invalid JSON in schema file: ${schemaPath}`],
|
|
298
|
-
resolvedSchemaPath: resolvedPath
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
// Validate
|
|
302
|
-
const validate = ajv.compile(schema);
|
|
303
|
-
const valid = validate(value);
|
|
304
|
-
if (!valid && validate.errors) {
|
|
305
|
-
const errors = validate.errors.map(err => convertAjvError(err, value));
|
|
306
|
-
const errorStrings = validate.errors.map(err => {
|
|
307
|
-
const path = err.instancePath || '(root)';
|
|
308
|
-
return `${path}: ${err.message}`;
|
|
309
|
-
});
|
|
310
|
-
return {
|
|
311
|
-
valid: false,
|
|
312
|
-
errors,
|
|
313
|
-
errorStrings,
|
|
314
|
-
resolvedSchemaPath: resolvedPath,
|
|
315
|
-
schema
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
return {
|
|
319
|
-
valid: true,
|
|
320
|
-
resolvedSchemaPath: resolvedPath,
|
|
321
|
-
schema
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
catch (e) {
|
|
325
|
-
const error = e instanceof Error ? e.message : String(e);
|
|
326
|
-
return {
|
|
327
|
-
valid: false,
|
|
328
|
-
errors: [{
|
|
329
|
-
instancePath: '(root)',
|
|
330
|
-
schemaPath: '',
|
|
331
|
-
keyword: 'exception',
|
|
332
|
-
message: `Schema validation error: ${error}`,
|
|
333
|
-
params: {},
|
|
334
|
-
severity: 'error'
|
|
335
|
-
}],
|
|
336
|
-
errorStrings: [`Schema validation error: ${error}`]
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Validate a value against a JSON Schema file
|
|
342
|
-
* @param value The value to validate
|
|
343
|
-
* @param schemaPath Absolute or relative path to the schema file
|
|
344
|
-
* @param basePath Base path for resolving relative schema paths
|
|
345
|
-
* @returns Object with valid flag and optional errors (backward compatible)
|
|
346
|
-
*/
|
|
347
|
-
function validateAgainstSchema(value, schemaPath, basePath) {
|
|
348
|
-
const result = validateAgainstSchemaDetailed(value, schemaPath, basePath);
|
|
349
|
-
return {
|
|
350
|
-
valid: result.valid,
|
|
351
|
-
errors: result.errorStrings
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Generate a schema file name from an endpoint URL
|
|
356
|
-
* Example: "GET /api/users/{id}" => "GET-api-users-id"
|
|
357
|
-
*/
|
|
358
|
-
function generateSchemaFileName(method, url) {
|
|
359
|
-
// Extract path from URL (remove protocol, host, query string)
|
|
360
|
-
let urlPath = url;
|
|
361
|
-
try {
|
|
362
|
-
const parsed = new URL(url);
|
|
363
|
-
urlPath = parsed.pathname;
|
|
364
|
-
}
|
|
365
|
-
catch {
|
|
366
|
-
// URL might be a relative path, try to extract just the path part
|
|
367
|
-
const queryIndex = url.indexOf('?');
|
|
368
|
-
if (queryIndex > -1) {
|
|
369
|
-
urlPath = url.substring(0, queryIndex);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
// Clean up the path
|
|
373
|
-
const cleanPath = urlPath
|
|
374
|
-
.replace(/^\/+|\/+$/g, '') // Remove leading/trailing slashes
|
|
375
|
-
.replace(/\{[^}]+\}/g, 'param') // {id} => param
|
|
376
|
-
.replace(/[^a-zA-Z0-9-]/g, '-') // Replace non-alphanumeric with dash
|
|
377
|
-
.replace(/-+/g, '-') // Collapse multiple dashes
|
|
378
|
-
.replace(/^-|-$/g, ''); // Remove leading/trailing dashes
|
|
379
|
-
return `${method.toUpperCase()}-${cleanPath || 'root'}.schema.json`;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Save a schema to a file
|
|
383
|
-
* @param schema The JSON Schema object
|
|
384
|
-
* @param filePath Absolute path to save the schema
|
|
385
|
-
*/
|
|
386
|
-
function saveSchema(schema, filePath) {
|
|
387
|
-
const dir = path.dirname(filePath);
|
|
388
|
-
if (!fs.existsSync(dir)) {
|
|
389
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
390
|
-
}
|
|
391
|
-
fs.writeFileSync(filePath, JSON.stringify(schema, null, 2), 'utf-8');
|
|
392
|
-
}
|
|
393
|
-
//# sourceMappingURL=schemaGenerator.js.map
|