norn-cli 2.4.0 → 2.6.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.
Files changed (96) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +26 -1
  3. package/dist/cli.js +330 -85
  4. package/package.json +24 -5
  5. package/schemas/norn.config.schema.json +43 -1
  6. package/scripts/__pycache__/reddit_signal_miner.cpython-312.pyc +0 -0
  7. package/scripts/reddit_signal_miner.py +482 -0
  8. package/.claude/settings.local.json +0 -18
  9. package/.claude/skills/norn-social-campaign/SKILL.md +0 -70
  10. package/out/apiResponseIntellisenseCache.js +0 -394
  11. package/out/assertionRunner.js +0 -567
  12. package/out/cacheDir.js +0 -136
  13. package/out/chatParticipant.js +0 -763
  14. package/out/cli/colors.js +0 -127
  15. package/out/cli/formatters/assertion.js +0 -102
  16. package/out/cli/formatters/index.js +0 -23
  17. package/out/cli/formatters/response.js +0 -106
  18. package/out/cli/formatters/summary.js +0 -246
  19. package/out/cli/redaction.js +0 -237
  20. package/out/cli/reporters/html.js +0 -689
  21. package/out/cli/reporters/index.js +0 -22
  22. package/out/cli/reporters/junit.js +0 -226
  23. package/out/codeLensProvider.js +0 -351
  24. package/out/compareContentProvider.js +0 -85
  25. package/out/completionProvider.js +0 -3739
  26. package/out/contractAssertionSummary.js +0 -225
  27. package/out/contractDecorationProvider.js +0 -243
  28. package/out/coverageCalculator.js +0 -879
  29. package/out/coveragePanel.js +0 -597
  30. package/out/debug/breakpointResolver.js +0 -84
  31. package/out/debug/breakpoints.js +0 -52
  32. package/out/debug/nornDebugAdapter.js +0 -166
  33. package/out/debug/nornDebugSession.js +0 -613
  34. package/out/debug/sequenceLocationIndex.js +0 -77
  35. package/out/debug/types.js +0 -3
  36. package/out/deepClone.js +0 -21
  37. package/out/diagnosticProvider.js +0 -2554
  38. package/out/environmentParser.js +0 -736
  39. package/out/environmentProvider.js +0 -544
  40. package/out/environmentTemplates.js +0 -146
  41. package/out/errors/formatError.js +0 -113
  42. package/out/errors/nornError.js +0 -29
  43. package/out/formUrlEncoded.js +0 -89
  44. package/out/httpClient.js +0 -348
  45. package/out/httpRuntimeOptions.js +0 -16
  46. package/out/importErrors.js +0 -31
  47. package/out/inlayHintResolver.js +0 -70
  48. package/out/jsonFileReader.js +0 -323
  49. package/out/mcpClient.js +0 -193
  50. package/out/mcpConfig.js +0 -184
  51. package/out/mcpToolIntellisenseCache.js +0 -96
  52. package/out/mcpToolSchema.js +0 -50
  53. package/out/nornConfig.js +0 -132
  54. package/out/nornHoverProvider.js +0 -124
  55. package/out/nornInlayHintsProvider.js +0 -191
  56. package/out/nornPrompt.js +0 -755
  57. package/out/nornSqlParser.js +0 -286
  58. package/out/nornapiHoverProvider.js +0 -135
  59. package/out/nornapiInlayHintsProvider.js +0 -94
  60. package/out/nornapiParser.js +0 -324
  61. package/out/nornenvCodeActionProvider.js +0 -101
  62. package/out/nornenvDecorationProvider.js +0 -239
  63. package/out/nornenvFoldingProvider.js +0 -63
  64. package/out/nornenvHoverProvider.js +0 -114
  65. package/out/nornenvInlayHintsProvider.js +0 -99
  66. package/out/nornenvLanguageModel.js +0 -187
  67. package/out/nornenvRegionRefactor.js +0 -267
  68. package/out/nornsqlHoverProvider.js +0 -95
  69. package/out/nornsqlInlayHintsProvider.js +0 -114
  70. package/out/parser.js +0 -839
  71. package/out/pathAccess.js +0 -28
  72. package/out/postmanImportPanel.js +0 -732
  73. package/out/postmanImportPlanner.js +0 -1155
  74. package/out/postmanImportSidebarView.js +0 -532
  75. package/out/quotedString.js +0 -35
  76. package/out/requestPreparation.js +0 -179
  77. package/out/requestValidation.js +0 -146
  78. package/out/responsePanel.js +0 -7754
  79. package/out/schemaGenerator.js +0 -562
  80. package/out/scriptRunner.js +0 -419
  81. package/out/secrets/cliSecrets.js +0 -415
  82. package/out/secrets/crypto.js +0 -105
  83. package/out/secrets/envFileSecrets.js +0 -177
  84. package/out/secrets/keyStore.js +0 -259
  85. package/out/sequenceDeclaration.js +0 -15
  86. package/out/sequenceRunner.js +0 -3590
  87. package/out/sqlAdapterRunner.js +0 -122
  88. package/out/sqlBuiltInAdapters.js +0 -604
  89. package/out/sqlConfig.js +0 -184
  90. package/out/starterCatalog.js +0 -554
  91. package/out/stringUtils.js +0 -25
  92. package/out/swaggerBodyIntellisenseCache.js +0 -114
  93. package/out/swaggerParser.js +0 -464
  94. package/out/testProvider.js +0 -767
  95. package/out/theoryCaseLoader.js +0 -113
  96. package/out/validationCache.js +0 -211
@@ -1,22 +0,0 @@
1
- "use strict";
2
- /**
3
- * CLI reporters index - re-exports all reporter functions
4
- */
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
- };
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- __exportStar(require("./junit"), exports);
21
- __exportStar(require("./html"), exports);
22
- //# sourceMappingURL=index.js.map
@@ -1,226 +0,0 @@
1
- "use strict";
2
- /**
3
- * JUnit XML reporter for CI/CD integration
4
- *
5
- * Generates JUnit XML format that's compatible with:
6
- * - Azure DevOps (ADO)
7
- * - Jenkins
8
- * - GitHub Actions
9
- * - GitLab CI
10
- * - CircleCI
11
- * - Most CI/CD platforms
12
- */
13
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- var desc = Object.getOwnPropertyDescriptor(m, k);
16
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
- desc = { enumerable: true, get: function() { return m[k]; } };
18
- }
19
- Object.defineProperty(o, k2, desc);
20
- }) : (function(o, m, k, k2) {
21
- if (k2 === undefined) k2 = k;
22
- o[k2] = m[k];
23
- }));
24
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
- Object.defineProperty(o, "default", { enumerable: true, value: v });
26
- }) : function(o, v) {
27
- o["default"] = v;
28
- });
29
- var __importStar = (this && this.__importStar) || (function () {
30
- var ownKeys = function(o) {
31
- ownKeys = Object.getOwnPropertyNames || function (o) {
32
- var ar = [];
33
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
- return ar;
35
- };
36
- return ownKeys(o);
37
- };
38
- return function (mod) {
39
- if (mod && mod.__esModule) return mod;
40
- var result = {};
41
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
- __setModuleDefault(result, mod);
43
- return result;
44
- };
45
- })();
46
- Object.defineProperty(exports, "__esModule", { value: true });
47
- exports.generateJUnitReport = generateJUnitReport;
48
- exports.generateJUnitReportFromResponse = generateJUnitReportFromResponse;
49
- const fs = __importStar(require("fs"));
50
- const redaction_1 = require("../redaction");
51
- /**
52
- * Escape XML special characters
53
- */
54
- function escapeXml(text) {
55
- return text
56
- .replace(/&/g, '&amp;')
57
- .replace(/</g, '&lt;')
58
- .replace(/>/g, '&gt;')
59
- .replace(/"/g, '&quot;')
60
- .replace(/'/g, '&apos;');
61
- }
62
- /**
63
- * Format duration as seconds with 3 decimal places
64
- */
65
- function formatDurationSeconds(ms) {
66
- return (ms / 1000).toFixed(3);
67
- }
68
- /**
69
- * Generate JUnit XML report from sequence results
70
- */
71
- function generateJUnitReport(results, options) {
72
- const { outputPath, redaction, suiteName = 'Norn Tests' } = options;
73
- const totalTests = results.reduce((sum, r) => sum + Math.max(1, r.assertionResults?.length || 0), 0);
74
- const totalFailures = results.reduce((sum, r) => sum + (r.assertionResults?.filter(a => !a.passed).length || (r.success ? 0 : 1)), 0);
75
- const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
76
- const totalErrors = results.filter(r => r.errors.length > 0 && r.success === false).length;
77
- let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
78
- xml += `<testsuites name="${escapeXml(suiteName)}" tests="${totalTests}" failures="${totalFailures}" errors="${totalErrors}" time="${formatDurationSeconds(totalDuration)}">\n`;
79
- for (const result of results) {
80
- xml += generateTestSuite(result, redaction);
81
- }
82
- xml += '</testsuites>\n';
83
- fs.writeFileSync(outputPath, xml, 'utf-8');
84
- }
85
- /**
86
- * Generate a testsuite element for a sequence
87
- */
88
- function generateTestSuite(result, redaction) {
89
- const assertions = result.assertionResults || [];
90
- const testCount = Math.max(1, assertions.length);
91
- const failures = assertions.filter(a => !a.passed).length + (result.success ? 0 : (assertions.length === 0 ? 1 : 0));
92
- let xml = ` <testsuite name="${escapeXml(result.name)}" tests="${testCount}" failures="${failures}" errors="0" time="${formatDurationSeconds(result.duration)}">\n`;
93
- if (assertions.length > 0) {
94
- // Create a testcase for each assertion
95
- for (const assertion of assertions) {
96
- xml += generateTestCase(result.name, assertion, redaction);
97
- }
98
- }
99
- else {
100
- // No assertions - create a single testcase for the sequence
101
- xml += generateSequenceTestCase(result, redaction);
102
- }
103
- // Add system-out with request/response details
104
- xml += generateSystemOut(result, redaction);
105
- // Add system-err with errors
106
- if (result.errors.length > 0) {
107
- xml += ` <system-err><![CDATA[${result.errors.map(e => (0, redaction_1.redactString)(e, redaction)).join('\n')}]]></system-err>\n`;
108
- }
109
- xml += ' </testsuite>\n';
110
- return xml;
111
- }
112
- /**
113
- * Generate a testcase element for an assertion
114
- */
115
- function generateTestCase(sequenceName, assertion, redaction) {
116
- const testName = assertion.message || assertion.expression;
117
- const className = sequenceName;
118
- let xml = ` <testcase name="${escapeXml(testName)}" classname="${escapeXml(className)}">\n`;
119
- if (!assertion.passed) {
120
- const message = assertion.error || `Expected ${assertion.rightExpression || assertion.rightValue}, got ${JSON.stringify(assertion.leftValue)}`;
121
- xml += ` <failure message="${escapeXml((0, redaction_1.redactString)(message, redaction))}" type="AssertionError">\n`;
122
- xml += `<![CDATA[Expression: ${assertion.expression}\n`;
123
- xml += `Expected: ${JSON.stringify(assertion.rightValue)}\n`;
124
- xml += `Actual: ${JSON.stringify(assertion.leftValue)}]]>\n`;
125
- xml += ' </failure>\n';
126
- }
127
- xml += ' </testcase>\n';
128
- return xml;
129
- }
130
- /**
131
- * Generate a testcase element for a sequence without assertions
132
- */
133
- function generateSequenceTestCase(result, redaction) {
134
- let xml = ` <testcase name="${escapeXml(result.name)}" classname="${escapeXml(result.name)}" time="${formatDurationSeconds(result.duration)}">\n`;
135
- if (!result.success) {
136
- const errorMessages = result.errors.length > 0
137
- ? result.errors.map(e => (0, redaction_1.redactString)(e, redaction)).join('; ')
138
- : 'Sequence failed';
139
- xml += ` <failure message="${escapeXml(errorMessages)}" type="SequenceError">\n`;
140
- xml += `<![CDATA[${errorMessages}]]>\n`;
141
- xml += ' </failure>\n';
142
- }
143
- xml += ' </testcase>\n';
144
- return xml;
145
- }
146
- /**
147
- * Generate system-out with request/response details
148
- */
149
- function generateSystemOut(result, redaction) {
150
- const lines = [];
151
- for (const step of result.steps) {
152
- if (step.type === 'request' && step.response) {
153
- const url = step.requestUrl ? (0, redaction_1.redactUrl)(step.requestUrl, redaction) : 'unknown';
154
- lines.push(`[${step.requestMethod || 'REQUEST'}] ${url}`);
155
- lines.push(` Status: ${step.response.status} ${step.response.statusText}`);
156
- lines.push(` Duration: ${step.response.duration}ms`);
157
- if (step.response.body) {
158
- const bodyStr = typeof step.response.body === 'object'
159
- ? JSON.stringify((0, redaction_1.redactBody)(step.response.body, redaction), null, 2)
160
- : (0, redaction_1.redactString)(String(step.response.body), redaction);
161
- // Truncate long bodies
162
- if (bodyStr.length > 1000) {
163
- lines.push(` Body: ${bodyStr.substring(0, 1000)}... (truncated)`);
164
- }
165
- else {
166
- lines.push(` Body: ${bodyStr}`);
167
- }
168
- }
169
- lines.push('');
170
- }
171
- else if (step.type === 'print' && step.print) {
172
- lines.push(`[PRINT] ${step.print.title}`);
173
- if (step.print.body) {
174
- lines.push(` ${step.print.body}`);
175
- }
176
- }
177
- else if (step.type === 'mcp' && step.mcp) {
178
- if (step.mcp.operation === 'list') {
179
- lines.push(`[MCP LIST] ${step.mcp.serverAlias}`);
180
- lines.push(` Tools: ${(step.mcp.tools || []).map(tool => tool.name).join(', ')}`);
181
- }
182
- else {
183
- lines.push(`[MCP CALL] ${step.mcp.serverAlias}.${step.mcp.toolName || ''}`);
184
- if (step.mcp.result?.text) {
185
- lines.push(` Text: ${(0, redaction_1.redactString)(step.mcp.result.text, redaction)}`);
186
- }
187
- if (step.mcp.result?.structuredContent) {
188
- lines.push(` Structured: ${JSON.stringify(step.mcp.result.structuredContent)}`);
189
- }
190
- }
191
- }
192
- }
193
- if (lines.length === 0) {
194
- return '';
195
- }
196
- return ` <system-out><![CDATA[${lines.join('\n')}]]></system-out>\n`;
197
- }
198
- /**
199
- * Generate JUnit report from a single HTTP response (for non-sequence runs)
200
- */
201
- function generateJUnitReportFromResponse(response, testName, options) {
202
- const { outputPath, redaction, suiteName = 'Norn Tests' } = options;
203
- const isSuccess = response.status >= 200 && response.status < 300;
204
- const failures = isSuccess ? 0 : 1;
205
- let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
206
- xml += `<testsuites name="${escapeXml(suiteName)}" tests="1" failures="${failures}" errors="0" time="${formatDurationSeconds(response.duration)}">\n`;
207
- xml += ` <testsuite name="${escapeXml(testName)}" tests="1" failures="${failures}" errors="0" time="${formatDurationSeconds(response.duration)}">\n`;
208
- xml += ` <testcase name="${escapeXml(testName)}" classname="${escapeXml(testName)}" time="${formatDurationSeconds(response.duration)}">\n`;
209
- if (!isSuccess) {
210
- xml += ` <failure message="HTTP ${response.status} ${response.statusText}" type="HttpError">\n`;
211
- xml += `<![CDATA[Status: ${response.status} ${response.statusText}`;
212
- if (response.body) {
213
- const bodyStr = typeof response.body === 'object'
214
- ? JSON.stringify((0, redaction_1.redactBody)(response.body, redaction), null, 2)
215
- : (0, redaction_1.redactString)(String(response.body), redaction);
216
- xml += `\nResponse: ${bodyStr}`;
217
- }
218
- xml += ']]>\n';
219
- xml += ' </failure>\n';
220
- }
221
- xml += ' </testcase>\n';
222
- xml += ' </testsuite>\n';
223
- xml += '</testsuites>\n';
224
- fs.writeFileSync(outputPath, xml, 'utf-8');
225
- }
226
- //# sourceMappingURL=junit.js.map
@@ -1,351 +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.HttpCodeLensProvider = void 0;
37
- exports.updateCodeLensCoverage = updateCodeLensCoverage;
38
- const vscode = __importStar(require("vscode"));
39
- const environmentProvider_1 = require("./environmentProvider");
40
- const envFileSecrets_1 = require("./secrets/envFileSecrets");
41
- const crypto_1 = require("./secrets/crypto");
42
- const environmentParser_1 = require("./environmentParser");
43
- const nornenvRegionRefactor_1 = require("./nornenvRegionRefactor");
44
- // Cached coverage percentage for CodeLens display
45
- /**
46
- * Update the cached coverage for CodeLens display
47
- */
48
- function updateCodeLensCoverage(percentage, total, covered) {
49
- void percentage;
50
- void total;
51
- void covered;
52
- }
53
- /**
54
- * Checks if a sequence declaration has only optional parameters (all have defaults).
55
- * Returns true if no parameters or all parameters have defaults.
56
- */
57
- function canRunSequenceWithoutArgs(line) {
58
- // Check if there are parameters
59
- const parenMatch = line.match(/\(([^)]*)\)/);
60
- if (!parenMatch) {
61
- // No parameters - can run
62
- return true;
63
- }
64
- const paramsStr = parenMatch[1].trim();
65
- if (!paramsStr) {
66
- // Empty parens - can run
67
- return true;
68
- }
69
- // Split by comma (simple split, not handling quoted strings with commas)
70
- const params = paramsStr.split(',');
71
- // Check each parameter has a default (contains =)
72
- for (const param of params) {
73
- const trimmed = param.trim();
74
- if (!trimmed) {
75
- continue;
76
- }
77
- // Must contain = for a default value
78
- if (!trimmed.includes('=')) {
79
- return false;
80
- }
81
- }
82
- return true;
83
- }
84
- /**
85
- * Checks for @data(...) or @theory(...) annotations directly above a sequence declaration.
86
- * This allows CodeLens execution for parameterized sequences with required parameters.
87
- */
88
- function hasTheoryAnnotationsAbove(document, sequenceLine) {
89
- const dataOrTheoryPattern = /^@(data|theory)\s*\(/;
90
- const decoratorPattern = /^@/;
91
- for (let i = sequenceLine - 1; i >= 0; i--) {
92
- const line = document.lineAt(i).text.trim();
93
- // Allow blank lines between decorators and sequence declaration
94
- if (!line) {
95
- continue;
96
- }
97
- // Stop as soon as the decorator block ends
98
- if (!decoratorPattern.test(line)) {
99
- break;
100
- }
101
- if (dataOrTheoryPattern.test(line)) {
102
- return true;
103
- }
104
- }
105
- return false;
106
- }
107
- class HttpCodeLensProvider {
108
- _onDidChangeCodeLenses = new vscode.EventEmitter();
109
- onDidChangeCodeLenses = this._onDidChangeCodeLenses.event;
110
- constructor() {
111
- // Refresh code lenses when environment changes
112
- vscode.commands.registerCommand('norn.refreshCodeLenses', () => {
113
- this._onDidChangeCodeLenses.fire();
114
- });
115
- }
116
- /**
117
- * Gets the environment display text for CodeLens
118
- * Returns 'add' if no env file exists, environment name if it does
119
- */
120
- getEnvDisplay(documentPath) {
121
- const envFile = (0, environmentProvider_1.findEnvFileFromPath)(documentPath);
122
- if (!envFile) {
123
- return { text: '+ Add env file', hasEnvFile: false };
124
- }
125
- const activeEnv = (0, environmentProvider_1.getActiveEnvironment)(documentPath);
126
- const availableEnvs = (0, environmentProvider_1.getAvailableEnvironments)(envFile);
127
- if (availableEnvs.length === 0) {
128
- return { text: 'env: (empty)', hasEnvFile: true };
129
- }
130
- return { text: activeEnv ? `env: ${activeEnv}` : 'env: none', hasEnvFile: true };
131
- }
132
- provideCodeLenses(document) {
133
- const codeLenses = [];
134
- const methodRegex = /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+/;
135
- // Match sequence (with optional test keyword) and optional parameters
136
- const sequenceRegex = /^(?:test\s+)?sequence\s+([a-zA-Z_][a-zA-Z0-9_-]*)(?:\s*\([^)]*\))?$/;
137
- // Match swagger statement with optional quotes around URL (only in .nornapi files)
138
- const swaggerRegex = /^swagger\s+["']?(https?:\/\/[^\s"']+)["']?\s*$/;
139
- // Match matchesSchema assertions: assert $1.body matchesSchema "./schema.json"
140
- // Note: .+? is non-greedy to not consume matchesSchema
141
- const matchesSchemaRegex = /^\s*assert\s+.+?\s+matchesSchema\s+["']?([^"']+)["']?/i;
142
- const envInfo = this.getEnvDisplay(document.uri.fsPath);
143
- const isNornApiFile = document.languageId === 'nornapi';
144
- const isNornFile = document.languageId === 'norn';
145
- const isNornenvFile = (0, envFileSecrets_1.isNornenvDocumentLike)(document.languageId, document.uri.fsPath);
146
- const secretLinesByNumber = isNornenvFile
147
- ? new Map((0, envFileSecrets_1.extractSecretLines)(document.getText(), document.uri.fsPath).map(secret => [secret.lineNumber, secret]))
148
- : new Map();
149
- const nornenvConfig = isNornenvFile ? (0, environmentParser_1.parseEnvFile)(document.getText(), document.uri.fsPath) : undefined;
150
- // Region-pattern refactor — single top-of-file lens when a flat S × R matrix is detected.
151
- if (isNornenvFile && nornenvConfig) {
152
- const pattern = (0, nornenvRegionRefactor_1.detectRegionPattern)(nornenvConfig, document.getText());
153
- if (pattern) {
154
- const { liftedToStage, liftedToRegion, leafSpecific } = pattern.summary;
155
- const topRange = new vscode.Range(0, 0, 0, 0);
156
- codeLenses.push(new vscode.CodeLens(topRange, {
157
- title: `$(sparkle) Refactor ${pattern.cells.length} envs into ${pattern.stages.length}+${pattern.regions.length} templates`,
158
- command: 'norn.nornenv.refactorRegionPattern',
159
- arguments: [document.uri.fsPath],
160
- tooltip: `Detected an S × R pattern (${pattern.stages.join(', ')} × ${pattern.regions.join(', ')}). `
161
- + `Refactor will lift ${liftedToStage} vars to stage templates, ${liftedToRegion} to region templates, and keep ${leafSpecific} as leaf-specific.`
162
- }));
163
- }
164
- }
165
- // Track if we're inside a sequence
166
- let insideSequence = false;
167
- for (let i = 0; i < document.lineCount; i++) {
168
- const line = document.lineAt(i);
169
- const trimmedText = line.text.trim();
170
- if (isNornenvFile) {
171
- const headerMatch = trimmedText.match(/^\[env:([a-zA-Z_][a-zA-Z0-9_-]*)(?:\s+extends\s+([^\]]+))?\]\s*$/i);
172
- if (headerMatch) {
173
- const envName = headerMatch[1];
174
- const parents = (headerMatch[2] ?? '')
175
- .split(',')
176
- .map(parent => parent.trim())
177
- .filter(parent => parent.length > 0);
178
- const range = new vscode.Range(i, 0, i, line.text.length);
179
- const activeEnv = (0, environmentProvider_1.getActiveEnvironment)(document.uri.fsPath);
180
- if (activeEnv !== envName) {
181
- codeLenses.push(new vscode.CodeLens(range, {
182
- title: '$(debug-start) Activate',
183
- command: 'norn.nornenv.activate',
184
- arguments: [envName, document.uri.fsPath],
185
- tooltip: `Use '${envName}' as the active Norn environment`
186
- }));
187
- }
188
- if (parents.length > 0) {
189
- const inheritedCount = nornenvConfig
190
- ? (0, environmentParser_1.resolveInheritedVariableDetails)(envName, nornenvConfig).size
191
- : 0;
192
- codeLenses.push(new vscode.CodeLens(range, {
193
- title: `$(list-tree) Peek inherited (${inheritedCount})`,
194
- command: 'norn.nornenv.peekInherited',
195
- arguments: [envName, document.uri.fsPath],
196
- tooltip: `Show variables inherited by '${envName}'`
197
- }));
198
- }
199
- continue;
200
- }
201
- const secret = secretLinesByNumber.get(i);
202
- if (!secret) {
203
- continue;
204
- }
205
- const range = new vscode.Range(i, 0, i, line.text.length);
206
- if (!secret.encrypted) {
207
- codeLenses.push(new vscode.CodeLens(range, {
208
- title: '$(lock) Encrypt Secret',
209
- command: 'norn.encryptSecretAtLine',
210
- arguments: [document.uri.fsPath, i],
211
- tooltip: 'Encrypt this secret in-place'
212
- }));
213
- continue;
214
- }
215
- const parsed = (0, crypto_1.parseEncryptedSecretValue)(secret.value);
216
- const hasValidCiphertext = parsed.ok;
217
- codeLenses.push(new vscode.CodeLens(range, {
218
- title: '$(eye) View Decrypted',
219
- command: 'norn.viewDecryptedSecretAtLine',
220
- arguments: [document.uri.fsPath, i],
221
- tooltip: 'Reveal decrypted value in a temporary editor'
222
- }));
223
- codeLenses.push(new vscode.CodeLens(range, {
224
- title: '$(sync) Rotate Secret',
225
- command: 'norn.rotateSecretAtLine',
226
- arguments: [document.uri.fsPath, i],
227
- tooltip: hasValidCiphertext
228
- ? 'Replace with a new encrypted value'
229
- : 'Replace malformed secret with a new encrypted value'
230
- }));
231
- codeLenses.push(new vscode.CodeLens(range, {
232
- title: '$(trash) Delete Secret',
233
- command: 'norn.deleteSecretAtLine',
234
- arguments: [document.uri.fsPath, i],
235
- tooltip: 'Remove this secret declaration line'
236
- }));
237
- continue;
238
- }
239
- // Check for swagger statement (only in .nornapi files)
240
- if (isNornApiFile) {
241
- const swaggerMatch = trimmedText.match(swaggerRegex);
242
- if (swaggerMatch) {
243
- const range = new vscode.Range(i, 0, i, line.text.length);
244
- codeLenses.push(new vscode.CodeLens(range, {
245
- title: '$(debug-start) Import Endpoints',
246
- command: 'norn.importSwagger',
247
- arguments: [swaggerMatch[1], i],
248
- tooltip: `Parse OpenAPI spec and generate endpoints`
249
- }));
250
- // Add Generate Schemas CodeLens
251
- codeLenses.push(new vscode.CodeLens(range, {
252
- title: '$(file-code) Generate Schemas',
253
- command: 'norn.generateSchemasFromSwagger',
254
- arguments: [swaggerMatch[1], document.uri.fsPath],
255
- tooltip: 'Generate JSON Schema files from OpenAPI response definitions'
256
- }));
257
- // Add file-scoped coverage CodeLens
258
- codeLenses.push(new vscode.CodeLens(range, {
259
- title: '$(graph) Show Coverage',
260
- command: 'norn.showCoverage',
261
- arguments: [document.uri.fsPath],
262
- tooltip: 'Show API coverage for this .nornapi file (includes this folder and subfolders only).'
263
- }));
264
- continue;
265
- }
266
- }
267
- // Check for matchesSchema assertions (in .norn files)
268
- if (isNornFile) {
269
- const schemaMatch = trimmedText.match(matchesSchemaRegex);
270
- if (schemaMatch) {
271
- const schemaPath = schemaMatch[1];
272
- const range = new vscode.Range(i, 0, i, line.text.length);
273
- // Open Contract lens
274
- codeLenses.push(new vscode.CodeLens(range, {
275
- title: '$(file-code) Open Contract',
276
- command: 'norn.openSchemaFile',
277
- arguments: [schemaPath, document.uri.fsPath],
278
- tooltip: `Open schema file: ${schemaPath}`
279
- }));
280
- // View Contract Report lens
281
- codeLenses.push(new vscode.CodeLens(range, {
282
- title: '$(checklist) View Contract',
283
- command: 'norn.viewContractReport',
284
- arguments: [i, document.uri.fsPath, schemaPath],
285
- tooltip: 'Run validation and show contract comparison report'
286
- }));
287
- continue;
288
- }
289
- }
290
- // Check for sequence start
291
- const sequenceMatch = trimmedText.match(sequenceRegex);
292
- if (sequenceMatch) {
293
- insideSequence = true;
294
- const range = new vscode.Range(i, 0, i, line.text.length);
295
- // Show Run Sequence if parameters are all optional, or values are provided via @data/@theory.
296
- const canRunWithoutArgs = canRunSequenceWithoutArgs(trimmedText);
297
- const hasTheoryAnnotations = hasTheoryAnnotationsAbove(document, i);
298
- if (canRunWithoutArgs || hasTheoryAnnotations) {
299
- // Run Sequence lens
300
- codeLenses.push(new vscode.CodeLens(range, {
301
- title: '▶ Run Sequence',
302
- command: 'norn.runSequence',
303
- arguments: [i],
304
- tooltip: `Run all requests in sequence "${sequenceMatch[1]}"`
305
- }));
306
- // Debug Sequence lens
307
- codeLenses.push(new vscode.CodeLens(range, {
308
- title: '▷ Debug Sequence',
309
- command: 'norn.debugSequence',
310
- arguments: [i, document.uri.fsPath, sequenceMatch[1]],
311
- tooltip: `Debug sequence "${sequenceMatch[1]}" with breakpoints and stepping`
312
- }));
313
- // Environment lens
314
- codeLenses.push(new vscode.CodeLens(range, {
315
- title: envInfo.text,
316
- command: envInfo.hasEnvFile ? 'norn.selectEnvironmentAndRefresh' : 'norn.createEnvFile',
317
- arguments: envInfo.hasEnvFile ? [document.uri.fsPath] : undefined,
318
- tooltip: envInfo.hasEnvFile ? 'Click to change environment' : 'Click to create .nornenv file'
319
- }));
320
- }
321
- continue;
322
- }
323
- // Check for sequence end
324
- if (trimmedText === 'end sequence') {
325
- insideSequence = false;
326
- continue;
327
- }
328
- // Only show "Send Request" for requests OUTSIDE of sequences
329
- if (!insideSequence && methodRegex.test(line.text)) {
330
- const range = new vscode.Range(i, 0, i, line.text.length);
331
- // Send Request lens
332
- codeLenses.push(new vscode.CodeLens(range, {
333
- title: '▶ Send Request',
334
- command: 'norn.sendRequest',
335
- arguments: [i],
336
- tooltip: 'Send this HTTP request'
337
- }));
338
- // Environment lens
339
- codeLenses.push(new vscode.CodeLens(range, {
340
- title: envInfo.text,
341
- command: envInfo.hasEnvFile ? 'norn.selectEnvironmentAndRefresh' : 'norn.createEnvFile',
342
- arguments: envInfo.hasEnvFile ? [document.uri.fsPath] : undefined,
343
- tooltip: envInfo.hasEnvFile ? 'Click to change environment' : 'Click to create .nornenv file'
344
- }));
345
- }
346
- }
347
- return codeLenses;
348
- }
349
- }
350
- exports.HttpCodeLensProvider = HttpCodeLensProvider;
351
- //# sourceMappingURL=codeLensProvider.js.map