norn-cli 1.3.10 → 1.3.12
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/.norn-cache/validation-results.json +2 -2
- package/CHANGELOG.md +17 -0
- package/dist/cli.js +83 -19
- package/package.json +1 -1
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
"sourceFile": "tests/Regression/15-contract-validation.norn",
|
|
7
7
|
"assertionLine": 15,
|
|
8
8
|
"status": "pass",
|
|
9
|
-
"lastRunTime": "2026-02-
|
|
9
|
+
"lastRunTime": "2026-02-09T20:01:50.112Z"
|
|
10
10
|
},
|
|
11
11
|
"tests/Regression/15-contract-validation.norn:25": {
|
|
12
12
|
"schemaPath": "tests/Regression/schemas/GET-users-1.schema.json",
|
|
13
13
|
"sourceFile": "tests/Regression/15-contract-validation.norn",
|
|
14
14
|
"assertionLine": 25,
|
|
15
15
|
"status": "pass",
|
|
16
|
-
"lastRunTime": "2026-02-
|
|
16
|
+
"lastRunTime": "2026-02-09T20:01:54.179Z"
|
|
17
17
|
},
|
|
18
18
|
"tests/Regression/15-contract-validation.norn:35": {
|
|
19
19
|
"schemaPath": "tests/Regression/schemas/GET-todos-1.schema.json",
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the "Norn" extension will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.3.12] - 2026-02-10
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Variable Request with Form Body**: Fixed `var x = POST EndpointName HeaderGroup` not passing URL-encoded body
|
|
9
|
+
- Body lines following the var request command are now correctly collected and sent
|
|
10
|
+
- Example: `var result = POST PostFormData FormData` followed by `key=value` lines now works
|
|
11
|
+
|
|
12
|
+
## [1.3.11] - 2026-02-09
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Syntax Highlighting**: Fixed header value colors in sequences
|
|
16
|
+
- `Content-Type: application/json` now correctly colors the entire value
|
|
17
|
+
- `/json` part no longer incorrectly highlighted as URL
|
|
18
|
+
- **Syntax Highlighting**: Fixed numbers in variable names being highlighted separately
|
|
19
|
+
- `item3.status` now shows as single variable color instead of `item` + `3` in different colors
|
|
20
|
+
- Added proper variable pattern matching in assert statements
|
|
21
|
+
|
|
5
22
|
## [1.3.10] - 2026-02-09
|
|
6
23
|
|
|
7
24
|
### Fixed
|
package/dist/cli.js
CHANGED
|
@@ -20338,7 +20338,7 @@ ${resolvedContent}`);
|
|
|
20338
20338
|
lineNumber: imp.lineNumber
|
|
20339
20339
|
});
|
|
20340
20340
|
} else {
|
|
20341
|
-
sequenceSources.set(lowerName,
|
|
20341
|
+
sequenceSources.set(lowerName, absolutePath);
|
|
20342
20342
|
const resolvedContent = substituteVariables(seq.content, importedVariables);
|
|
20343
20343
|
importedContents.push(`sequence ${seq.name}
|
|
20344
20344
|
${resolvedContent}
|
|
@@ -20358,7 +20358,8 @@ end sequence`);
|
|
|
20358
20358
|
errors,
|
|
20359
20359
|
resolvedPaths,
|
|
20360
20360
|
headerGroups,
|
|
20361
|
-
endpoints
|
|
20361
|
+
endpoints,
|
|
20362
|
+
sequenceSources
|
|
20362
20363
|
};
|
|
20363
20364
|
}
|
|
20364
20365
|
function extractSequencesFromText(text) {
|
|
@@ -27519,6 +27520,20 @@ function extractStepsFromSequence(content) {
|
|
|
27519
27520
|
let currentRequest = [];
|
|
27520
27521
|
let currentRequestStartLine = -1;
|
|
27521
27522
|
let inRequest = false;
|
|
27523
|
+
let currentVarRequest = [];
|
|
27524
|
+
let currentVarRequestStartLine = -1;
|
|
27525
|
+
let inVarRequest = false;
|
|
27526
|
+
const savePendingVarRequest = () => {
|
|
27527
|
+
if (currentVarRequest.length > 0) {
|
|
27528
|
+
steps.push({
|
|
27529
|
+
type: "varRequest",
|
|
27530
|
+
content: currentVarRequest.join("\n"),
|
|
27531
|
+
lineNumber: currentVarRequestStartLine
|
|
27532
|
+
});
|
|
27533
|
+
currentVarRequest = [];
|
|
27534
|
+
inVarRequest = false;
|
|
27535
|
+
}
|
|
27536
|
+
};
|
|
27522
27537
|
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
27523
27538
|
const line2 = lines[lineIdx];
|
|
27524
27539
|
const trimmed = line2.trim();
|
|
@@ -27532,6 +27547,7 @@ function extractStepsFromSequence(content) {
|
|
|
27532
27547
|
currentRequest = [];
|
|
27533
27548
|
inRequest = false;
|
|
27534
27549
|
}
|
|
27550
|
+
savePendingVarRequest();
|
|
27535
27551
|
steps.push({
|
|
27536
27552
|
type: "assertion",
|
|
27537
27553
|
content: trimmed,
|
|
@@ -27549,6 +27565,7 @@ function extractStepsFromSequence(content) {
|
|
|
27549
27565
|
currentRequest = [];
|
|
27550
27566
|
inRequest = false;
|
|
27551
27567
|
}
|
|
27568
|
+
savePendingVarRequest();
|
|
27552
27569
|
const condition = parseIfCondition(trimmed);
|
|
27553
27570
|
steps.push({
|
|
27554
27571
|
type: "if",
|
|
@@ -27567,6 +27584,7 @@ function extractStepsFromSequence(content) {
|
|
|
27567
27584
|
currentRequest = [];
|
|
27568
27585
|
inRequest = false;
|
|
27569
27586
|
}
|
|
27587
|
+
savePendingVarRequest();
|
|
27570
27588
|
steps.push({
|
|
27571
27589
|
type: "endif",
|
|
27572
27590
|
content: "",
|
|
@@ -27584,6 +27602,7 @@ function extractStepsFromSequence(content) {
|
|
|
27584
27602
|
currentRequest = [];
|
|
27585
27603
|
inRequest = false;
|
|
27586
27604
|
}
|
|
27605
|
+
savePendingVarRequest();
|
|
27587
27606
|
steps.push({
|
|
27588
27607
|
type: "print",
|
|
27589
27608
|
content: trimmed,
|
|
@@ -27602,6 +27621,7 @@ function extractStepsFromSequence(content) {
|
|
|
27602
27621
|
currentRequest = [];
|
|
27603
27622
|
inRequest = false;
|
|
27604
27623
|
}
|
|
27624
|
+
savePendingVarRequest();
|
|
27605
27625
|
steps.push({
|
|
27606
27626
|
type: "wait",
|
|
27607
27627
|
content: trimmed,
|
|
@@ -27619,6 +27639,7 @@ function extractStepsFromSequence(content) {
|
|
|
27619
27639
|
currentRequest = [];
|
|
27620
27640
|
inRequest = false;
|
|
27621
27641
|
}
|
|
27642
|
+
savePendingVarRequest();
|
|
27622
27643
|
steps.push({
|
|
27623
27644
|
type: "json",
|
|
27624
27645
|
content: trimmed,
|
|
@@ -27636,6 +27657,7 @@ function extractStepsFromSequence(content) {
|
|
|
27636
27657
|
currentRequest = [];
|
|
27637
27658
|
inRequest = false;
|
|
27638
27659
|
}
|
|
27660
|
+
savePendingVarRequest();
|
|
27639
27661
|
steps.push({
|
|
27640
27662
|
type: "propAssign",
|
|
27641
27663
|
content: trimmed,
|
|
@@ -27653,6 +27675,7 @@ function extractStepsFromSequence(content) {
|
|
|
27653
27675
|
currentRequest = [];
|
|
27654
27676
|
inRequest = false;
|
|
27655
27677
|
}
|
|
27678
|
+
savePendingVarRequest();
|
|
27656
27679
|
steps.push({
|
|
27657
27680
|
type: "script",
|
|
27658
27681
|
content: trimmed,
|
|
@@ -27670,6 +27693,7 @@ function extractStepsFromSequence(content) {
|
|
|
27670
27693
|
currentRequest = [];
|
|
27671
27694
|
inRequest = false;
|
|
27672
27695
|
}
|
|
27696
|
+
savePendingVarRequest();
|
|
27673
27697
|
steps.push({
|
|
27674
27698
|
type: "varRunSequence",
|
|
27675
27699
|
content: trimmed,
|
|
@@ -27687,6 +27711,7 @@ function extractStepsFromSequence(content) {
|
|
|
27687
27711
|
currentRequest = [];
|
|
27688
27712
|
inRequest = false;
|
|
27689
27713
|
}
|
|
27714
|
+
savePendingVarRequest();
|
|
27690
27715
|
steps.push({
|
|
27691
27716
|
type: "namedRequest",
|
|
27692
27717
|
content: trimmed,
|
|
@@ -27704,11 +27729,10 @@ function extractStepsFromSequence(content) {
|
|
|
27704
27729
|
currentRequest = [];
|
|
27705
27730
|
inRequest = false;
|
|
27706
27731
|
}
|
|
27707
|
-
|
|
27708
|
-
|
|
27709
|
-
|
|
27710
|
-
|
|
27711
|
-
});
|
|
27732
|
+
savePendingVarRequest();
|
|
27733
|
+
currentVarRequest = [trimmed];
|
|
27734
|
+
currentVarRequestStartLine = lineIdx;
|
|
27735
|
+
inVarRequest = true;
|
|
27712
27736
|
continue;
|
|
27713
27737
|
}
|
|
27714
27738
|
if (isVarAssignCommand(trimmed)) {
|
|
@@ -27721,6 +27745,7 @@ function extractStepsFromSequence(content) {
|
|
|
27721
27745
|
currentRequest = [];
|
|
27722
27746
|
inRequest = false;
|
|
27723
27747
|
}
|
|
27748
|
+
savePendingVarRequest();
|
|
27724
27749
|
steps.push({
|
|
27725
27750
|
type: "varAssign",
|
|
27726
27751
|
content: trimmed,
|
|
@@ -27736,9 +27761,17 @@ function extractStepsFromSequence(content) {
|
|
|
27736
27761
|
lineNumber: currentRequestStartLine
|
|
27737
27762
|
});
|
|
27738
27763
|
}
|
|
27764
|
+
savePendingVarRequest();
|
|
27739
27765
|
currentRequest = [trimmed];
|
|
27740
27766
|
currentRequestStartLine = lineIdx;
|
|
27741
27767
|
inRequest = true;
|
|
27768
|
+
} else if (inVarRequest) {
|
|
27769
|
+
if (trimmed === "" || trimmed.match(/^[a-zA-Z_][a-zA-Z0-9_]*\s*[=:]\s*.+$/) || trimmed.startsWith("{") || trimmed.startsWith('"') || trimmed.startsWith("[")) {
|
|
27770
|
+
currentVarRequest.push(trimmed);
|
|
27771
|
+
} else {
|
|
27772
|
+
savePendingVarRequest();
|
|
27773
|
+
lineIdx--;
|
|
27774
|
+
}
|
|
27742
27775
|
} else if (inRequest) {
|
|
27743
27776
|
if (trimmed.match(/^var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*\$\d+/)) {
|
|
27744
27777
|
if (currentRequest.length > 0) {
|
|
@@ -27770,6 +27803,7 @@ function extractStepsFromSequence(content) {
|
|
|
27770
27803
|
lineNumber: currentRequestStartLine
|
|
27771
27804
|
});
|
|
27772
27805
|
}
|
|
27806
|
+
savePendingVarRequest();
|
|
27773
27807
|
return steps;
|
|
27774
27808
|
}
|
|
27775
27809
|
function extractCaptureDirectives(content) {
|
|
@@ -27836,7 +27870,7 @@ function getValueByPath(response, path5) {
|
|
|
27836
27870
|
return void 0;
|
|
27837
27871
|
}
|
|
27838
27872
|
}
|
|
27839
|
-
async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, workingDir, fullDocumentText, onProgress, callStack, sequenceArgs, apiDefinitions, tagFilterOptions) {
|
|
27873
|
+
async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, workingDir, fullDocumentText, onProgress, callStack, sequenceArgs, apiDefinitions, tagFilterOptions, sequenceSources) {
|
|
27840
27874
|
const startTime = Date.now();
|
|
27841
27875
|
const responses = [];
|
|
27842
27876
|
const scriptResults = [];
|
|
@@ -28155,9 +28189,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28155
28189
|
continue;
|
|
28156
28190
|
}
|
|
28157
28191
|
if (step.type === "varRequest") {
|
|
28158
|
-
const
|
|
28192
|
+
const commandLine = step.content.split("\n")[0];
|
|
28193
|
+
const parsed = parseVarRequestCommand(commandLine);
|
|
28159
28194
|
if (!parsed) {
|
|
28160
|
-
errors.push(`Step ${stepIdx + 1}: Invalid variable request: ${
|
|
28195
|
+
errors.push(`Step ${stepIdx + 1}: Invalid variable request: ${commandLine}`);
|
|
28161
28196
|
return {
|
|
28162
28197
|
name: "",
|
|
28163
28198
|
success: false,
|
|
@@ -28233,6 +28268,12 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28233
28268
|
const headerLines = Object.entries(resolvedHeaders).map(([k, v]) => `${k}: ${v}`);
|
|
28234
28269
|
requestText += "\n" + headerLines.join("\n");
|
|
28235
28270
|
}
|
|
28271
|
+
const contentLines = step.content.split("\n");
|
|
28272
|
+
if (contentLines.length > 1) {
|
|
28273
|
+
const bodyLines = contentLines.slice(1);
|
|
28274
|
+
const resolvedBodyLines = bodyLines.map((line2) => substituteVariables(line2, runtimeVariables));
|
|
28275
|
+
requestText += "\n\n" + resolvedBodyLines.join("\n");
|
|
28276
|
+
}
|
|
28236
28277
|
const requestParsed = parserHttpRequest(requestText, runtimeVariables);
|
|
28237
28278
|
const retryOpts = parsed.retryCount ? {
|
|
28238
28279
|
retryCount: parsed.retryCount,
|
|
@@ -28426,12 +28467,20 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28426
28467
|
}
|
|
28427
28468
|
}
|
|
28428
28469
|
reportProgress(stepIdx, "sequenceStart", `Starting sequence ${targetName}`, void 0, targetName);
|
|
28470
|
+
let subWorkingDir = workingDir;
|
|
28471
|
+
if (sequenceSources) {
|
|
28472
|
+
const sourceFile = sequenceSources.get(targetName.toLowerCase());
|
|
28473
|
+
if (sourceFile) {
|
|
28474
|
+
const path5 = await import("path");
|
|
28475
|
+
subWorkingDir = path5.dirname(sourceFile);
|
|
28476
|
+
}
|
|
28477
|
+
}
|
|
28429
28478
|
const subResult = await runSequenceWithJar(
|
|
28430
28479
|
targetSequence.content,
|
|
28431
28480
|
runtimeVariables,
|
|
28432
28481
|
// Pass current runtime variables (includes file vars + any captured)
|
|
28433
28482
|
cookieJar,
|
|
28434
|
-
|
|
28483
|
+
subWorkingDir,
|
|
28435
28484
|
fullDocumentText,
|
|
28436
28485
|
onProgress,
|
|
28437
28486
|
// Pass through progress callback
|
|
@@ -28441,8 +28490,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28441
28490
|
// Pass bound arguments as initial scope variables
|
|
28442
28491
|
apiDefinitions,
|
|
28443
28492
|
// Pass API definitions for endpoint resolution
|
|
28444
|
-
tagFilterOptions
|
|
28493
|
+
tagFilterOptions,
|
|
28445
28494
|
// Pass tag filter options for nested sequence filtering
|
|
28495
|
+
sequenceSources
|
|
28496
|
+
// Pass sequence sources for nested sequences
|
|
28446
28497
|
);
|
|
28447
28498
|
reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${targetName}`, void 0, targetName);
|
|
28448
28499
|
for (const subResponse of subResult.responses) {
|
|
@@ -28751,11 +28802,19 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28751
28802
|
}
|
|
28752
28803
|
}
|
|
28753
28804
|
reportProgress(stepIdx, "sequenceStart", `Starting sequence ${sequenceName}`, void 0, sequenceName);
|
|
28805
|
+
let subWorkingDir = workingDir;
|
|
28806
|
+
if (sequenceSources) {
|
|
28807
|
+
const sourceFile = sequenceSources.get(sequenceName.toLowerCase());
|
|
28808
|
+
if (sourceFile) {
|
|
28809
|
+
const path5 = await import("path");
|
|
28810
|
+
subWorkingDir = path5.dirname(sourceFile);
|
|
28811
|
+
}
|
|
28812
|
+
}
|
|
28754
28813
|
const subResult = await runSequenceWithJar(
|
|
28755
28814
|
targetSequence.content,
|
|
28756
28815
|
runtimeVariables,
|
|
28757
28816
|
cookieJar,
|
|
28758
|
-
|
|
28817
|
+
subWorkingDir,
|
|
28759
28818
|
fullDocumentText,
|
|
28760
28819
|
onProgress,
|
|
28761
28820
|
[...currentCallStack, sequenceName],
|
|
@@ -28763,8 +28822,10 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28763
28822
|
// Pass bound arguments
|
|
28764
28823
|
apiDefinitions,
|
|
28765
28824
|
// Pass API definitions for endpoint resolution
|
|
28766
|
-
tagFilterOptions
|
|
28825
|
+
tagFilterOptions,
|
|
28767
28826
|
// Pass tag filter options for nested sequence filtering
|
|
28827
|
+
sequenceSources
|
|
28828
|
+
// Pass sequence sources for nested sequences
|
|
28768
28829
|
);
|
|
28769
28830
|
reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${sequenceName}`, void 0, sequenceName);
|
|
28770
28831
|
for (const subResponse of subResult.responses) {
|
|
@@ -30499,7 +30560,7 @@ function formatCaseLabel(params) {
|
|
|
30499
30560
|
});
|
|
30500
30561
|
return `[${parts.join(", ")}]`;
|
|
30501
30562
|
}
|
|
30502
|
-
async function runAllSequences(fileContent, variables, cookieJar, workingDir, apiDefinitions, tagFilterOptions) {
|
|
30563
|
+
async function runAllSequences(fileContent, variables, cookieJar, workingDir, apiDefinitions, tagFilterOptions, sequenceSources) {
|
|
30503
30564
|
const allSequences = extractSequences(fileContent);
|
|
30504
30565
|
const results = [];
|
|
30505
30566
|
const testSequences = allSequences.filter((seq) => seq.isTest);
|
|
@@ -30543,7 +30604,8 @@ async function runAllSequences(fileContent, variables, cookieJar, workingDir, ap
|
|
|
30543
30604
|
void 0,
|
|
30544
30605
|
caseArgs,
|
|
30545
30606
|
apiDefinitions,
|
|
30546
|
-
tagFilterOptions
|
|
30607
|
+
tagFilterOptions,
|
|
30608
|
+
sequenceSources
|
|
30547
30609
|
);
|
|
30548
30610
|
result.name = `${seq.name}${caseLabel}`;
|
|
30549
30611
|
results.push(result);
|
|
@@ -30567,7 +30629,8 @@ async function runAllSequences(fileContent, variables, cookieJar, workingDir, ap
|
|
|
30567
30629
|
void 0,
|
|
30568
30630
|
defaultArgs,
|
|
30569
30631
|
apiDefinitions,
|
|
30570
|
-
tagFilterOptions
|
|
30632
|
+
tagFilterOptions,
|
|
30633
|
+
sequenceSources
|
|
30571
30634
|
);
|
|
30572
30635
|
result.name = seq.name;
|
|
30573
30636
|
results.push(result);
|
|
@@ -30742,7 +30805,8 @@ ${fileContent}` : fileContent;
|
|
|
30742
30805
|
void 0,
|
|
30743
30806
|
defaultArgs,
|
|
30744
30807
|
apiDefinitions,
|
|
30745
|
-
tagFilterOptions
|
|
30808
|
+
tagFilterOptions,
|
|
30809
|
+
importResult.sequenceSources
|
|
30746
30810
|
);
|
|
30747
30811
|
seqResult.name = targetSeq.name;
|
|
30748
30812
|
if (options.output === "json") {
|
|
@@ -30823,7 +30887,7 @@ ${fileContent}` : fileContent;
|
|
|
30823
30887
|
\u2501\u2501\u2501 ${relPath} \u2501\u2501\u2501`));
|
|
30824
30888
|
}
|
|
30825
30889
|
const apiDefinitions = (importResult.headerGroups?.length || 0) > 0 || (importResult.endpoints?.length || 0) > 0 ? { headerGroups: importResult.headerGroups || [], endpoints: importResult.endpoints || [] } : void 0;
|
|
30826
|
-
const seqResults = await runAllSequences(fileContentWithImports, variables, cookieJar, workingDir, apiDefinitions, tagFilterOptions);
|
|
30890
|
+
const seqResults = await runAllSequences(fileContentWithImports, variables, cookieJar, workingDir, apiDefinitions, tagFilterOptions, importResult.sequenceSources);
|
|
30827
30891
|
for (const result2 of seqResults) {
|
|
30828
30892
|
result2.sourceFile = filePath;
|
|
30829
30893
|
allResults.push(result2);
|
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.12",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|