norn-cli 1.3.15 → 1.3.17

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 (3) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cli.js +48 -40
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to the "Norn" extension will be documented in this file.
4
4
 
5
+ ## [1.3.17] - 2026-02-14
6
+
7
+ ### Fixed
8
+ - **Import Resolution**: Improved duplicate import handling while preserving circular import detection
9
+ - Re-importing already loaded files is now treated as a no-op instead of a false circular error
10
+ - True cycles are still detected via active import stack tracking
11
+ - **Sequence Var-Request Parsing**: Fixed multi-line `var x = METHOD ...` request collection
12
+ - Headers and JSON/form bodies are now preserved until the next real command boundary
13
+ - Body-only shorthand continues to parse correctly
14
+ - **Syntax Highlighting**: Improved `var` request URL/header/body highlighting in `.norn` files
15
+ - Better distinction between endpoint captures and raw URL captures
16
+ - Header groups and body lines now color consistently in URL-style var requests
17
+
18
+ ## [1.3.16] - 2026-02-13
19
+
20
+ ### Fixed
21
+ - **IntelliSense**: Additional completion fixes and reliability improvements
22
+
5
23
  ## [1.3.15] - 2026-02-13
6
24
 
7
25
  ### Fixed
package/dist/cli.js CHANGED
@@ -20075,50 +20075,45 @@ function getNamedRequest(text, name) {
20075
20075
  (r) => r.name === name || r.name.toLowerCase() === name.toLowerCase()
20076
20076
  );
20077
20077
  }
20078
- function extractVariables(text) {
20079
- const variables = {};
20080
- const variableRegex = /^\s*var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/gm;
20081
- const runtimePatterns = [
20082
- /^run\s+/i,
20083
- // var x = run ... (scripts/sequences)
20084
- /^\$\d+/,
20085
- // var x = $1... (response captures)
20086
- /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+/i
20087
- // var x = METHOD url (request captures)
20088
- ];
20089
- let match;
20090
- while ((match = variableRegex.exec(text)) !== null) {
20091
- let value = match[2].trim();
20092
- const isRuntimeValue = runtimePatterns.some((pattern) => pattern.test(value));
20093
- if (isRuntimeValue) {
20094
- continue;
20095
- }
20096
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
20097
- value = value.slice(1, -1);
20098
- }
20099
- variables[match[1]] = value;
20100
- }
20101
- return variables;
20078
+ var RUNTIME_VARIABLE_VALUE_PATTERNS = [
20079
+ /^run\s+/i,
20080
+ // var x = run ... (scripts/sequences)
20081
+ /^\$\d+/,
20082
+ // var x = $1... (response captures)
20083
+ /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+/i
20084
+ // var x = METHOD url (request captures)
20085
+ ];
20086
+ function isRuntimeComputedVariableValue(value) {
20087
+ return RUNTIME_VARIABLE_VALUE_PATTERNS.some((pattern) => pattern.test(value));
20088
+ }
20089
+ function isSequenceStartDeclaration(line2) {
20090
+ return /^(?:test\s+)?sequence\s+[a-zA-Z_][a-zA-Z0-9_-]*(?:\s*\([^)]*\))?\s*$/i.test(line2);
20091
+ }
20092
+ function isSequenceEndDeclaration(line2) {
20093
+ return /^end\s+sequence\s*$/i.test(line2);
20102
20094
  }
20103
20095
  function extractFileLevelVariables(text) {
20104
20096
  const variables = {};
20105
20097
  const lines = text.split("\n");
20106
20098
  const variableRegex = /^\s*var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$/;
20107
- let inSequence = false;
20099
+ let sequenceDepth = 0;
20108
20100
  for (const line2 of lines) {
20109
20101
  const trimmed = line2.trim();
20110
- if (/^sequence\s+[a-zA-Z_][a-zA-Z0-9_-]*$/.test(trimmed)) {
20111
- inSequence = true;
20102
+ if (isSequenceStartDeclaration(trimmed)) {
20103
+ sequenceDepth++;
20112
20104
  continue;
20113
20105
  }
20114
- if (trimmed === "end sequence") {
20115
- inSequence = false;
20106
+ if (isSequenceEndDeclaration(trimmed)) {
20107
+ sequenceDepth = Math.max(0, sequenceDepth - 1);
20116
20108
  continue;
20117
20109
  }
20118
- if (!inSequence) {
20110
+ if (sequenceDepth === 0) {
20119
20111
  const match = line2.match(variableRegex);
20120
20112
  if (match) {
20121
20113
  let value = match[2].trim();
20114
+ if (isRuntimeComputedVariableValue(value)) {
20115
+ continue;
20116
+ }
20122
20117
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
20123
20118
  value = value.slice(1, -1);
20124
20119
  }
@@ -20278,7 +20273,7 @@ function extractImports(text) {
20278
20273
  }
20279
20274
  return imports;
20280
20275
  }
20281
- async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__PURE__ */ new Set()) {
20276
+ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__PURE__ */ new Set(), importStack = /* @__PURE__ */ new Set()) {
20282
20277
  const imports = extractImports(text);
20283
20278
  const errors = [];
20284
20279
  const importedContents = [];
@@ -20292,7 +20287,7 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
20292
20287
  for (const imp of imports) {
20293
20288
  const path5 = await import("path");
20294
20289
  const absolutePath = path5.resolve(baseDir, imp.path);
20295
- if (alreadyImported.has(absolutePath)) {
20290
+ if (importStack.has(absolutePath)) {
20296
20291
  errors.push({
20297
20292
  path: imp.path,
20298
20293
  error: `Circular import detected`,
@@ -20300,9 +20295,13 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
20300
20295
  });
20301
20296
  continue;
20302
20297
  }
20298
+ if (alreadyImported.has(absolutePath)) {
20299
+ continue;
20300
+ }
20303
20301
  try {
20304
20302
  const content = await readFile2(absolutePath);
20305
20303
  alreadyImported.add(absolutePath);
20304
+ importStack.add(absolutePath);
20306
20305
  resolvedPaths.push(absolutePath);
20307
20306
  if (imp.path.endsWith(".nornapi")) {
20308
20307
  const apiDef = parseNornApiFile(content);
@@ -20335,7 +20334,7 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
20335
20334
  continue;
20336
20335
  }
20337
20336
  const importDir = path5.dirname(absolutePath);
20338
- const nestedResult = await resolveImports(content, importDir, readFile2, alreadyImported);
20337
+ const nestedResult = await resolveImports(content, importDir, readFile2, alreadyImported, importStack);
20339
20338
  errors.push(...nestedResult.errors);
20340
20339
  resolvedPaths.push(...nestedResult.resolvedPaths);
20341
20340
  for (const group of nestedResult.headerGroups) {
@@ -20367,7 +20366,7 @@ async function resolveImports(text, baseDir, readFile2, alreadyImported = /* @__
20367
20366
  if (nestedResult.importedContent) {
20368
20367
  importedContents.push(nestedResult.importedContent);
20369
20368
  }
20370
- const importedVariables = extractVariables(content);
20369
+ const importedVariables = extractFileLevelVariables(content);
20371
20370
  const importedNamedRequests = extractNamedRequests(content);
20372
20371
  const importedSequences = extractSequencesFromText(content);
20373
20372
  for (const req of importedNamedRequests) {
@@ -20409,6 +20408,8 @@ end sequence`);
20409
20408
  error: error.message || "Failed to read file",
20410
20409
  lineNumber: imp.lineNumber
20411
20410
  });
20411
+ } finally {
20412
+ importStack.delete(absolutePath);
20412
20413
  }
20413
20414
  }
20414
20415
  return {
@@ -27575,6 +27576,7 @@ function extractStepsFromSequence(content) {
27575
27576
  const lines = content.split("\n");
27576
27577
  const steps = [];
27577
27578
  const methodRegex = /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+/i;
27579
+ const stepBoundaryRegex = /^(###|(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+|(test\s+)?sequence\s+|end\s+sequence|end\s+if|endif\b|var\s+|run\s+|print\s+|assert\s+|if\s+|wait\s+|import\s+|return\s+|\[)/i;
27578
27580
  let currentRequest = [];
27579
27581
  let currentRequestStartLine = -1;
27580
27582
  let inRequest = false;
@@ -27824,11 +27826,11 @@ function extractStepsFromSequence(content) {
27824
27826
  currentRequestStartLine = lineIdx;
27825
27827
  inRequest = true;
27826
27828
  } else if (inVarRequest) {
27827
- if (trimmed === "" || trimmed.match(/^[a-zA-Z_][a-zA-Z0-9_]*\s*[=:]\s*.+$/) || trimmed.startsWith("{") || trimmed.startsWith('"') || trimmed.startsWith("[")) {
27828
- currentVarRequest.push(trimmed);
27829
- } else {
27829
+ if (trimmed !== "" && stepBoundaryRegex.test(trimmed)) {
27830
27830
  savePendingVarRequest();
27831
27831
  lineIdx--;
27832
+ } else {
27833
+ currentVarRequest.push(trimmed);
27832
27834
  }
27833
27835
  } else if (inRequest) {
27834
27836
  if (trimmed.match(/^var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*\$\d+/)) {
@@ -28328,9 +28330,15 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
28328
28330
  }
28329
28331
  const contentLines = step.content.split("\n");
28330
28332
  if (contentLines.length > 1) {
28331
- const bodyLines = contentLines.slice(1);
28332
- const resolvedBodyLines = bodyLines.map((line2) => substituteVariables(line2, runtimeVariables));
28333
- requestText += "\n\n" + resolvedBodyLines.join("\n");
28333
+ const extraLines = contentLines.slice(1).map((line2) => substituteVariables(line2, runtimeVariables));
28334
+ const hasExplicitBlank = extraLines.some((line2) => line2.trim() === "");
28335
+ const firstNonEmpty = extraLines.find((line2) => line2.trim() !== "");
28336
+ const startsWithHeader = firstNonEmpty ? /^[A-Za-z0-9\-_]+\s*:/.test(firstNonEmpty.trim()) : false;
28337
+ if (hasExplicitBlank || startsWithHeader) {
28338
+ requestText += "\n" + extraLines.join("\n");
28339
+ } else {
28340
+ requestText += "\n\n" + extraLines.join("\n");
28341
+ }
28334
28342
  }
28335
28343
  const requestParsed = parserHttpRequest(requestText, runtimeVariables);
28336
28344
  const retryOpts = parsed.retryCount ? {
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.3.15",
5
+ "version": "1.3.17",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"