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.
- package/CHANGELOG.md +18 -0
- package/dist/cli.js +48 -40
- 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
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
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
|
|
20099
|
+
let sequenceDepth = 0;
|
|
20108
20100
|
for (const line2 of lines) {
|
|
20109
20101
|
const trimmed = line2.trim();
|
|
20110
|
-
if (
|
|
20111
|
-
|
|
20102
|
+
if (isSequenceStartDeclaration(trimmed)) {
|
|
20103
|
+
sequenceDepth++;
|
|
20112
20104
|
continue;
|
|
20113
20105
|
}
|
|
20114
|
-
if (trimmed
|
|
20115
|
-
|
|
20106
|
+
if (isSequenceEndDeclaration(trimmed)) {
|
|
20107
|
+
sequenceDepth = Math.max(0, sequenceDepth - 1);
|
|
20116
20108
|
continue;
|
|
20117
20109
|
}
|
|
20118
|
-
if (
|
|
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 (
|
|
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 =
|
|
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
|
|
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
|
|
28332
|
-
const
|
|
28333
|
-
|
|
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.
|
|
5
|
+
"version": "1.3.17",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|