norn-cli 2.3.0 → 2.4.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 (92) hide show
  1. package/.claude/skills/norn-social-campaign/SKILL.md +70 -0
  2. package/CHANGELOG.md +6 -0
  3. package/demos/nornenv-region-refactor/README.md +64 -0
  4. package/dist/cli.js +360 -1
  5. package/out/apiResponseIntellisenseCache.js +394 -0
  6. package/out/assertionRunner.js +567 -0
  7. package/out/cacheDir.js +136 -0
  8. package/out/chatParticipant.js +763 -0
  9. package/out/cli/colors.js +127 -0
  10. package/out/cli/formatters/assertion.js +102 -0
  11. package/out/cli/formatters/index.js +23 -0
  12. package/out/cli/formatters/response.js +106 -0
  13. package/out/cli/formatters/summary.js +246 -0
  14. package/out/cli/redaction.js +237 -0
  15. package/out/cli/reporters/html.js +689 -0
  16. package/out/cli/reporters/index.js +22 -0
  17. package/out/cli/reporters/junit.js +226 -0
  18. package/out/codeLensProvider.js +351 -0
  19. package/out/compareContentProvider.js +85 -0
  20. package/out/completionProvider.js +3739 -0
  21. package/out/contractAssertionSummary.js +225 -0
  22. package/out/contractDecorationProvider.js +243 -0
  23. package/out/coverageCalculator.js +879 -0
  24. package/out/coveragePanel.js +597 -0
  25. package/out/debug/breakpointResolver.js +84 -0
  26. package/out/debug/breakpoints.js +52 -0
  27. package/out/debug/nornDebugAdapter.js +166 -0
  28. package/out/debug/nornDebugSession.js +613 -0
  29. package/out/debug/sequenceLocationIndex.js +77 -0
  30. package/out/debug/types.js +3 -0
  31. package/out/deepClone.js +21 -0
  32. package/out/diagnosticProvider.js +2554 -0
  33. package/out/environmentParser.js +736 -0
  34. package/out/environmentProvider.js +544 -0
  35. package/out/environmentTemplates.js +146 -0
  36. package/out/errors/formatError.js +113 -0
  37. package/out/errors/nornError.js +29 -0
  38. package/out/formUrlEncoded.js +89 -0
  39. package/out/httpClient.js +348 -0
  40. package/out/httpRuntimeOptions.js +16 -0
  41. package/out/importErrors.js +31 -0
  42. package/out/inlayHintResolver.js +70 -0
  43. package/out/jsonFileReader.js +323 -0
  44. package/out/mcpClient.js +193 -0
  45. package/out/mcpConfig.js +184 -0
  46. package/out/mcpToolIntellisenseCache.js +96 -0
  47. package/out/mcpToolSchema.js +50 -0
  48. package/out/nornConfig.js +132 -0
  49. package/out/nornHoverProvider.js +124 -0
  50. package/out/nornInlayHintsProvider.js +191 -0
  51. package/out/nornPrompt.js +755 -0
  52. package/out/nornSqlParser.js +286 -0
  53. package/out/nornapiHoverProvider.js +135 -0
  54. package/out/nornapiInlayHintsProvider.js +94 -0
  55. package/out/nornapiParser.js +324 -0
  56. package/out/nornenvCodeActionProvider.js +101 -0
  57. package/out/nornenvDecorationProvider.js +239 -0
  58. package/out/nornenvFoldingProvider.js +63 -0
  59. package/out/nornenvHoverProvider.js +114 -0
  60. package/out/nornenvInlayHintsProvider.js +99 -0
  61. package/out/nornenvLanguageModel.js +187 -0
  62. package/out/nornenvRegionRefactor.js +267 -0
  63. package/out/nornsqlHoverProvider.js +95 -0
  64. package/out/nornsqlInlayHintsProvider.js +114 -0
  65. package/out/parser.js +839 -0
  66. package/out/pathAccess.js +28 -0
  67. package/out/postmanImportPanel.js +732 -0
  68. package/out/postmanImportPlanner.js +1155 -0
  69. package/out/postmanImportSidebarView.js +532 -0
  70. package/out/quotedString.js +35 -0
  71. package/out/requestPreparation.js +179 -0
  72. package/out/requestValidation.js +146 -0
  73. package/out/responsePanel.js +7754 -0
  74. package/out/schemaGenerator.js +562 -0
  75. package/out/scriptRunner.js +419 -0
  76. package/out/secrets/cliSecrets.js +415 -0
  77. package/out/secrets/crypto.js +105 -0
  78. package/out/secrets/envFileSecrets.js +177 -0
  79. package/out/secrets/keyStore.js +259 -0
  80. package/out/sequenceDeclaration.js +15 -0
  81. package/out/sequenceRunner.js +3590 -0
  82. package/out/sqlAdapterRunner.js +122 -0
  83. package/out/sqlBuiltInAdapters.js +604 -0
  84. package/out/sqlConfig.js +184 -0
  85. package/out/starterCatalog.js +554 -0
  86. package/out/stringUtils.js +25 -0
  87. package/out/swaggerBodyIntellisenseCache.js +114 -0
  88. package/out/swaggerParser.js +464 -0
  89. package/out/testProvider.js +767 -0
  90. package/out/theoryCaseLoader.js +113 -0
  91. package/out/validationCache.js +211 -0
  92. package/package.json +6 -1
@@ -0,0 +1,544 @@
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.onDidChangeActiveEnvironment = exports.parseEnvFile = void 0;
37
+ exports.initializeEnvironmentMemento = initializeEnvironmentMemento;
38
+ exports.findEnvFile = findEnvFile;
39
+ exports.findEnvFileFromPath = findEnvFileFromPath;
40
+ exports.loadEnvironmentConfig = loadEnvironmentConfig;
41
+ exports.getEnvironmentSecretErrorDetails = getEnvironmentSecretErrorDetails;
42
+ exports.getEnvironmentSecretErrors = getEnvironmentSecretErrors;
43
+ exports.getEnvironmentSecretNames = getEnvironmentSecretNames;
44
+ exports.promptForMissingEnvironmentSecretKeys = promptForMissingEnvironmentSecretKeys;
45
+ exports.setEnvironmentSessionSecretKey = setEnvironmentSessionSecretKey;
46
+ exports.clearEnvironmentSessionSecretKeys = clearEnvironmentSessionSecretKeys;
47
+ exports.getActiveEnvironment = getActiveEnvironment;
48
+ exports.setActiveEnvironment = setActiveEnvironment;
49
+ exports.getEnvironmentVariables = getEnvironmentVariables;
50
+ exports.getEnvironmentImportErrors = getEnvironmentImportErrors;
51
+ exports.getEnvironmentImportErrorDetails = getEnvironmentImportErrorDetails;
52
+ exports.getAvailableEnvironments = getAvailableEnvironments;
53
+ exports.createStatusBarItem = createStatusBarItem;
54
+ exports.refreshEnvironmentStatusBar = refreshEnvironmentStatusBar;
55
+ exports.showEnvironmentPicker = showEnvironmentPicker;
56
+ exports.disposeStatusBar = disposeStatusBar;
57
+ exports.createCoverageStatusBarItem = createCoverageStatusBarItem;
58
+ exports.refreshCoverageStatusBarContext = refreshCoverageStatusBarContext;
59
+ exports.updateCoverageStatusBar = updateCoverageStatusBar;
60
+ exports.getCoverageStatusBarItem = getCoverageStatusBarItem;
61
+ const vscode = __importStar(require("vscode"));
62
+ const fs = __importStar(require("fs"));
63
+ const path = __importStar(require("path"));
64
+ const environmentParser_1 = require("./environmentParser");
65
+ const keyStore_1 = require("./secrets/keyStore");
66
+ const environmentTemplates_1 = require("./environmentTemplates");
67
+ var environmentParser_2 = require("./environmentParser");
68
+ Object.defineProperty(exports, "parseEnvFile", { enumerable: true, get: function () { return environmentParser_2.parseEnvFile; } });
69
+ /**
70
+ * Active env is tracked PER `.nornenv` file (keyed by absolute path). In a monorepo with
71
+ * multiple workspace folders each holding their own `.nornenv`, each remembers its own env.
72
+ * The whole map is persisted to a workspace-scoped memento so selections survive restart.
73
+ */
74
+ const activeEnvironmentByEnvFile = new Map();
75
+ let environmentMemento;
76
+ const ACTIVE_ENV_MEMENTO_KEY = 'norn.activeEnvironmentByEnvFile';
77
+ let statusBarItem;
78
+ let coverageStatusBarItem;
79
+ let hasCoverageData = false;
80
+ const activeEnvironmentEmitter = new vscode.EventEmitter();
81
+ exports.onDidChangeActiveEnvironment = activeEnvironmentEmitter.event;
82
+ /**
83
+ * Loads persisted active-env selections from the workspace memento. Drops entries whose
84
+ * `.nornenv` file no longer exists so stale paths don't accumulate over time.
85
+ * Must be called once during `activate()` before any UI is shown.
86
+ */
87
+ function initializeEnvironmentMemento(memento) {
88
+ environmentMemento = memento;
89
+ activeEnvironmentByEnvFile.clear();
90
+ const stored = memento.get(ACTIVE_ENV_MEMENTO_KEY, []);
91
+ let changed = false;
92
+ for (const [envFilePath, envName] of stored) {
93
+ if (fs.existsSync(envFilePath)) {
94
+ activeEnvironmentByEnvFile.set(envFilePath, envName);
95
+ }
96
+ else {
97
+ changed = true;
98
+ }
99
+ }
100
+ if (changed) {
101
+ persistActiveEnvironments();
102
+ }
103
+ updateStatusBar();
104
+ }
105
+ function persistActiveEnvironments() {
106
+ if (!environmentMemento) {
107
+ return;
108
+ }
109
+ void environmentMemento.update(ACTIVE_ENV_MEMENTO_KEY, Array.from(activeEnvironmentByEnvFile.entries()));
110
+ }
111
+ function getContextEnvFile() {
112
+ const activeEditor = vscode.window.activeTextEditor;
113
+ if (activeEditor && activeEditor.document.uri.scheme === 'file') {
114
+ return findEnvFileFromPath(activeEditor.document.uri.fsPath);
115
+ }
116
+ return findEnvFile();
117
+ }
118
+ function resolveEnvFilePath(pathOrSourceFile) {
119
+ if (!pathOrSourceFile) {
120
+ return getContextEnvFile();
121
+ }
122
+ const resolvedPath = path.resolve(pathOrSourceFile);
123
+ if (path.basename(resolvedPath) === environmentParser_1.ENV_FILENAME && fs.existsSync(resolvedPath)) {
124
+ return resolvedPath;
125
+ }
126
+ return findEnvFileFromPath(resolvedPath);
127
+ }
128
+ /**
129
+ * Finds the .nornenv file in the workspace
130
+ */
131
+ function findEnvFile() {
132
+ const workspaceFolders = vscode.workspace.workspaceFolders;
133
+ if (!workspaceFolders) {
134
+ return undefined;
135
+ }
136
+ for (const folder of workspaceFolders) {
137
+ const envPath = path.join(folder.uri.fsPath, environmentParser_1.ENV_FILENAME);
138
+ if (fs.existsSync(envPath)) {
139
+ return envPath;
140
+ }
141
+ }
142
+ return undefined;
143
+ }
144
+ /**
145
+ * Finds .nornenv file relative to a specific file path (for CLI usage)
146
+ */
147
+ function findEnvFileFromPath(filePath) {
148
+ return (0, environmentParser_1.findEnvFileFromPath)(filePath);
149
+ }
150
+ // parseEnvFile is now imported from ./environmentParser
151
+ /**
152
+ * Reads and parses the resolved .nornenv file.
153
+ * Accepts either:
154
+ * - a path to a .nornenv file, or
155
+ * - a source file/directory path (it will resolve nearest .nornenv), or
156
+ * - undefined (uses current editor context/workspace fallback).
157
+ */
158
+ function loadEnvironmentConfig(pathOrSourceFile) {
159
+ const filePath = resolveEnvFilePath(pathOrSourceFile);
160
+ if (!filePath || !fs.existsSync(filePath)) {
161
+ return undefined;
162
+ }
163
+ try {
164
+ const result = (0, environmentParser_1.loadAndResolveEnvFile)(filePath);
165
+ if (result.errors.length > 0 || result.secretErrors.length > 0) {
166
+ return undefined;
167
+ }
168
+ return result.config;
169
+ }
170
+ catch {
171
+ return undefined;
172
+ }
173
+ }
174
+ function getEnvironmentSecretErrorDetails(pathOrSourceFile) {
175
+ const filePath = resolveEnvFilePath(pathOrSourceFile);
176
+ if (!filePath || !fs.existsSync(filePath)) {
177
+ return [];
178
+ }
179
+ try {
180
+ const result = (0, environmentParser_1.loadAndResolveEnvFile)(filePath);
181
+ return result.secretErrors;
182
+ }
183
+ catch {
184
+ return [];
185
+ }
186
+ }
187
+ function getEnvironmentSecretErrors(pathOrSourceFile) {
188
+ return getEnvironmentSecretErrorDetails(pathOrSourceFile).map(e => e.message);
189
+ }
190
+ function getEnvironmentSecretNames(pathOrSourceFile) {
191
+ const filePath = resolveEnvFilePath(pathOrSourceFile);
192
+ if (!filePath || !fs.existsSync(filePath)) {
193
+ return new Set();
194
+ }
195
+ try {
196
+ const result = (0, environmentParser_1.loadAndResolveEnvFile)(filePath);
197
+ if (result.errors.length > 0) {
198
+ return new Set();
199
+ }
200
+ return new Set(result.config.secretNames);
201
+ }
202
+ catch {
203
+ return new Set();
204
+ }
205
+ }
206
+ function getUnlockableKids(errors) {
207
+ const kids = new Set();
208
+ for (const err of errors) {
209
+ if ((err.code === 'missing-key' || err.code === 'decrypt-failed') && err.kid) {
210
+ kids.add(err.kid);
211
+ }
212
+ }
213
+ return Array.from(kids.values()).sort();
214
+ }
215
+ async function promptForMissingEnvironmentSecretKeys(pathOrSourceFile) {
216
+ const filePath = resolveEnvFilePath(pathOrSourceFile);
217
+ if (!filePath || !fs.existsSync(filePath)) {
218
+ return true;
219
+ }
220
+ for (let attempt = 0; attempt < 5; attempt++) {
221
+ const secretErrors = getEnvironmentSecretErrorDetails(filePath);
222
+ const kids = getUnlockableKids(secretErrors);
223
+ if (kids.length === 0) {
224
+ return true;
225
+ }
226
+ for (const kid of kids) {
227
+ const value = await vscode.window.showInputBox({
228
+ prompt: `Enter shared key for kid '${kid}'`,
229
+ password: true,
230
+ ignoreFocusOut: true,
231
+ placeHolder: 'Paste shared key (it will be cached in the project .norn-cache)'
232
+ });
233
+ if (!value || value.trim() === '') {
234
+ return false;
235
+ }
236
+ (0, keyStore_1.saveSecretKeyToCache)(kid, value.trim(), filePath);
237
+ }
238
+ }
239
+ return getUnlockableKids(getEnvironmentSecretErrorDetails(filePath)).length === 0;
240
+ }
241
+ function setEnvironmentSessionSecretKey(kid, key) {
242
+ (0, keyStore_1.setSessionSecretKey)(kid, key);
243
+ }
244
+ function clearEnvironmentSessionSecretKeys() {
245
+ (0, keyStore_1.clearSessionSecretKeys)();
246
+ }
247
+ /**
248
+ * Returns the active environment for the `.nornenv` in scope for `pathOrSourceFile`,
249
+ * or for the current editor's context if omitted. Returns `undefined` when no `.nornenv`
250
+ * is in scope or when no env has been activated for that scope.
251
+ */
252
+ function getActiveEnvironment(pathOrSourceFile) {
253
+ const envFilePath = resolveEnvFilePath(pathOrSourceFile);
254
+ if (!envFilePath) {
255
+ return undefined;
256
+ }
257
+ return activeEnvironmentByEnvFile.get(envFilePath);
258
+ }
259
+ /**
260
+ * Sets the active environment for the `.nornenv` in scope for `pathOrSourceFile`,
261
+ * or for the current editor's context if omitted. Persists the change to the workspace
262
+ * memento so it survives reload. Pass `undefined` to clear the selection for that scope.
263
+ */
264
+ function setActiveEnvironment(envName, pathOrSourceFile) {
265
+ const envFilePath = resolveEnvFilePath(pathOrSourceFile);
266
+ if (!envFilePath) {
267
+ return;
268
+ }
269
+ const previous = activeEnvironmentByEnvFile.get(envFilePath);
270
+ if (envName === undefined) {
271
+ activeEnvironmentByEnvFile.delete(envFilePath);
272
+ }
273
+ else {
274
+ activeEnvironmentByEnvFile.set(envFilePath, envName);
275
+ }
276
+ persistActiveEnvironments();
277
+ updateStatusBar();
278
+ if (previous !== envName) {
279
+ activeEnvironmentEmitter.fire(envName);
280
+ }
281
+ }
282
+ /**
283
+ * Gets all variables for the current environment.
284
+ *
285
+ * Resolution: `common` ← ancestor templates/envs (left-to-right walk, right-most wins) ← self.
286
+ * Templates contribute their variables via `extends` but are never directly selectable.
287
+ *
288
+ * `envVariables` (passed to scope attachment) is the active env's **self-declared** vars only —
289
+ * not including ancestors. This keeps the runtime scope check meaningful (an env-section
290
+ * self-reference fires the scope diagnostic), while ancestor references are permitted
291
+ * because ancestor names appear in neither commonNames nor environmentNames.
292
+ */
293
+ function getEnvironmentVariables(pathOrSourceFile) {
294
+ const config = loadEnvironmentConfig(pathOrSourceFile);
295
+ if (!config) {
296
+ return {};
297
+ }
298
+ let variables = { ...config.common };
299
+ let envVariables;
300
+ const activeForContext = getActiveEnvironment(pathOrSourceFile);
301
+ if (activeForContext) {
302
+ const env = config.environments.find(e => e.name === activeForContext);
303
+ if (env) {
304
+ envVariables = env.variables;
305
+ // resolveEffectiveEnvVariables returns common + ancestor chain + self merged
306
+ // with the correct precedence (right-most parent wins; self overrides all).
307
+ variables = (0, environmentParser_1.resolveEffectiveEnvVariables)(activeForContext, config);
308
+ }
309
+ }
310
+ return (0, environmentTemplates_1.attachEnvironmentTemplateScopes)(variables, config.common, envVariables);
311
+ }
312
+ /**
313
+ * Checks if the resolved .nornenv file has import errors.
314
+ * Returns the error messages, or an empty array if no errors.
315
+ */
316
+ function getEnvironmentImportErrors(pathOrSourceFile) {
317
+ return getEnvironmentImportErrorDetails(pathOrSourceFile).map(e => e.message);
318
+ }
319
+ /**
320
+ * Returns detailed .nornenv import errors (message + file path + line).
321
+ */
322
+ function getEnvironmentImportErrorDetails(pathOrSourceFile) {
323
+ const filePath = resolveEnvFilePath(pathOrSourceFile);
324
+ if (!filePath || !fs.existsSync(filePath)) {
325
+ return [];
326
+ }
327
+ try {
328
+ const result = (0, environmentParser_1.loadAndResolveEnvFile)(filePath);
329
+ return result.errors;
330
+ }
331
+ catch {
332
+ return [];
333
+ }
334
+ }
335
+ /**
336
+ * Gets available environment names
337
+ */
338
+ function getAvailableEnvironments(pathOrSourceFile) {
339
+ const filePath = resolveEnvFilePath(pathOrSourceFile);
340
+ if (!filePath || !fs.existsSync(filePath)) {
341
+ return [];
342
+ }
343
+ try {
344
+ const result = (0, environmentParser_1.loadAndResolveEnvFile)(filePath);
345
+ return result.config.environments.map(e => e.name);
346
+ }
347
+ catch {
348
+ return [];
349
+ }
350
+ }
351
+ /**
352
+ * Creates and shows the status bar item
353
+ */
354
+ function createStatusBarItem() {
355
+ statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
356
+ statusBarItem.command = 'norn.selectEnvironment';
357
+ updateStatusBar();
358
+ statusBarItem.show();
359
+ return statusBarItem;
360
+ }
361
+ /**
362
+ * Refreshes the status bar text based on current editor context.
363
+ */
364
+ function refreshEnvironmentStatusBar() {
365
+ updateStatusBar();
366
+ }
367
+ /**
368
+ * Updates the status bar item text
369
+ */
370
+ function updateStatusBar() {
371
+ if (!statusBarItem) {
372
+ return;
373
+ }
374
+ const envFile = getContextEnvFile();
375
+ if (!envFile) {
376
+ statusBarItem.text = '$(globe) Norn: No Env';
377
+ statusBarItem.tooltip = 'No .nornenv file found';
378
+ return;
379
+ }
380
+ const activeForContext = activeEnvironmentByEnvFile.get(envFile);
381
+ if (activeForContext) {
382
+ statusBarItem.text = `$(globe) Norn: ${activeForContext}`;
383
+ statusBarItem.tooltip = `Active environment: ${activeForContext}\nScope: ${envFile}\nClick to change`;
384
+ }
385
+ else {
386
+ statusBarItem.text = '$(globe) Norn: Select Env';
387
+ statusBarItem.tooltip = `No env selected for ${envFile}\nClick to select`;
388
+ }
389
+ }
390
+ /**
391
+ * Shows the environment picker
392
+ */
393
+ async function showEnvironmentPicker(pathOrSourceFile) {
394
+ const envFilePath = pathOrSourceFile ? resolveEnvFilePath(pathOrSourceFile) : getContextEnvFile();
395
+ const environments = envFilePath ? getAvailableEnvironments(envFilePath) : [];
396
+ if (environments.length === 0) {
397
+ const createFile = await vscode.window.showInformationMessage('No .nornenv file found. Would you like to create one?', 'Create .nornenv', 'Cancel');
398
+ if (createFile === 'Create .nornenv') {
399
+ await createEnvFileTemplate();
400
+ }
401
+ return;
402
+ }
403
+ const activeForScope = envFilePath ? activeEnvironmentByEnvFile.get(envFilePath) : undefined;
404
+ const items = [
405
+ {
406
+ label: '$(circle-slash) None',
407
+ description: 'Use only common variables',
408
+ picked: !activeForScope
409
+ },
410
+ ...environments.map(env => ({
411
+ label: `$(server-environment) ${env}`,
412
+ description: env === activeForScope ? '(active)' : '',
413
+ picked: env === activeForScope
414
+ }))
415
+ ];
416
+ const selected = await vscode.window.showQuickPick(items, {
417
+ placeHolder: 'Select environment',
418
+ title: 'Norn Environment'
419
+ });
420
+ if (selected) {
421
+ if (selected.label.includes('None')) {
422
+ setActiveEnvironment(undefined, envFilePath);
423
+ vscode.window.showInformationMessage('Norn: Environment cleared');
424
+ }
425
+ else {
426
+ const envName = selected.label.replace('$(server-environment) ', '');
427
+ setActiveEnvironment(envName, envFilePath);
428
+ vscode.window.showInformationMessage(`Norn: Switched to "${envName}" environment`);
429
+ }
430
+ }
431
+ }
432
+ /**
433
+ * Creates a template .nornenv file
434
+ */
435
+ async function createEnvFileTemplate() {
436
+ const workspaceFolders = vscode.workspace.workspaceFolders;
437
+ if (!workspaceFolders) {
438
+ vscode.window.showErrorMessage('No workspace folder open');
439
+ return;
440
+ }
441
+ const template = `# Norn Environment Configuration
442
+ # Common variables are available in all environments
443
+
444
+ var timeout = 30000
445
+ var version = v1
446
+
447
+ # Development environment
448
+ [env:dev]
449
+ var baseUrl = http://localhost:3000
450
+ var apiKey = dev-key-123
451
+
452
+ # Staging environment
453
+ [env:staging]
454
+ var baseUrl = https://staging.api.example.com
455
+ var apiKey = staging-key-456
456
+
457
+ # Production environment
458
+ [env:prod]
459
+ var baseUrl = https://api.example.com
460
+ var apiKey = prod-key-789
461
+ `;
462
+ const envPath = path.join(workspaceFolders[0].uri.fsPath, environmentParser_1.ENV_FILENAME);
463
+ fs.writeFileSync(envPath, template, 'utf-8');
464
+ const doc = await vscode.workspace.openTextDocument(envPath);
465
+ await vscode.window.showTextDocument(doc);
466
+ updateStatusBar();
467
+ vscode.window.showInformationMessage('Created .nornenv file');
468
+ }
469
+ /**
470
+ * Disposes the status bar item
471
+ */
472
+ function disposeStatusBar() {
473
+ if (statusBarItem) {
474
+ statusBarItem.dispose();
475
+ statusBarItem = undefined;
476
+ }
477
+ if (coverageStatusBarItem) {
478
+ coverageStatusBarItem.dispose();
479
+ coverageStatusBarItem = undefined;
480
+ }
481
+ activeEnvironmentEmitter.dispose();
482
+ }
483
+ /**
484
+ * Creates the coverage status bar item
485
+ */
486
+ function createCoverageStatusBarItem() {
487
+ coverageStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99);
488
+ coverageStatusBarItem.command = 'norn.showCoverage';
489
+ coverageStatusBarItem.hide(); // Hidden until we know there's swagger
490
+ return coverageStatusBarItem;
491
+ }
492
+ function getActiveNornapiPath() {
493
+ const activeEditor = vscode.window.activeTextEditor;
494
+ if (!activeEditor) {
495
+ return undefined;
496
+ }
497
+ return activeEditor.document.languageId === 'nornapi'
498
+ ? activeEditor.document.uri.fsPath
499
+ : undefined;
500
+ }
501
+ /**
502
+ * Refresh coverage status bar command context for the current active editor.
503
+ */
504
+ function refreshCoverageStatusBarContext() {
505
+ if (!coverageStatusBarItem || !hasCoverageData) {
506
+ return;
507
+ }
508
+ const activeNornapiPath = getActiveNornapiPath();
509
+ coverageStatusBarItem.command = {
510
+ command: 'norn.showCoverage',
511
+ title: 'Show Coverage',
512
+ arguments: activeNornapiPath ? [activeNornapiPath] : []
513
+ };
514
+ coverageStatusBarItem.text = '$(graph) Coverage';
515
+ coverageStatusBarItem.tooltip = activeNornapiPath
516
+ ? 'Show API coverage for this .nornapi file (includes this folder and subfolders only).'
517
+ : 'Show API coverage';
518
+ coverageStatusBarItem.show();
519
+ }
520
+ /**
521
+ * Updates the coverage status bar display
522
+ */
523
+ function updateCoverageStatusBar(hasSwagger, percentage, total, covered) {
524
+ if (!coverageStatusBarItem) {
525
+ return;
526
+ }
527
+ if (!hasSwagger) {
528
+ hasCoverageData = false;
529
+ coverageStatusBarItem.hide();
530
+ return;
531
+ }
532
+ hasCoverageData = true;
533
+ void percentage;
534
+ void total;
535
+ void covered;
536
+ refreshCoverageStatusBarContext();
537
+ }
538
+ /**
539
+ * Get the coverage status bar item (for external updates)
540
+ */
541
+ function getCoverageStatusBarItem() {
542
+ return coverageStatusBarItem;
543
+ }
544
+ //# sourceMappingURL=environmentProvider.js.map
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.attachEnvironmentTemplateScopes = attachEnvironmentTemplateScopes;
4
+ exports.hasEnvironmentTemplate = hasEnvironmentTemplate;
5
+ exports.resolveEnvironmentTemplateValue = resolveEnvironmentTemplateValue;
6
+ exports.collectResolvedEnvironmentSecretValues = collectResolvedEnvironmentSecretValues;
7
+ const TEMPLATE_TOKEN_REGEX = /\{\{([^{}]+)\}\}/g;
8
+ const ENV_REFERENCE_REGEX = /^(?:\$env\.)?([a-zA-Z_][a-zA-Z0-9_]*)$/;
9
+ const ENV_TEMPLATE_SCOPES_KEY = '$nornEnvTemplateScopes';
10
+ function attachEnvironmentTemplateScopes(variables, commonVariables, environmentVariables) {
11
+ Object.defineProperty(variables, ENV_TEMPLATE_SCOPES_KEY, {
12
+ value: {
13
+ commonNames: new Set(Object.keys(commonVariables)),
14
+ environmentNames: new Set(Object.keys(environmentVariables ?? {}))
15
+ },
16
+ enumerable: false,
17
+ configurable: true,
18
+ writable: false
19
+ });
20
+ return variables;
21
+ }
22
+ function getEnvironmentTemplateScopes(variables) {
23
+ const value = variables[ENV_TEMPLATE_SCOPES_KEY];
24
+ if (!value || typeof value !== 'object') {
25
+ return undefined;
26
+ }
27
+ const candidate = value;
28
+ if (!(candidate.commonNames instanceof Set) || !(candidate.environmentNames instanceof Set)) {
29
+ return undefined;
30
+ }
31
+ return candidate;
32
+ }
33
+ function valueToTemplateString(value) {
34
+ if (value === undefined || value === null) {
35
+ return '';
36
+ }
37
+ if (typeof value === 'object') {
38
+ return JSON.stringify(value);
39
+ }
40
+ return String(value);
41
+ }
42
+ function parseTemplateReference(rawReference) {
43
+ const trimmed = rawReference.trim();
44
+ const match = trimmed.match(ENV_REFERENCE_REGEX);
45
+ return match?.[1];
46
+ }
47
+ function hasEnvironmentTemplate(value) {
48
+ return typeof value === 'string' && /\{\{[^{}]+\}\}/.test(value);
49
+ }
50
+ function resolveEnvironmentTemplateValue(variableName, variables, secretNames = new Set()) {
51
+ const resolving = new Set();
52
+ const cache = new Map();
53
+ const scopes = getEnvironmentTemplateScopes(variables);
54
+ const resolveVariable = (name) => {
55
+ const cached = cache.get(name);
56
+ if (cached) {
57
+ return cached;
58
+ }
59
+ if (resolving.has(name)) {
60
+ const cycle = [...resolving, name].join(' -> ');
61
+ return {
62
+ value: `{{${name}}}`,
63
+ secret: secretNames.has(name),
64
+ dependencies: new Set([name]),
65
+ errors: [{
66
+ kind: 'cycle',
67
+ variableName: name,
68
+ reference: name,
69
+ message: `Circular .nornenv template reference detected: ${cycle}`
70
+ }]
71
+ };
72
+ }
73
+ if (!Object.prototype.hasOwnProperty.call(variables, name)) {
74
+ return {
75
+ value: `{{${name}}}`,
76
+ secret: false,
77
+ dependencies: new Set([name]),
78
+ errors: [{
79
+ kind: 'unresolved',
80
+ variableName: name,
81
+ reference: name,
82
+ message: `.nornenv template reference '${name}' was not found in the active environment.`
83
+ }]
84
+ };
85
+ }
86
+ resolving.add(name);
87
+ const rawValue = valueToTemplateString(variables[name]);
88
+ const dependencies = new Set();
89
+ const errors = [];
90
+ let secret = secretNames.has(name);
91
+ const value = rawValue.replace(TEMPLATE_TOKEN_REGEX, (token, rawReference) => {
92
+ const referenceName = parseTemplateReference(rawReference);
93
+ if (!referenceName) {
94
+ errors.push({
95
+ kind: 'unsupported',
96
+ variableName: name,
97
+ reference: rawReference.trim(),
98
+ message: `.nornenv template reference '${rawReference.trim()}' is not supported. Use {{name}} or {{$env.name}}.`
99
+ });
100
+ return token;
101
+ }
102
+ const resolved = resolveVariable(referenceName);
103
+ dependencies.add(referenceName);
104
+ for (const dependency of resolved.dependencies) {
105
+ dependencies.add(dependency);
106
+ }
107
+ if (scopes?.environmentNames.has(name) && scopes.environmentNames.has(referenceName)) {
108
+ errors.push({
109
+ kind: 'scope',
110
+ variableName: name,
111
+ reference: referenceName,
112
+ message: `.nornenv template reference '${referenceName}' is not in scope. Values inside [env:...] sections can reference common variables and inherited ancestor variables only.`
113
+ });
114
+ }
115
+ if (resolved.secret) {
116
+ secret = true;
117
+ }
118
+ errors.push(...resolved.errors);
119
+ return resolved.value;
120
+ });
121
+ resolving.delete(name);
122
+ const resolution = {
123
+ value,
124
+ secret,
125
+ dependencies,
126
+ errors
127
+ };
128
+ cache.set(name, resolution);
129
+ return resolution;
130
+ };
131
+ return resolveVariable(variableName);
132
+ }
133
+ function collectResolvedEnvironmentSecretValues(variables, secretNames) {
134
+ const values = new Map();
135
+ for (const name of secretNames) {
136
+ if (!Object.prototype.hasOwnProperty.call(variables, name)) {
137
+ continue;
138
+ }
139
+ const resolved = resolveEnvironmentTemplateValue(name, variables, secretNames);
140
+ if (resolved.errors.length === 0) {
141
+ values.set(name, resolved.value);
142
+ }
143
+ }
144
+ return values;
145
+ }
146
+ //# sourceMappingURL=environmentTemplates.js.map