norn-cli 1.2.1 → 1.2.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  All notable changes to the "Norn" extension will be documented in this file.
4
4
 
5
+ ## [1.2.3] - 2026-02-07
6
+
7
+ ### Added
8
+ - **Duplicate import detection**: Errors shown when importing files with duplicate definitions
9
+ - Header groups and endpoints from `.nornapi` files
10
+ - Named requests and sequences from `.norn` files
11
+ - Blocks execution in CLI, VS Code, and Test Explorer
12
+ - Red squiggly on import line with descriptive error message
13
+
14
+ ### Fixed
15
+ - **.nornenv validation**: `secret var = value` now shows error (use `secret name = value` instead)
16
+ - Syntax highlighting marks invalid pattern in red
17
+ - Diagnostic message explains correct usage
18
+ - **IntelliSense context**: Script types (`bash`, `powershell`, `js`, `readJson`) only appear after `run` keyword
19
+ - **var keyword highlighting**: `var` keyword stays colored while typing variable name
20
+
21
+ ## [1.2.2] - 2026-02-05
22
+
23
+ ### Added
24
+ - **Coverage for sub-sequences**: Coverage now follows `run SequenceName` calls to track API calls in nested sequences
25
+ - **Coverage for named request blocks**: Named request blocks (`[RequestName]`) with hardcoded URLs are now matched to swagger endpoints by URL path
26
+ - **Auto-refresh coverage on save**: Coverage panel updates automatically when `.norn` or `.nornapi` files are saved
27
+
28
+ ### Improved
29
+ - **IntelliSense for .nornapi files**: Content-Type header completions now work in `.nornapi` files (previously only worked in `.norn`)
30
+
5
31
  ## [1.2.1] - 2026-02-03
6
32
 
7
33
  ### Added
package/dist/cli.js CHANGED
@@ -13493,6 +13493,10 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
13493
13493
  const resolvedPaths = [];
13494
13494
  const headerGroups = [];
13495
13495
  const endpoints = [];
13496
+ const headerGroupSources = /* @__PURE__ */ new Map();
13497
+ const endpointSources = /* @__PURE__ */ new Map();
13498
+ const namedRequestSources = /* @__PURE__ */ new Map();
13499
+ const sequenceSources = /* @__PURE__ */ new Map();
13496
13500
  for (const imp of imports) {
13497
13501
  const path4 = await import("path");
13498
13502
  const absolutePath = path4.resolve(baseDir, imp.path);
@@ -13510,16 +13514,64 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
13510
13514
  resolvedPaths.push(absolutePath);
13511
13515
  if (imp.path.endsWith(".nornapi")) {
13512
13516
  const apiDef = parseNornApiFile(content);
13513
- headerGroups.push(...apiDef.headerGroups);
13514
- endpoints.push(...apiDef.endpoints);
13517
+ for (const group of apiDef.headerGroups) {
13518
+ const existingSource = headerGroupSources.get(group.name);
13519
+ if (existingSource) {
13520
+ errors.push({
13521
+ path: imp.path,
13522
+ error: `Duplicate header group '${group.name}': already defined in '${existingSource}'`,
13523
+ lineNumber: imp.lineNumber
13524
+ });
13525
+ } else {
13526
+ headerGroupSources.set(group.name, imp.path);
13527
+ headerGroups.push(group);
13528
+ }
13529
+ }
13530
+ for (const endpoint of apiDef.endpoints) {
13531
+ const existingSource = endpointSources.get(endpoint.name);
13532
+ if (existingSource) {
13533
+ errors.push({
13534
+ path: imp.path,
13535
+ error: `Duplicate endpoint '${endpoint.name}': already defined in '${existingSource}'`,
13536
+ lineNumber: imp.lineNumber
13537
+ });
13538
+ } else {
13539
+ endpointSources.set(endpoint.name, imp.path);
13540
+ endpoints.push(endpoint);
13541
+ }
13542
+ }
13515
13543
  continue;
13516
13544
  }
13517
13545
  const importDir = path4.dirname(absolutePath);
13518
13546
  const nestedResult = await resolveImports(content, importDir, readFile2, alreadyImported);
13519
13547
  errors.push(...nestedResult.errors);
13520
13548
  resolvedPaths.push(...nestedResult.resolvedPaths);
13521
- headerGroups.push(...nestedResult.headerGroups);
13522
- endpoints.push(...nestedResult.endpoints);
13549
+ for (const group of nestedResult.headerGroups) {
13550
+ const existingSource = headerGroupSources.get(group.name);
13551
+ if (existingSource) {
13552
+ errors.push({
13553
+ path: imp.path,
13554
+ error: `Duplicate header group '${group.name}': already defined in '${existingSource}'`,
13555
+ lineNumber: imp.lineNumber
13556
+ });
13557
+ } else {
13558
+ headerGroupSources.set(group.name, imp.path);
13559
+ headerGroups.push(group);
13560
+ }
13561
+ }
13562
+ for (const endpoint of nestedResult.endpoints) {
13563
+ const existingSource = endpointSources.get(endpoint.name);
13564
+ if (existingSource) {
13565
+ errors.push({
13566
+ path: imp.path,
13567
+ error: `Duplicate endpoint '${endpoint.name}': already defined in '${existingSource}'`,
13568
+ lineNumber: imp.lineNumber
13569
+ });
13570
+ } else {
13571
+ endpointSources.set(endpoint.name, imp.path);
13572
+ endpoints.push(endpoint);
13573
+ }
13574
+ }
13523
13575
  if (nestedResult.importedContent) {
13524
13576
  importedContents.push(nestedResult.importedContent);
13525
13577
  }
@@ -13527,15 +13579,37 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
13527
13579
  const importedNamedRequests = extractNamedRequests(content);
13528
13580
  const importedSequences = extractSequencesFromText(content);
13529
13581
  for (const req of importedNamedRequests) {
13530
- const resolvedContent = substituteVariables(req.content, importedVariables);
13531
- importedContents.push(`[${req.name}]
13582
+ const lowerName = req.name.toLowerCase();
13583
+ const existingSource = namedRequestSources.get(lowerName);
13584
+ if (existingSource) {
13585
+ errors.push({
13586
+ path: imp.path,
13587
+ error: `Duplicate named request '${req.name}': already defined in '${existingSource}'`,
13588
+ lineNumber: imp.lineNumber
13589
+ });
13590
+ } else {
13591
+ namedRequestSources.set(lowerName, imp.path);
13592
+ const resolvedContent = substituteVariables(req.content, importedVariables);
13593
+ importedContents.push(`[${req.name}]
13532
13594
  ${resolvedContent}`);
13595
+ }
13533
13596
  }
13534
13597
  for (const seq of importedSequences) {
13535
- const resolvedContent = substituteVariables(seq.content, importedVariables);
13536
- importedContents.push(`sequence ${seq.name}
13598
+ const lowerName = seq.name.toLowerCase();
13599
+ const existingSource = sequenceSources.get(lowerName);
13600
+ if (existingSource) {
13601
+ errors.push({
13602
+ path: imp.path,
13603
+ error: `Duplicate sequence '${seq.name}': already defined in '${existingSource}'`,
13604
+ lineNumber: imp.lineNumber
13605
+ });
13606
+ } else {
13607
+ sequenceSources.set(lowerName, imp.path);
13608
+ const resolvedContent = substituteVariables(seq.content, importedVariables);
13609
+ importedContents.push(`sequence ${seq.name}
13537
13610
  ${resolvedContent}
13538
13611
  end sequence`);
13612
+ }
13539
13613
  }
13540
13614
  } catch (error) {
13541
13615
  errors.push({
@@ -21228,14 +21302,28 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
21228
21302
  if (endpoint && apiDefinitions) {
21229
21303
  const paramsStr = endpointMatch[2] || "";
21230
21304
  const headerGroupsStr = endpointMatch[3]?.trim() || "";
21231
- const paramValues = paramsStr ? paramsStr.split(",").map((p) => p.trim().replace(/^["']|["']$/g, "")) : [];
21305
+ const paramTokens = paramsStr ? paramsStr.split(",").map((p) => p.trim()) : [];
21232
21306
  const params = {};
21233
- endpoint.parameters.forEach((paramName, idx) => {
21234
- if (paramValues[idx] !== void 0) {
21235
- const bareResolved = resolveBareVariables(paramValues[idx], runtimeVariables);
21236
- params[paramName] = substituteVariables(bareResolved, runtimeVariables);
21307
+ const isNamedSyntax = paramTokens.length > 0 && paramTokens[0].includes(":");
21308
+ if (isNamedSyntax) {
21309
+ for (const token of paramTokens) {
21310
+ const kvMatch = token.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
21311
+ if (kvMatch) {
21312
+ const paramName = kvMatch[1];
21313
+ let paramValue = kvMatch[2].trim().replace(/^["']|["']$/g, "");
21314
+ const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
21315
+ params[paramName] = substituteVariables(bareResolved, runtimeVariables);
21316
+ }
21237
21317
  }
21238
- });
21318
+ } else {
21319
+ endpoint.parameters.forEach((paramName, idx) => {
21320
+ if (paramTokens[idx] !== void 0) {
21321
+ let paramValue = paramTokens[idx].replace(/^["']|["']$/g, "");
21322
+ const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
21323
+ params[paramName] = substituteVariables(bareResolved, runtimeVariables);
21324
+ }
21325
+ });
21326
+ }
21239
21327
  let pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
21240
21328
  for (const paramName of endpoint.parameters) {
21241
21329
  const value = params[paramName];
@@ -21257,7 +21345,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
21257
21345
  }
21258
21346
  }
21259
21347
  }
21260
- requestDescription = `${potentialEndpointName}(${paramValues.join(", ")}) \u2192 ${parsed.method} ${resolvedUrl}`;
21348
+ requestDescription = `${potentialEndpointName}(${paramTokens.join(", ")}) \u2192 ${parsed.method} ${resolvedUrl}`;
21261
21349
  } else {
21262
21350
  resolvedUrl = substituteVariables(parsed.url, runtimeVariables);
21263
21351
  requestDescription = `${parsed.method} ${resolvedUrl}`;
@@ -23389,9 +23477,22 @@ async function main() {
23389
23477
  workingDir,
23390
23478
  async (importPath) => fsPromises.readFile(importPath, "utf-8")
23391
23479
  );
23392
- for (const err of importResult.errors) {
23480
+ const duplicateErrors = importResult.errors.filter(
23481
+ (err) => err.error.includes("Duplicate header group") || err.error.includes("Duplicate endpoint") || err.error.includes("Duplicate named request") || err.error.includes("Duplicate sequence")
23482
+ );
23483
+ const otherErrors = importResult.errors.filter(
23484
+ (err) => !err.error.includes("Duplicate header group") && !err.error.includes("Duplicate endpoint") && !err.error.includes("Duplicate named request") && !err.error.includes("Duplicate sequence")
23485
+ );
23486
+ for (const err of otherErrors) {
23393
23487
  console.error(colors.warning(`Import warning: ${err.path} - ${err.error}`));
23394
23488
  }
23489
+ if (duplicateErrors.length > 0) {
23490
+ console.error(colors.error("Cannot execute - duplicate definitions found:"));
23491
+ for (const err of duplicateErrors) {
23492
+ console.error(colors.error(` ${err.path}: ${err.error}`));
23493
+ }
23494
+ process.exit(1);
23495
+ }
23395
23496
  const fileContentWithImports = importResult.importedContent ? `${importResult.importedContent}
23396
23497
 
23397
23498
  ${fileContent}` : fileContent;
@@ -23498,9 +23599,22 @@ ${fileContent}` : fileContent;
23498
23599
  workingDir,
23499
23600
  async (importPath) => fsPromises.readFile(importPath, "utf-8")
23500
23601
  );
23501
- for (const err of importResult.errors) {
23602
+ const duplicateErrors2 = importResult.errors.filter(
23603
+ (err) => err.error.includes("Duplicate header group") || err.error.includes("Duplicate endpoint") || err.error.includes("Duplicate named request") || err.error.includes("Duplicate sequence")
23604
+ );
23605
+ const otherErrors2 = importResult.errors.filter(
23606
+ (err) => !err.error.includes("Duplicate header group") && !err.error.includes("Duplicate endpoint") && !err.error.includes("Duplicate named request") && !err.error.includes("Duplicate sequence")
23607
+ );
23608
+ for (const err of otherErrors2) {
23502
23609
  console.error(colors.warning(`Import warning: ${err.path} - ${err.error}`));
23503
23610
  }
23611
+ if (duplicateErrors2.length > 0) {
23612
+ console.error(colors.error("Cannot execute - duplicate definitions found:"));
23613
+ for (const err of duplicateErrors2) {
23614
+ console.error(colors.error(` ${err.path}: ${err.error}`));
23615
+ }
23616
+ process.exit(1);
23617
+ }
23504
23618
  const fileContentWithImports = importResult.importedContent ? `${importResult.importedContent}
23505
23619
 
23506
23620
  ${fileContent}` : fileContent;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "norn-cli",
3
3
  "displayName": "Norn - REST Client",
4
4
  "description": "A powerful REST client for making HTTP requests with sequences, variables, scripts, and cookie support",
5
- "version": "1.2.1",
5
+ "version": "1.2.3",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"
package/norn-1.2.0.vsix DELETED
Binary file