norn-cli 1.3.16 → 1.3.18
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 +72 -0
- package/CHANGELOG.md +34 -1
- package/README.md +4 -2
- package/dist/cli.js +135 -63
- package/out/assertionRunner.js +537 -0
- package/out/cli/colors.js +129 -0
- package/out/cli/formatters/assertion.js +75 -0
- package/out/cli/formatters/index.js +23 -0
- package/out/cli/formatters/response.js +106 -0
- package/out/cli/formatters/summary.js +187 -0
- package/out/cli/redaction.js +237 -0
- package/out/cli/reporters/html.js +634 -0
- package/out/cli/reporters/index.js +22 -0
- package/out/cli/reporters/junit.js +211 -0
- package/out/cli.js +926 -0
- package/out/codeLensProvider.js +254 -0
- package/out/compareContentProvider.js +85 -0
- package/out/completionProvider.js +1886 -0
- package/out/contractDecorationProvider.js +243 -0
- package/out/coverageCalculator.js +756 -0
- package/out/coveragePanel.js +542 -0
- package/out/diagnosticProvider.js +980 -0
- package/out/environmentProvider.js +373 -0
- package/out/extension.js +1025 -0
- package/out/httpClient.js +269 -0
- package/out/jsonFileReader.js +320 -0
- package/out/nornapiParser.js +326 -0
- package/out/parser.js +725 -0
- package/out/responsePanel.js +4674 -0
- package/out/schemaGenerator.js +393 -0
- package/out/scriptRunner.js +419 -0
- package/out/sequenceRunner.js +3046 -0
- package/out/swaggerParser.js +339 -0
- package/out/test/extension.test.js +48 -0
- package/out/testProvider.js +658 -0
- package/out/validationCache.js +245 -0
- package/package.json +1 -1
|
@@ -0,0 +1,373 @@
|
|
|
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.findEnvFile = findEnvFile;
|
|
37
|
+
exports.findEnvFileFromPath = findEnvFileFromPath;
|
|
38
|
+
exports.parseEnvFile = parseEnvFile;
|
|
39
|
+
exports.loadEnvironmentConfig = loadEnvironmentConfig;
|
|
40
|
+
exports.getActiveEnvironment = getActiveEnvironment;
|
|
41
|
+
exports.setActiveEnvironment = setActiveEnvironment;
|
|
42
|
+
exports.getEnvironmentVariables = getEnvironmentVariables;
|
|
43
|
+
exports.getAvailableEnvironments = getAvailableEnvironments;
|
|
44
|
+
exports.createStatusBarItem = createStatusBarItem;
|
|
45
|
+
exports.showEnvironmentPicker = showEnvironmentPicker;
|
|
46
|
+
exports.disposeStatusBar = disposeStatusBar;
|
|
47
|
+
exports.createCoverageStatusBarItem = createCoverageStatusBarItem;
|
|
48
|
+
exports.updateCoverageStatusBar = updateCoverageStatusBar;
|
|
49
|
+
exports.getCoverageStatusBarItem = getCoverageStatusBarItem;
|
|
50
|
+
const vscode = __importStar(require("vscode"));
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const ENV_FILENAME = '.nornenv';
|
|
54
|
+
// Current active environment (stored globally)
|
|
55
|
+
let activeEnvironment;
|
|
56
|
+
let statusBarItem;
|
|
57
|
+
let coverageStatusBarItem;
|
|
58
|
+
/**
|
|
59
|
+
* Finds the .nornenv file in the workspace
|
|
60
|
+
*/
|
|
61
|
+
function findEnvFile() {
|
|
62
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
63
|
+
if (!workspaceFolders) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
for (const folder of workspaceFolders) {
|
|
67
|
+
const envPath = path.join(folder.uri.fsPath, ENV_FILENAME);
|
|
68
|
+
if (fs.existsSync(envPath)) {
|
|
69
|
+
return envPath;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Finds .nornenv file relative to a specific file path (for CLI usage)
|
|
76
|
+
*/
|
|
77
|
+
function findEnvFileFromPath(filePath) {
|
|
78
|
+
let dir = path.dirname(filePath);
|
|
79
|
+
const root = path.parse(dir).root;
|
|
80
|
+
while (dir !== root) {
|
|
81
|
+
const envPath = path.join(dir, ENV_FILENAME);
|
|
82
|
+
if (fs.existsSync(envPath)) {
|
|
83
|
+
return envPath;
|
|
84
|
+
}
|
|
85
|
+
dir = path.dirname(dir);
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Parses the .nornenv file content
|
|
91
|
+
*/
|
|
92
|
+
function parseEnvFile(content) {
|
|
93
|
+
const lines = content.split('\n');
|
|
94
|
+
const config = {
|
|
95
|
+
common: {},
|
|
96
|
+
environments: [],
|
|
97
|
+
secretNames: new Set(),
|
|
98
|
+
secretValues: new Map()
|
|
99
|
+
};
|
|
100
|
+
let currentEnv = null;
|
|
101
|
+
const envRegex = /^\[env:([a-zA-Z_][a-zA-Z0-9_-]*)\]$/;
|
|
102
|
+
const varRegex = /^var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
|
|
103
|
+
const secretRegex = /^secret\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
const trimmed = line.trim();
|
|
106
|
+
// Skip empty lines and comments
|
|
107
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Check for environment section
|
|
111
|
+
const envMatch = trimmed.match(envRegex);
|
|
112
|
+
if (envMatch) {
|
|
113
|
+
currentEnv = {
|
|
114
|
+
name: envMatch[1],
|
|
115
|
+
variables: {}
|
|
116
|
+
};
|
|
117
|
+
config.environments.push(currentEnv);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
// Check for secret declaration (treated like var but marked for redaction)
|
|
121
|
+
const secretMatch = trimmed.match(secretRegex);
|
|
122
|
+
if (secretMatch) {
|
|
123
|
+
const varName = secretMatch[1];
|
|
124
|
+
const varValue = secretMatch[2].trim();
|
|
125
|
+
// Track as secret
|
|
126
|
+
config.secretNames.add(varName);
|
|
127
|
+
config.secretValues.set(varName, varValue);
|
|
128
|
+
// Also add to variables so it can be used
|
|
129
|
+
if (currentEnv) {
|
|
130
|
+
currentEnv.variables[varName] = varValue;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
config.common[varName] = varValue;
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// Check for variable declaration
|
|
138
|
+
const varMatch = trimmed.match(varRegex);
|
|
139
|
+
if (varMatch) {
|
|
140
|
+
const varName = varMatch[1];
|
|
141
|
+
const varValue = varMatch[2].trim();
|
|
142
|
+
if (currentEnv) {
|
|
143
|
+
currentEnv.variables[varName] = varValue;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
config.common[varName] = varValue;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return config;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reads and parses the .nornenv file
|
|
154
|
+
*/
|
|
155
|
+
function loadEnvironmentConfig(envFilePath) {
|
|
156
|
+
const filePath = envFilePath || findEnvFile();
|
|
157
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
162
|
+
return parseEnvFile(content);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Gets the currently active environment name
|
|
170
|
+
*/
|
|
171
|
+
function getActiveEnvironment() {
|
|
172
|
+
return activeEnvironment;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Sets the active environment
|
|
176
|
+
*/
|
|
177
|
+
function setActiveEnvironment(envName) {
|
|
178
|
+
activeEnvironment = envName;
|
|
179
|
+
updateStatusBar();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Gets all variables for the current environment (common + environment-specific)
|
|
183
|
+
*/
|
|
184
|
+
function getEnvironmentVariables(envFilePath) {
|
|
185
|
+
const config = loadEnvironmentConfig(envFilePath);
|
|
186
|
+
if (!config) {
|
|
187
|
+
return {};
|
|
188
|
+
}
|
|
189
|
+
// Start with common variables
|
|
190
|
+
const variables = { ...config.common };
|
|
191
|
+
// Override with environment-specific variables if an environment is active
|
|
192
|
+
if (activeEnvironment) {
|
|
193
|
+
const env = config.environments.find(e => e.name === activeEnvironment);
|
|
194
|
+
if (env) {
|
|
195
|
+
Object.assign(variables, env.variables);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return variables;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Gets available environment names
|
|
202
|
+
*/
|
|
203
|
+
function getAvailableEnvironments(envFilePath) {
|
|
204
|
+
const config = loadEnvironmentConfig(envFilePath);
|
|
205
|
+
if (!config) {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
return config.environments.map(e => e.name);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Creates and shows the status bar item
|
|
212
|
+
*/
|
|
213
|
+
function createStatusBarItem() {
|
|
214
|
+
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
|
215
|
+
statusBarItem.command = 'norn.selectEnvironment';
|
|
216
|
+
updateStatusBar();
|
|
217
|
+
statusBarItem.show();
|
|
218
|
+
return statusBarItem;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Updates the status bar item text
|
|
222
|
+
*/
|
|
223
|
+
function updateStatusBar() {
|
|
224
|
+
if (!statusBarItem) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const envFile = findEnvFile();
|
|
228
|
+
if (!envFile) {
|
|
229
|
+
statusBarItem.text = '$(globe) Norn: No Env';
|
|
230
|
+
statusBarItem.tooltip = 'No .nornenv file found';
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (activeEnvironment) {
|
|
234
|
+
statusBarItem.text = `$(globe) Norn: ${activeEnvironment}`;
|
|
235
|
+
statusBarItem.tooltip = `Active environment: ${activeEnvironment}\nClick to change`;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
statusBarItem.text = '$(globe) Norn: Select Env';
|
|
239
|
+
statusBarItem.tooltip = 'Click to select an environment';
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Shows the environment picker
|
|
244
|
+
*/
|
|
245
|
+
async function showEnvironmentPicker() {
|
|
246
|
+
const environments = getAvailableEnvironments();
|
|
247
|
+
if (environments.length === 0) {
|
|
248
|
+
const createFile = await vscode.window.showInformationMessage('No .nornenv file found. Would you like to create one?', 'Create .nornenv', 'Cancel');
|
|
249
|
+
if (createFile === 'Create .nornenv') {
|
|
250
|
+
await createEnvFileTemplate();
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const items = [
|
|
255
|
+
{
|
|
256
|
+
label: '$(circle-slash) None',
|
|
257
|
+
description: 'Use only common variables',
|
|
258
|
+
picked: !activeEnvironment
|
|
259
|
+
},
|
|
260
|
+
...environments.map(env => ({
|
|
261
|
+
label: `$(server-environment) ${env}`,
|
|
262
|
+
description: env === activeEnvironment ? '(active)' : '',
|
|
263
|
+
picked: env === activeEnvironment
|
|
264
|
+
}))
|
|
265
|
+
];
|
|
266
|
+
const selected = await vscode.window.showQuickPick(items, {
|
|
267
|
+
placeHolder: 'Select environment',
|
|
268
|
+
title: 'Norn Environment'
|
|
269
|
+
});
|
|
270
|
+
if (selected) {
|
|
271
|
+
if (selected.label.includes('None')) {
|
|
272
|
+
setActiveEnvironment(undefined);
|
|
273
|
+
vscode.window.showInformationMessage('Norn: Environment cleared');
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
const envName = selected.label.replace('$(server-environment) ', '');
|
|
277
|
+
setActiveEnvironment(envName);
|
|
278
|
+
vscode.window.showInformationMessage(`Norn: Switched to "${envName}" environment`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Creates a template .nornenv file
|
|
284
|
+
*/
|
|
285
|
+
async function createEnvFileTemplate() {
|
|
286
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
287
|
+
if (!workspaceFolders) {
|
|
288
|
+
vscode.window.showErrorMessage('No workspace folder open');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const template = `# Norn Environment Configuration
|
|
292
|
+
# Common variables are available in all environments
|
|
293
|
+
|
|
294
|
+
var timeout = 30000
|
|
295
|
+
var version = v1
|
|
296
|
+
|
|
297
|
+
# Development environment
|
|
298
|
+
[env:dev]
|
|
299
|
+
var baseUrl = http://localhost:3000
|
|
300
|
+
var apiKey = dev-key-123
|
|
301
|
+
|
|
302
|
+
# Staging environment
|
|
303
|
+
[env:staging]
|
|
304
|
+
var baseUrl = https://staging.api.example.com
|
|
305
|
+
var apiKey = staging-key-456
|
|
306
|
+
|
|
307
|
+
# Production environment
|
|
308
|
+
[env:prod]
|
|
309
|
+
var baseUrl = https://api.example.com
|
|
310
|
+
var apiKey = prod-key-789
|
|
311
|
+
`;
|
|
312
|
+
const envPath = path.join(workspaceFolders[0].uri.fsPath, ENV_FILENAME);
|
|
313
|
+
fs.writeFileSync(envPath, template, 'utf-8');
|
|
314
|
+
const doc = await vscode.workspace.openTextDocument(envPath);
|
|
315
|
+
await vscode.window.showTextDocument(doc);
|
|
316
|
+
updateStatusBar();
|
|
317
|
+
vscode.window.showInformationMessage('Created .nornenv file');
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Disposes the status bar item
|
|
321
|
+
*/
|
|
322
|
+
function disposeStatusBar() {
|
|
323
|
+
if (statusBarItem) {
|
|
324
|
+
statusBarItem.dispose();
|
|
325
|
+
statusBarItem = undefined;
|
|
326
|
+
}
|
|
327
|
+
if (coverageStatusBarItem) {
|
|
328
|
+
coverageStatusBarItem.dispose();
|
|
329
|
+
coverageStatusBarItem = undefined;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Creates the coverage status bar item
|
|
334
|
+
*/
|
|
335
|
+
function createCoverageStatusBarItem() {
|
|
336
|
+
coverageStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99);
|
|
337
|
+
coverageStatusBarItem.command = 'norn.showCoverage';
|
|
338
|
+
coverageStatusBarItem.hide(); // Hidden until we know there's swagger
|
|
339
|
+
return coverageStatusBarItem;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Updates the coverage status bar display
|
|
343
|
+
*/
|
|
344
|
+
function updateCoverageStatusBar(hasSwagger, percentage, total, covered) {
|
|
345
|
+
if (!coverageStatusBarItem) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!hasSwagger) {
|
|
349
|
+
coverageStatusBarItem.hide();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
// Choose icon based on coverage level
|
|
353
|
+
let icon = '$(graph)';
|
|
354
|
+
if (percentage >= 80) {
|
|
355
|
+
icon = '$(pass)';
|
|
356
|
+
}
|
|
357
|
+
else if (percentage >= 50) {
|
|
358
|
+
icon = '$(warning)';
|
|
359
|
+
}
|
|
360
|
+
else if (percentage > 0) {
|
|
361
|
+
icon = '$(error)';
|
|
362
|
+
}
|
|
363
|
+
coverageStatusBarItem.text = `${icon} ${percentage}%`;
|
|
364
|
+
coverageStatusBarItem.tooltip = `API Coverage: ${covered}/${total} response codes tested\nClick to view details`;
|
|
365
|
+
coverageStatusBarItem.show();
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Get the coverage status bar item (for external updates)
|
|
369
|
+
*/
|
|
370
|
+
function getCoverageStatusBarItem() {
|
|
371
|
+
return coverageStatusBarItem;
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=environmentProvider.js.map
|