norn-cli 1.10.1 → 1.10.2

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 +17 -0
  2. package/dist/cli.js +96 -73
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ All notable changes to the "Norn" extension will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.10.2] - 2026-03-15
8
+
9
+ ### Fixed
10
+ - **Sequence Return + Response Panel UX**:
11
+ - Fixed `var result = run SomeSequence` so single-value returns can capture literal values like `return "Bar"` instead of resolving to empty output.
12
+ - Fixed streaming/final response panel step timers so print/SQL rows show per-step duration instead of elapsed-from-start totals.
13
+ - Fixed the streaming step counter so hidden `wait` steps no longer inflate the top-bar step total.
14
+ - Fixed `.norn` syntax highlighting so quoted string literals after `return` use normal string coloring.
15
+
16
+ - **Request Parsing + CLI Error Output**:
17
+ - Fixed request parsing to stop bodies at the next Norn block boundary, preventing later named requests or sequence declarations from leaking into request bodies.
18
+ - Fixed CLI text and HTML reports to preserve multiline request/sequence error details instead of collapsing them into single-line output.
19
+
20
+ - **API Coverage + Import UX**:
21
+ - Fixed API coverage to follow status assertions on captured request variables and named requests that call `.nornapi` endpoints.
22
+ - Fixed the OpenAPI import section picker so selecting `All` stays mutually exclusive with individual section selections.
23
+
7
24
  ## [1.7.0] - 2026-03-07
8
25
 
9
26
  ### Added
package/dist/cli.js CHANGED
@@ -101801,6 +101801,16 @@ function getHeaderGroup(definition, name) {
101801
101801
  function getEndpoint(definition, name) {
101802
101802
  return definition.endpoints.find((ep) => ep.name === name);
101803
101803
  }
101804
+ function resolveEndpointPath(endpoint, params) {
101805
+ let resolvedPath = endpoint.path;
101806
+ for (const paramName of endpoint.parameters) {
101807
+ const value = params[paramName];
101808
+ if (value !== void 0) {
101809
+ resolvedPath = resolvedPath.replace(`{${paramName}}`, value);
101810
+ }
101811
+ }
101812
+ return resolvedPath;
101813
+ }
101804
101814
  function resolveHeaderValues(headerGroup, variables) {
101805
101815
  const resolved = {};
101806
101816
  for (const [name, value] of Object.entries(headerGroup.headers)) {
@@ -102283,7 +102293,7 @@ function extractNamedRequests(text) {
102283
102293
  let endLine = lines.length - 1;
102284
102294
  for (let j = contentStartLine; j < lines.length; j++) {
102285
102295
  const scanLine = lines[j].trim();
102286
- if (scanLine.match(/^\[/) || scanLine.match(/^sequence\s+/) || scanLine.startsWith("###")) {
102296
+ if (isNamedRequestDeclarationLine(scanLine) || isSequenceStartDeclaration(scanLine) || isSequenceEndDeclaration(scanLine) || scanLine.startsWith("###")) {
102287
102297
  endLine = j - 1;
102288
102298
  break;
102289
102299
  }
@@ -102499,10 +102509,9 @@ function parserHttpRequest(text, variables = {}) {
102499
102509
  let body;
102500
102510
  if (bodyStartIndex > 0) {
102501
102511
  const bodyLines = [];
102502
- const boundaryRegex = /^\[.*\]$|^sequence\s+|^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+/i;
102503
102512
  for (let i = bodyStartIndex; i < allLines.length; i++) {
102504
102513
  const line2 = allLines[i].trim();
102505
- if (boundaryRegex.test(line2)) {
102514
+ if (isRequestLine(line2) || isNamedRequestDeclarationLine(line2) || isSequenceLine(line2) || isEndSequenceLine(line2) || line2.startsWith("###")) {
102506
102515
  break;
102507
102516
  }
102508
102517
  if (line2 && !line2.startsWith("#") && !line2.startsWith("var ")) {
@@ -102516,6 +102525,19 @@ function parserHttpRequest(text, variables = {}) {
102516
102525
  }
102517
102526
  return { method: method.toUpperCase(), url: url2, headers, body, retryCount, backoffMs };
102518
102527
  }
102528
+ var METHOD_REGEX = /^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|TRACE|CONNECT)\s+/i;
102529
+ function isRequestLine(line2) {
102530
+ return METHOD_REGEX.test(line2.trim());
102531
+ }
102532
+ function isSequenceLine(line2) {
102533
+ return isSequenceStartDeclaration(line2.trim());
102534
+ }
102535
+ function isNamedRequestDeclarationLine(line2) {
102536
+ return /^\[[^\]]+\]\s*$/.test(line2.trim());
102537
+ }
102538
+ function isEndSequenceLine(line2) {
102539
+ return isSequenceEndDeclaration(line2.trim());
102540
+ }
102519
102541
  function extractImports(text) {
102520
102542
  const lines = text.split("\n");
102521
102543
  const imports = [];
@@ -110280,6 +110302,10 @@ function resolveReturnExpression(expr, runtimeVariables) {
110280
110302
  return { key: varName, value: varValue };
110281
110303
  }
110282
110304
  }
110305
+ function resolveSingleReturnValue(expr, runtimeVariables) {
110306
+ const result = evaluateSqlArgumentExpression(expr, runtimeVariables);
110307
+ return result.error ? "" : result.value;
110308
+ }
110283
110309
  function extractReturnVariables(content) {
110284
110310
  const lines = content.split("\n");
110285
110311
  for (const line2 of lines) {
@@ -110289,6 +110315,54 @@ function extractReturnVariables(content) {
110289
110315
  }
110290
110316
  return null;
110291
110317
  }
110318
+ function buildParsedNamedRequest(requestContent, runtimeVariables, apiDefinitions) {
110319
+ if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(requestContent, apiDefinitions.endpoints)) {
110320
+ const apiRequest = parseApiRequest(
110321
+ requestContent,
110322
+ apiDefinitions.endpoints,
110323
+ apiDefinitions.headerGroups
110324
+ );
110325
+ if (apiRequest) {
110326
+ const endpoint = getEndpoint(apiDefinitions, apiRequest.endpointName);
110327
+ if (!endpoint) {
110328
+ throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
110329
+ }
110330
+ const resolvedParams = {};
110331
+ for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
110332
+ if (runtimeVariables[paramValue] !== void 0) {
110333
+ resolvedParams[paramName] = String(runtimeVariables[paramValue]);
110334
+ } else {
110335
+ resolvedParams[paramName] = substituteVariables(paramValue, runtimeVariables);
110336
+ }
110337
+ }
110338
+ const resolvedPath = substituteVariables(
110339
+ resolveEndpointPath(endpoint, resolvedParams),
110340
+ runtimeVariables
110341
+ );
110342
+ const combinedHeaders = {};
110343
+ for (const groupName of apiRequest.headerGroupNames) {
110344
+ const group = getHeaderGroup(apiDefinitions, groupName);
110345
+ if (group) {
110346
+ Object.assign(combinedHeaders, resolveHeaderValues(group, runtimeVariables));
110347
+ }
110348
+ }
110349
+ for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
110350
+ combinedHeaders[headerName] = substituteVariables(headerValue, runtimeVariables);
110351
+ }
110352
+ return {
110353
+ method: apiRequest.method,
110354
+ url: resolvedPath,
110355
+ headers: combinedHeaders,
110356
+ body: apiRequest.body ? substituteVariables(apiRequest.body, runtimeVariables) : void 0
110357
+ };
110358
+ }
110359
+ }
110360
+ let parsed = parserHttpRequest(requestContent, runtimeVariables);
110361
+ if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
110362
+ parsed = applyHeaderGroupsToRequest(parsed, requestContent, apiDefinitions.headerGroups, runtimeVariables);
110363
+ }
110364
+ return parsed;
110365
+ }
110292
110366
  function parseRunArguments(argsStr) {
110293
110367
  const args = [];
110294
110368
  if (!argsStr || !argsStr.trim()) {
@@ -111662,6 +111736,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111662
111736
  for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
111663
111737
  const step = steps[stepIdx];
111664
111738
  await emitBeforeStep(step, stepIdx);
111739
+ const stepStartTime = Date.now();
111665
111740
  try {
111666
111741
  if (step.type === "if") {
111667
111742
  if (shouldSkip()) {
@@ -111672,6 +111747,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111672
111747
  const stepResult = {
111673
111748
  type: "print",
111674
111749
  stepIndex: stepIdx,
111750
+ durationMs: Date.now() - stepStartTime,
111675
111751
  print: {
111676
111752
  title: `if ${step.content}: ${conditionMet ? "true" : "false"}`,
111677
111753
  timestamp: Date.now() - startTime
@@ -111713,6 +111789,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111713
111789
  const stepResult = {
111714
111790
  type: "assertion",
111715
111791
  stepIndex: stepIdx,
111792
+ durationMs: Date.now() - stepStartTime,
111716
111793
  assertion: result,
111717
111794
  lineNumber: step.lineNumber
111718
111795
  };
@@ -111721,13 +111798,6 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111721
111798
  reportProgress(stepIdx, "assertion", `${statusIcon} assert ${result.expression}`, stepResult);
111722
111799
  if (!result.passed) {
111723
111800
  const failMessage = result.message ? `Assertion failed: ${result.message}` : `Assertion failed: ${result.expression}`;
111724
- errors.push(failMessage);
111725
- if (result.error) {
111726
- errors.push(` Error: ${result.error}`);
111727
- } else {
111728
- errors.push(` Expected: ${result.rightExpression ?? result.operator}`);
111729
- errors.push(` Actual: ${JSON.stringify(result.leftValue)}`);
111730
- }
111731
111801
  await emitFailure(failMessage, step, stepIdx);
111732
111802
  return {
111733
111803
  name: "",
@@ -111751,6 +111821,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111751
111821
  const stepResult = {
111752
111822
  type: "print",
111753
111823
  stepIndex: stepIdx,
111824
+ durationMs: Date.now() - stepStartTime,
111754
111825
  print: {
111755
111826
  title,
111756
111827
  body,
@@ -111768,6 +111839,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111768
111839
  const stepResult = {
111769
111840
  type: "print",
111770
111841
  stepIndex: stepIdx,
111842
+ durationMs: Date.now() - stepStartTime,
111771
111843
  print: {
111772
111844
  title: `Waited ${durationMs >= 1e3 ? durationMs / 1e3 + "s" : durationMs + "ms"}`,
111773
111845
  timestamp: Date.now() - startTime
@@ -111796,6 +111868,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111796
111868
  const stepResult = {
111797
111869
  type: "json",
111798
111870
  stepIndex: stepIdx,
111871
+ durationMs: Date.now() - stepStartTime,
111799
111872
  json: {
111800
111873
  varName: parsed.varName,
111801
111874
  filePath: result.filePath,
@@ -112074,6 +112147,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
112074
112147
  const stepResult = {
112075
112148
  type: "sql",
112076
112149
  stepIndex: stepIdx,
112150
+ durationMs: Date.now() - stepStartTime,
112077
112151
  sql: sqlResult,
112078
112152
  variableName: parsed.variableName,
112079
112153
  lineNumber: step.lineNumber
@@ -112479,60 +112553,7 @@ ${indentMultiline(userMessage)}`;
112479
112553
  let requestUrl = "";
112480
112554
  let requestMethod = "";
112481
112555
  try {
112482
- let requestParsed;
112483
- if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(namedRequest.content, apiDefinitions.endpoints)) {
112484
- const apiRequest = parseApiRequest(
112485
- namedRequest.content,
112486
- apiDefinitions.endpoints,
112487
- apiDefinitions.headerGroups
112488
- );
112489
- if (apiRequest) {
112490
- const endpoint = getEndpoint(
112491
- { headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
112492
- apiRequest.endpointName
112493
- );
112494
- if (endpoint) {
112495
- let resolvedPath = substituteVariables(endpoint.path, runtimeVariables);
112496
- for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
112497
- let resolvedValue = paramValue;
112498
- if (runtimeVariables[paramValue] !== void 0) {
112499
- resolvedValue = String(runtimeVariables[paramValue]);
112500
- } else {
112501
- resolvedValue = substituteVariables(paramValue, runtimeVariables);
112502
- }
112503
- resolvedPath = resolvedPath.replace(`{${paramName}}`, resolvedValue);
112504
- }
112505
- const combinedHeaders = {};
112506
- for (const groupName of apiRequest.headerGroupNames) {
112507
- const group = getHeaderGroup(apiDefinitions, groupName);
112508
- if (group) {
112509
- const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
112510
- Object.assign(combinedHeaders, resolvedHeaders);
112511
- }
112512
- }
112513
- for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
112514
- const resolved = substituteVariables(headerValue, runtimeVariables);
112515
- combinedHeaders[headerName] = resolved;
112516
- }
112517
- const bodyParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
112518
- requestParsed = {
112519
- method: apiRequest.method,
112520
- url: resolvedPath,
112521
- headers: combinedHeaders,
112522
- body: bodyParsed.body
112523
- };
112524
- } else {
112525
- throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
112526
- }
112527
- } else {
112528
- requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
112529
- }
112530
- } else {
112531
- requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
112532
- if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
112533
- requestParsed = applyHeaderGroupsToRequest(requestParsed, namedRequest.content, apiDefinitions.headerGroups, runtimeVariables);
112534
- }
112535
- }
112556
+ const requestParsed = buildParsedNamedRequest(namedRequest.content, runtimeVariables, apiDefinitions);
112536
112557
  requestUrl = requestParsed.url;
112537
112558
  requestMethod = requestParsed.method;
112538
112559
  validatePreparedRequest(
@@ -112642,7 +112663,7 @@ ${indentMultiline(userMessage)}`;
112642
112663
  let requestUrl = "";
112643
112664
  let requestMethod = "";
112644
112665
  try {
112645
- const requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
112666
+ const requestParsed = buildParsedNamedRequest(namedRequest.content, runtimeVariables, apiDefinitions);
112646
112667
  requestUrl = requestParsed.url;
112647
112668
  requestMethod = requestParsed.method;
112648
112669
  validatePreparedRequest(
@@ -112802,12 +112823,7 @@ ${indentMultiline(userMessage)}`;
112802
112823
  const returnExpressions = extractReturnVariables(targetSequence.content);
112803
112824
  if (returnExpressions && returnExpressions.length > 0 && subResult.variables) {
112804
112825
  if (returnExpressions.length === 1) {
112805
- const resolved = resolveReturnExpression(returnExpressions[0], subResult.variables);
112806
- if (resolved) {
112807
- runtimeVariables[varName] = resolved.value;
112808
- } else {
112809
- runtimeVariables[varName] = "";
112810
- }
112826
+ runtimeVariables[varName] = resolveSingleReturnValue(returnExpressions[0], subResult.variables);
112811
112827
  } else {
112812
112828
  const returnedObj = {};
112813
112829
  for (const expr of returnExpressions) {
@@ -113414,7 +113430,14 @@ function formatSequenceResult(result, options) {
113414
113430
  lines.push("");
113415
113431
  lines.push(colors.warning("Warnings/Errors:"));
113416
113432
  for (const err of result.errors) {
113417
- lines.push(` ${colors.bullet} ${err}`);
113433
+ const errLines = String(err ?? "").split("\n");
113434
+ if (errLines.length === 0) {
113435
+ continue;
113436
+ }
113437
+ lines.push(` ${colors.bullet} ${errLines[0]}`);
113438
+ for (const line2 of errLines.slice(1)) {
113439
+ lines.push(` ${line2}`);
113440
+ }
113418
113441
  }
113419
113442
  }
113420
113443
  return lines;
@@ -113976,7 +113999,7 @@ function generateErrorsHtml(errors, redaction) {
113976
113999
  <div class="errors">
113977
114000
  <h4>Errors</h4>
113978
114001
  <ul>
113979
- ${errors.map((e) => `<li>${escapeHtml(redactString(e, redaction))}</li>`).join("\n")}
114002
+ ${errors.map((e) => `<li>${escapeHtml(redactString(e, redaction)).replace(/\n/g, "<br>")}</li>`).join("\n")}
113980
114003
  </ul>
113981
114004
  </div>`;
113982
114005
  }
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.10.1",
5
+ "version": "1.10.2",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"