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.
Files changed (40) hide show
  1. package/AGENTS.md +9 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/cli.js +246 -80
  4. package/package.json +1 -1
  5. package/out/assertionRunner.js +0 -537
  6. package/out/chatParticipant.js +0 -722
  7. package/out/cli/colors.js +0 -129
  8. package/out/cli/formatters/assertion.js +0 -75
  9. package/out/cli/formatters/index.js +0 -23
  10. package/out/cli/formatters/response.js +0 -106
  11. package/out/cli/formatters/summary.js +0 -187
  12. package/out/cli/redaction.js +0 -237
  13. package/out/cli/reporters/html.js +0 -634
  14. package/out/cli/reporters/index.js +0 -22
  15. package/out/cli/reporters/junit.js +0 -211
  16. package/out/cli.js +0 -989
  17. package/out/codeLensProvider.js +0 -248
  18. package/out/compareContentProvider.js +0 -85
  19. package/out/completionProvider.js +0 -2404
  20. package/out/contractDecorationProvider.js +0 -243
  21. package/out/coverageCalculator.js +0 -837
  22. package/out/coveragePanel.js +0 -545
  23. package/out/diagnosticProvider.js +0 -1113
  24. package/out/environmentProvider.js +0 -442
  25. package/out/extension.js +0 -1114
  26. package/out/httpClient.js +0 -269
  27. package/out/jsonFileReader.js +0 -320
  28. package/out/nornPrompt.js +0 -580
  29. package/out/nornapiParser.js +0 -326
  30. package/out/parser.js +0 -725
  31. package/out/responsePanel.js +0 -4674
  32. package/out/schemaGenerator.js +0 -393
  33. package/out/scriptRunner.js +0 -419
  34. package/out/sequenceRunner.js +0 -3046
  35. package/out/swaggerBodyIntellisenseCache.js +0 -147
  36. package/out/swaggerParser.js +0 -419
  37. package/out/test/coverageCalculator.test.js +0 -100
  38. package/out/test/extension.test.js +0 -48
  39. package/out/testProvider.js +0 -658
  40. package/out/validationCache.js +0 -245
package/out/httpClient.js DELETED
@@ -1,269 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createCookieJar = createCookieJar;
7
- exports.getSharedCookieJar = getSharedCookieJar;
8
- exports.clearCookies = clearCookies;
9
- exports.getCookiesForUrl = getCookiesForUrl;
10
- exports.getAllCookies = getAllCookies;
11
- exports.sendRequest = sendRequest;
12
- exports.sendRequestWithJar = sendRequestWithJar;
13
- const axios_1 = __importDefault(require("axios"));
14
- const tough_cookie_1 = require("tough-cookie");
15
- // Shared cookie jar for VS Code extension (persists across requests)
16
- let sharedCookieJar = new tough_cookie_1.CookieJar();
17
- /**
18
- * Creates a fresh cookie jar for isolated execution (CLI, sequences)
19
- */
20
- function createCookieJar() {
21
- return new tough_cookie_1.CookieJar();
22
- }
23
- /**
24
- * Gets the shared cookie jar
25
- */
26
- function getSharedCookieJar() {
27
- return sharedCookieJar;
28
- }
29
- /**
30
- * Clears all cookies from the shared jar
31
- */
32
- function clearCookies() {
33
- sharedCookieJar = new tough_cookie_1.CookieJar();
34
- }
35
- /**
36
- * Gets all cookies from a jar for a specific URL
37
- */
38
- async function getCookiesForUrl(url, jar) {
39
- const targetJar = jar || sharedCookieJar;
40
- try {
41
- const cookies = await targetJar.getCookies(url);
42
- return cookies.map(cookieToInfo);
43
- }
44
- catch {
45
- return [];
46
- }
47
- }
48
- /**
49
- * Gets all cookies from a jar
50
- */
51
- async function getAllCookies(jar) {
52
- const targetJar = jar || sharedCookieJar;
53
- const serialized = await targetJar.serialize();
54
- return (serialized.cookies || []).map((c) => ({
55
- name: String(c.key || ''),
56
- value: String(c.value || ''),
57
- domain: String(c.domain || ''),
58
- path: String(c.path || '/'),
59
- expires: c.expires ? String(c.expires) : undefined,
60
- httpOnly: Boolean(c.httpOnly),
61
- secure: Boolean(c.secure),
62
- }));
63
- }
64
- function cookieToInfo(cookie) {
65
- return {
66
- name: cookie.key,
67
- value: cookie.value,
68
- domain: cookie.domain || '',
69
- path: cookie.path || '/',
70
- expires: cookie.expires instanceof Date ? cookie.expires.toISOString() : undefined,
71
- httpOnly: cookie.httpOnly || false,
72
- secure: cookie.secure || false,
73
- };
74
- }
75
- /**
76
- * Sends a request using the shared cookie jar (for VS Code extension)
77
- */
78
- async function sendRequest(request, retryOptions) {
79
- return sendRequestWithJar(request, sharedCookieJar, retryOptions);
80
- }
81
- /**
82
- * Helper function to wait
83
- */
84
- function sleep(ms) {
85
- return new Promise(resolve => setTimeout(resolve, ms));
86
- }
87
- /**
88
- * Check if a response indicates failure (should retry)
89
- * Retries on: network errors, 5xx server errors, 429 rate limit
90
- */
91
- function shouldRetry(response, error) {
92
- // Always retry on network errors
93
- if (error)
94
- return true;
95
- // Retry on server errors (5xx) or rate limiting (429)
96
- if (response && (response.status >= 500 || response.status === 429))
97
- return true;
98
- return false;
99
- }
100
- /**
101
- * Sends a request using a specific cookie jar
102
- * Manually follows redirects to properly capture Set-Cookie headers at each step
103
- * Supports automatic retry with linear backoff
104
- */
105
- async function sendRequestWithJar(request, jar, retryOptions) {
106
- const totalAttempts = (retryOptions?.retryCount ?? 0) + 1; // +1 for initial attempt
107
- const backoffMs = retryOptions?.backoffMs ?? 1000;
108
- const retriedErrors = [];
109
- let lastError = null;
110
- let attemptsMade = 0;
111
- for (let attempt = 1; attempt <= totalAttempts; attempt++) {
112
- attemptsMade = attempt;
113
- const startTime = Date.now();
114
- const maxRedirects = 10;
115
- let redirectCount = 0;
116
- let currentUrl = request.url;
117
- let currentMethod = request.method;
118
- let currentBody = request.body;
119
- let finalResponse = null;
120
- let sentRequestBody = undefined;
121
- let sentRequestHeaders = {};
122
- try {
123
- while (redirectCount <= maxRedirects) {
124
- // Get cookies for current URL
125
- const existingCookies = await jar.getCookies(currentUrl);
126
- const cookieHeader = existingCookies.map(c => `${c.key}=${c.value}`).join('; ');
127
- const headers = { ...request.headers };
128
- if (cookieHeader) {
129
- headers['Cookie'] = cookieHeader;
130
- }
131
- // Determine how to send the body based on Content-Type
132
- let data = undefined;
133
- if (currentBody) {
134
- const contentType = Object.keys(headers).find((key) => key.toLowerCase() === 'content-type');
135
- const contentTypeValue = contentType ? headers[contentType] : '';
136
- if (contentTypeValue.includes('application/x-www-form-urlencoded')) {
137
- const lines = currentBody.split('\n').map(line => line.trim()).filter(line => line);
138
- const params = lines.map(line => {
139
- if (line.includes('=')) {
140
- const eqIndex = line.indexOf('=');
141
- const key = line.substring(0, eqIndex).trim();
142
- const value = line.substring(eqIndex + 1).trim();
143
- return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
144
- }
145
- const colonIndex = line.indexOf(':');
146
- if (colonIndex > 0) {
147
- const key = line.substring(0, colonIndex).trim();
148
- const value = line.substring(colonIndex + 1).trim();
149
- return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
150
- }
151
- return encodeURIComponent(line);
152
- });
153
- data = params.join('&');
154
- }
155
- else if (contentTypeValue.includes('application/json')) {
156
- try {
157
- data = JSON.parse(currentBody);
158
- }
159
- catch {
160
- data = currentBody;
161
- }
162
- }
163
- else {
164
- try {
165
- data = JSON.parse(currentBody);
166
- }
167
- catch {
168
- data = currentBody;
169
- }
170
- }
171
- }
172
- // Capture what we're actually sending (for debugging)
173
- sentRequestBody = data;
174
- sentRequestHeaders = { ...headers };
175
- const response = await (0, axios_1.default)({
176
- method: currentMethod,
177
- url: currentUrl,
178
- headers,
179
- data,
180
- timeout: 30000,
181
- maxRedirects: 0,
182
- validateStatus: () => true,
183
- });
184
- // Store any Set-Cookie headers in the jar
185
- const setCookieHeader = response.headers['set-cookie'];
186
- if (setCookieHeader) {
187
- const cookies = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
188
- for (const cookieStr of cookies) {
189
- try {
190
- await jar.setCookie(cookieStr, currentUrl);
191
- }
192
- catch {
193
- // Ignore invalid cookies
194
- }
195
- }
196
- }
197
- // Check if this is a redirect
198
- if (response.status >= 300 && response.status < 400 && response.headers['location']) {
199
- redirectCount++;
200
- const location = response.headers['location'];
201
- currentUrl = new URL(location, currentUrl).toString();
202
- if (currentMethod !== 'GET' && currentMethod !== 'HEAD') {
203
- currentMethod = 'GET';
204
- currentBody = undefined;
205
- }
206
- continue;
207
- }
208
- finalResponse = response;
209
- break;
210
- }
211
- if (!finalResponse) {
212
- throw new Error('Too many redirects');
213
- }
214
- // Check if we should retry on this response status
215
- if (shouldRetry(finalResponse, null) && attempt < totalAttempts) {
216
- const waitMs = backoffMs * attempt; // Linear backoff
217
- const errorMessage = `HTTP ${finalResponse.status} ${finalResponse.statusText}`;
218
- retriedErrors.push(`Attempt ${attempt}: ${errorMessage}`);
219
- // Call retry callback if provided
220
- if (retryOptions?.onRetry) {
221
- retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
222
- }
223
- await sleep(waitMs);
224
- continue;
225
- }
226
- // Get ALL cookies in the jar
227
- const cookies = await getAllCookies(jar);
228
- const result = {
229
- status: finalResponse.status,
230
- statusText: finalResponse.statusText,
231
- headers: finalResponse.headers,
232
- body: finalResponse.data,
233
- duration: Date.now() - startTime,
234
- cookies,
235
- requestBody: sentRequestBody,
236
- requestHeaders: sentRequestHeaders,
237
- };
238
- // Add retry info if we made retries
239
- if (attemptsMade > 1 || totalAttempts > 1) {
240
- result.retryInfo = {
241
- attemptsMade,
242
- totalAttempts,
243
- retriedErrors,
244
- };
245
- }
246
- return result;
247
- }
248
- catch (error) {
249
- const errorMessage = error.message || String(error);
250
- lastError = new Error(`${errorMessage}\nURL used: ${currentUrl || request.url}`);
251
- // If we have more attempts, wait and retry
252
- if (attempt < totalAttempts) {
253
- const waitMs = backoffMs * attempt; // Linear backoff
254
- retriedErrors.push(`Attempt ${attempt}: ${errorMessage}`);
255
- // Call retry callback if provided
256
- if (retryOptions?.onRetry) {
257
- retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
258
- }
259
- await sleep(waitMs);
260
- continue;
261
- }
262
- // No more retries, throw the error
263
- throw lastError;
264
- }
265
- }
266
- // Should never reach here, but TypeScript needs this
267
- throw lastError || new Error('Request failed');
268
- }
269
- //# sourceMappingURL=httpClient.js.map
@@ -1,320 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.JsonVariableStore = void 0;
37
- exports.isJsonCommand = isJsonCommand;
38
- exports.parseJsonCommand = parseJsonCommand;
39
- exports.readJsonFile = readJsonFile;
40
- exports.getNestedValue = getNestedValue;
41
- exports.setNestedValue = setNestedValue;
42
- exports.isPropertyAssignment = isPropertyAssignment;
43
- exports.parsePropertyAssignment = parsePropertyAssignment;
44
- exports.valueToString = valueToString;
45
- exports.substituteVariablesWithJson = substituteVariablesWithJson;
46
- const fs = __importStar(require("fs"));
47
- const path = __importStar(require("path"));
48
- /**
49
- * Checks if a line is a JSON file command.
50
- * Formats:
51
- * var name = run readJson ./path/to/file.json
52
- * var name = run readJson "/path with spaces/file.json"
53
- */
54
- function isJsonCommand(line) {
55
- const trimmed = line.trim();
56
- return /^var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*run\s+readJson\s+/i.test(trimmed);
57
- }
58
- /**
59
- * Parses a JSON file command line.
60
- * Format: var <name> = run readJson <path>
61
- */
62
- function parseJsonCommand(line) {
63
- const trimmed = line.trim();
64
- // Match: var varName = run readJson path
65
- const match = trimmed.match(/^var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*run\s+readJson\s+(.+)$/i);
66
- if (!match) {
67
- return null;
68
- }
69
- let filePath = match[2].trim();
70
- // Remove quotes if present
71
- if ((filePath.startsWith('"') && filePath.endsWith('"')) ||
72
- (filePath.startsWith("'") && filePath.endsWith("'"))) {
73
- filePath = filePath.slice(1, -1);
74
- }
75
- return {
76
- varName: match[1],
77
- filePath
78
- };
79
- }
80
- /**
81
- * Reads and parses a JSON file.
82
- */
83
- function readJsonFile(filePath, workingDir) {
84
- // Resolve relative paths
85
- const resolvedPath = path.isAbsolute(filePath)
86
- ? filePath
87
- : path.resolve(workingDir || process.cwd(), filePath);
88
- try {
89
- if (!fs.existsSync(resolvedPath)) {
90
- return {
91
- success: false,
92
- error: `File not found: ${resolvedPath}`,
93
- filePath: resolvedPath
94
- };
95
- }
96
- const content = fs.readFileSync(resolvedPath, 'utf-8');
97
- const data = JSON.parse(content);
98
- return {
99
- success: true,
100
- data,
101
- filePath: resolvedPath
102
- };
103
- }
104
- catch (err) {
105
- const message = err instanceof Error ? err.message : String(err);
106
- return {
107
- success: false,
108
- error: `Failed to read/parse JSON file: ${message}`,
109
- filePath: resolvedPath
110
- };
111
- }
112
- }
113
- /**
114
- * Gets a value from an object using a dot-notation path.
115
- * Supports array indexing with brackets: obj.array[0].property
116
- *
117
- * @param obj The source object
118
- * @param path The dot-notation path (e.g., "user.addresses[0].city")
119
- * @returns The value at the path, or undefined if not found
120
- */
121
- function getNestedValue(obj, path) {
122
- if (!path || obj === null || obj === undefined) {
123
- return obj;
124
- }
125
- // Convert [0] to .0 and split by dots
126
- const parts = path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(p => p !== '');
127
- let current = obj;
128
- for (const part of parts) {
129
- if (current === null || current === undefined) {
130
- return undefined;
131
- }
132
- current = current[part];
133
- }
134
- return current;
135
- }
136
- /**
137
- * Sets a value in an object using a dot-notation path.
138
- * Creates intermediate objects/arrays as needed.
139
- * Supports array indexing with brackets: obj.array[0].property
140
- *
141
- * @param obj The source object to modify
142
- * @param path The dot-notation path (e.g., "user.addresses[0].city")
143
- * @param value The value to set
144
- * @returns true if successful, false if the path is invalid
145
- */
146
- function setNestedValue(obj, path, value) {
147
- if (!path || obj === null || obj === undefined || typeof obj !== 'object') {
148
- return false;
149
- }
150
- // Convert [0] to .0 and split by dots
151
- const parts = path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(p => p !== '');
152
- if (parts.length === 0) {
153
- return false;
154
- }
155
- let current = obj;
156
- for (let i = 0; i < parts.length - 1; i++) {
157
- const part = parts[i];
158
- const nextPart = parts[i + 1];
159
- if (current[part] === undefined || current[part] === null) {
160
- // Create intermediate object or array based on next part
161
- current[part] = /^\d+$/.test(nextPart) ? [] : {};
162
- }
163
- current = current[part];
164
- if (typeof current !== 'object') {
165
- return false; // Can't navigate further
166
- }
167
- }
168
- const lastPart = parts[parts.length - 1];
169
- current[lastPart] = value;
170
- return true;
171
- }
172
- /**
173
- * Checks if a line is a property assignment command.
174
- * Format: varName.property.path = value
175
- * varName[0].property = value
176
- */
177
- function isPropertyAssignment(line) {
178
- const trimmed = line.trim();
179
- // Match: identifier followed by . or [ then = something
180
- // But NOT starting with 'var ' or other keywords
181
- return /^[a-zA-Z_][a-zA-Z0-9_]*[\.\[]/.test(trimmed) &&
182
- /=/.test(trimmed) &&
183
- !trimmed.startsWith('var ') &&
184
- !trimmed.startsWith('run ') &&
185
- !trimmed.startsWith('print ') &&
186
- !trimmed.startsWith('assert ') &&
187
- !trimmed.startsWith('sequence ');
188
- }
189
- /**
190
- * Parses a property assignment command.
191
- * Format: varName.path.to.property = value
192
- * Returns the variable name, the property path, and the value.
193
- */
194
- function parsePropertyAssignment(line) {
195
- const trimmed = line.trim();
196
- // Match: varName.path = value OR varName[0].path = value
197
- const match = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])+)\s*=\s*(.+)$/);
198
- if (!match) {
199
- return null;
200
- }
201
- const varName = match[1];
202
- let propertyPath = match[2];
203
- const value = match[3].trim();
204
- // Remove leading dot if present
205
- if (propertyPath.startsWith('.')) {
206
- propertyPath = propertyPath.substring(1);
207
- }
208
- return {
209
- varName,
210
- propertyPath,
211
- value
212
- };
213
- }
214
- /**
215
- * Converts a value to a string for use in variable substitution.
216
- */
217
- function valueToString(value) {
218
- if (value === null) {
219
- return 'null';
220
- }
221
- if (value === undefined) {
222
- return '';
223
- }
224
- if (typeof value === 'object') {
225
- return JSON.stringify(value);
226
- }
227
- return String(value);
228
- }
229
- /**
230
- * Storage for JSON objects loaded during sequence execution.
231
- * Maps variable names to their parsed JSON data.
232
- */
233
- class JsonVariableStore {
234
- jsonObjects = new Map();
235
- /**
236
- * Stores a JSON object under the given variable name.
237
- */
238
- set(varName, data) {
239
- this.jsonObjects.set(varName, data);
240
- }
241
- /**
242
- * Gets the raw JSON object for a variable.
243
- */
244
- get(varName) {
245
- return this.jsonObjects.get(varName);
246
- }
247
- /**
248
- * Checks if a variable is a JSON object.
249
- */
250
- has(varName) {
251
- return this.jsonObjects.has(varName);
252
- }
253
- /**
254
- * Gets a nested value from a JSON variable.
255
- * @param varName The variable name
256
- * @param path Optional dot-notation path within the object
257
- */
258
- getValue(varName, path) {
259
- const obj = this.jsonObjects.get(varName);
260
- if (obj === undefined) {
261
- return undefined;
262
- }
263
- if (!path) {
264
- return obj;
265
- }
266
- return getNestedValue(obj, path);
267
- }
268
- /**
269
- * Clears all stored JSON objects.
270
- */
271
- clear() {
272
- this.jsonObjects.clear();
273
- }
274
- /**
275
- * Gets all variable names that have JSON objects stored.
276
- */
277
- getVariableNames() {
278
- return Array.from(this.jsonObjects.keys());
279
- }
280
- }
281
- exports.JsonVariableStore = JsonVariableStore;
282
- /**
283
- * Substitutes variables in text, with support for JSON object property access.
284
- * Handles both simple variables {{varName}} and nested access {{varName.property.path}}
285
- *
286
- * @param text The text containing variable references
287
- * @param variables Simple string variables (name -> value)
288
- * @param jsonStore Optional JSON variable store for complex objects
289
- */
290
- function substituteVariablesWithJson(text, variables, jsonStore) {
291
- // Match {{varName}} or {{varName.path.to.property}} or {{varName[0].property}}
292
- return text.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)\}\}/g, (match, varName, pathPart) => {
293
- // First check if it's a JSON object with a path
294
- if (jsonStore && jsonStore.has(varName)) {
295
- const path = pathPart ? pathPart.replace(/^\./, '') : '';
296
- const value = jsonStore.getValue(varName, path);
297
- return valueToString(value);
298
- }
299
- // Check if there's a path and we have a simple variable that might be JSON
300
- if (pathPart && varName in variables) {
301
- // Try to parse the variable value as JSON
302
- try {
303
- const parsed = JSON.parse(variables[varName]);
304
- const path = pathPart.replace(/^\./, '');
305
- const value = getNestedValue(parsed, path);
306
- return valueToString(value);
307
- }
308
- catch {
309
- // Not JSON, fall through to simple substitution
310
- }
311
- }
312
- // Simple variable substitution
313
- if (varName in variables) {
314
- return variables[varName];
315
- }
316
- // Variable not found, return original
317
- return match;
318
- });
319
- }
320
- //# sourceMappingURL=jsonFileReader.js.map