norn-cli 1.10.1 → 1.10.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cli.js +194 -82
  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 = [];
@@ -109438,6 +109460,9 @@ function getConnectionValue(values, ...keys) {
109438
109460
  function getConnectionString(values) {
109439
109461
  return getConnectionValue(values, "connectionString");
109440
109462
  }
109463
+ function usesWindowsIntegratedSqlServerAuth(connectionString) {
109464
+ return /(?:^|;)\s*Integrated\s+Security\s*=\s*(?:true|yes|sspi)\s*(?:;|$)/i.test(connectionString) || /(?:^|;)\s*Trusted_Connection\s*=\s*(?:true|yes|sspi)\s*(?:;|$)/i.test(connectionString);
109465
+ }
109441
109466
  function buildMissingConnectionError(adapterLabel, profile) {
109442
109467
  return `Missing required ${adapterLabel} connection string. Expected 'connectionString ${profile} = ...' in .nornenv.`;
109443
109468
  }
@@ -109495,7 +109520,7 @@ if ([string]::IsNullOrWhiteSpace($raw)) {
109495
109520
  throw 'Missing SQL adapter payload.'
109496
109521
  }
109497
109522
 
109498
- $payload = $raw | ConvertFrom-Json -Depth 100
109523
+ $payload = $raw | ConvertFrom-Json
109499
109524
  $connectionString = [string]$payload.connection.values.connectionString
109500
109525
  if ([string]::IsNullOrWhiteSpace($connectionString)) {
109501
109526
  throw 'Missing SQL Server Integrated Auth connection string.'
@@ -109526,9 +109551,13 @@ $resolveHandler = [System.ResolveEventHandler]{
109526
109551
  $sqlText = [string]$payload.operation.sql
109527
109552
  $mode = [string]$payload.mode
109528
109553
  $paramsObject = $payload.params
109554
+ $connectionStringHasUserId = $connectionString -match '(?i)(?:^|;)s*(?:User ID|UID)s*='
109555
+ $connectionStringHasPassword = $connectionString -match '(?i)(?:^|;)s*(?:Password|PWD)s*='
109529
109556
 
109530
109557
  $connection = $null
109558
+ $connectionBuilder = $null
109531
109559
  try {
109560
+ $connectionBuilder = New-Object Microsoft.Data.SqlClient.SqlConnectionStringBuilder $connectionString
109532
109561
  $connection = New-Object Microsoft.Data.SqlClient.SqlConnection $connectionString
109533
109562
  $command = $connection.CreateCommand()
109534
109563
  $command.CommandText = $sqlText
@@ -109600,18 +109629,97 @@ try {
109600
109629
  $response | ConvertTo-Json -Depth 100 -Compress
109601
109630
  }
109602
109631
  catch {
109603
- $details = @()
109632
+ $details = New-Object System.Collections.Generic.List[string]
109604
109633
  if ($_.Exception -and $_.Exception.GetType()) {
109605
- $details += "type: $($_.Exception.GetType().FullName)"
109634
+ $details.Add("type: $($_.Exception.GetType().FullName)")
109606
109635
  }
109607
109636
  if ($_.InvocationInfo -and $_.InvocationInfo.ScriptLineNumber) {
109608
- $details += "line: $($_.InvocationInfo.ScriptLineNumber)"
109637
+ $details.Add("line: $($_.InvocationInfo.ScriptLineNumber)")
109609
109638
  }
109610
109639
  if ($_.FullyQualifiedErrorId) {
109611
- $details += "id: $($_.FullyQualifiedErrorId)"
109640
+ $details.Add("id: $($_.FullyQualifiedErrorId)")
109641
+ }
109642
+
109643
+ try {
109644
+ $details.Add("adapter: sqlserver-windows")
109645
+ $details.Add("powershell: $($PSVersionTable.PSVersion)")
109646
+ $details.Add("sqlClientVersion: $(([Microsoft.Data.SqlClient.SqlConnection]).Assembly.GetName().Version)")
109647
+ $details.Add("windowsIdentity: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)")
109648
+ } catch {
109649
+ }
109650
+
109651
+ if ($null -ne $connectionBuilder) {
109652
+ if (-not [string]::IsNullOrWhiteSpace([string]$connectionBuilder.DataSource)) {
109653
+ $details.Add("dataSource: $($connectionBuilder.DataSource)")
109654
+ }
109655
+ if (-not [string]::IsNullOrWhiteSpace([string]$connectionBuilder.InitialCatalog)) {
109656
+ $details.Add("database: $($connectionBuilder.InitialCatalog)")
109657
+ }
109658
+ $details.Add("integratedSecurity: $($connectionBuilder.IntegratedSecurity)")
109659
+ $details.Add("encrypt: $($connectionBuilder.Encrypt)")
109660
+ $details.Add("trustServerCertificate: $($connectionBuilder.TrustServerCertificate)")
109661
+ $details.Add("applicationIntent: $($connectionBuilder.ApplicationIntent)")
109662
+ $details.Add("multiSubnetFailover: $($connectionBuilder.MultiSubnetFailover)")
109663
+
109664
+ try {
109665
+ $hostNameInCertificate = [string]$connectionBuilder['HostNameInCertificate']
109666
+ if (-not [string]::IsNullOrWhiteSpace($hostNameInCertificate)) {
109667
+ $details.Add("hostNameInCertificate: $hostNameInCertificate")
109668
+ }
109669
+ } catch {
109670
+ }
109671
+ }
109672
+
109673
+ $details.Add("userIdPresent: $connectionStringHasUserId")
109674
+ $details.Add("passwordPresent: $connectionStringHasPassword")
109675
+
109676
+ $sqlException = $null
109677
+ if ($_.Exception -is [Microsoft.Data.SqlClient.SqlException]) {
109678
+ $sqlException = [Microsoft.Data.SqlClient.SqlException]$_.Exception
109679
+ } elseif ($_.Exception -and $_.Exception.InnerException -is [Microsoft.Data.SqlClient.SqlException]) {
109680
+ $sqlException = [Microsoft.Data.SqlClient.SqlException]$_.Exception.InnerException
109681
+ }
109682
+
109683
+ if ($null -ne $sqlException) {
109684
+ $details.Add("sqlNumber: $($sqlException.Number)")
109685
+ $details.Add("sqlState: $($sqlException.State)")
109686
+ $details.Add("sqlClass: $($sqlException.Class)")
109687
+ if (-not [string]::IsNullOrWhiteSpace([string]$sqlException.Server)) {
109688
+ $details.Add("sqlServer: $($sqlException.Server)")
109689
+ }
109690
+ if (-not [string]::IsNullOrWhiteSpace([string]$sqlException.Procedure)) {
109691
+ $details.Add("sqlProcedure: $($sqlException.Procedure)")
109692
+ }
109693
+ if ($sqlException.LineNumber -gt 0) {
109694
+ $details.Add("sqlLineNumber: $($sqlException.LineNumber)")
109695
+ }
109696
+ if ($sqlException.ClientConnectionId -ne [guid]::Empty) {
109697
+ $details.Add("clientConnectionId: $($sqlException.ClientConnectionId)")
109698
+ }
109699
+ if ($sqlException.Errors -and $sqlException.Errors.Count -gt 0) {
109700
+ $messages = @()
109701
+ foreach ($sqlError in $sqlException.Errors) {
109702
+ if ($sqlError -and -not [string]::IsNullOrWhiteSpace([string]$sqlError.Message)) {
109703
+ $messages += [string]$sqlError.Message
109704
+ }
109705
+ }
109706
+ if ($messages.Count -gt 0) {
109707
+ $details.Add("sqlErrors: $($messages -join ' | ')")
109708
+ }
109709
+ }
109710
+ }
109711
+
109712
+ if ($_.Exception -and $_.Exception.InnerException) {
109713
+ $details.Add("innerType: $($_.Exception.InnerException.GetType().FullName)")
109714
+ if (-not [string]::IsNullOrWhiteSpace([string]$_.Exception.InnerException.Message)) {
109715
+ $details.Add("innerMessage: $($_.Exception.InnerException.Message)")
109716
+ }
109717
+ }
109718
+
109719
+ [Console]::Error.WriteLine("$($_.Exception.Message)")
109720
+ foreach ($detail in $details) {
109721
+ [Console]::Error.WriteLine(" $detail")
109612
109722
  }
109613
- $suffix = if ($details.Count -gt 0) { " ($($details -join ', '))" } else { '' }
109614
- [Console]::Error.WriteLine("$($_.Exception.Message)$suffix")
109615
109723
  exit 1
109616
109724
  }
109617
109725
  finally {
@@ -109772,6 +109880,9 @@ async function runBuiltInSqlServerAdapter(request) {
109772
109880
  if (!connectionString) {
109773
109881
  fail(buildMissingConnectionError("SQL Server", request.connection.profile));
109774
109882
  }
109883
+ if (usesWindowsIntegratedSqlServerAuth(connectionString)) {
109884
+ fail("This SQL Server connection string uses Windows/Integrated authentication. Use adapter 'sqlserver-windows' in norn.sql.json.");
109885
+ }
109775
109886
  const pool = new mssql.ConnectionPool(connectionString);
109776
109887
  try {
109777
109888
  await pool.connect();
@@ -109810,7 +109921,7 @@ async function runBuiltInSqlServerIntegratedAdapter(request) {
109810
109921
  const values = request.connection.values;
109811
109922
  const connectionString = getConnectionString(values);
109812
109923
  if (!connectionString) {
109813
- fail(buildMissingConnectionError("SQL Server Integrated Auth", request.connection.profile));
109924
+ fail(buildMissingConnectionError("SQL Server (Windows)", request.connection.profile));
109814
109925
  }
109815
109926
  const compiled = compileSqlServerSql(request.operation.sql, request.params || {});
109816
109927
  const compiledRequest = {
@@ -109824,7 +109935,7 @@ async function runBuiltInSqlServerIntegratedAdapter(request) {
109824
109935
  try {
109825
109936
  return await runSqlServerIntegratedViaPowerShell(compiledRequest);
109826
109937
  } catch (error) {
109827
- throw formatBuiltInDriverError("SQL Server Integrated Auth", error);
109938
+ throw formatBuiltInDriverError("SQL Server (Windows)", error);
109828
109939
  }
109829
109940
  }
109830
109941
  function isBuiltInSqlAdapter(adapterId) {
@@ -110280,6 +110391,10 @@ function resolveReturnExpression(expr, runtimeVariables) {
110280
110391
  return { key: varName, value: varValue };
110281
110392
  }
110282
110393
  }
110394
+ function resolveSingleReturnValue(expr, runtimeVariables) {
110395
+ const result = evaluateSqlArgumentExpression(expr, runtimeVariables);
110396
+ return result.error ? "" : result.value;
110397
+ }
110283
110398
  function extractReturnVariables(content) {
110284
110399
  const lines = content.split("\n");
110285
110400
  for (const line2 of lines) {
@@ -110289,6 +110404,54 @@ function extractReturnVariables(content) {
110289
110404
  }
110290
110405
  return null;
110291
110406
  }
110407
+ function buildParsedNamedRequest(requestContent, runtimeVariables, apiDefinitions) {
110408
+ if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(requestContent, apiDefinitions.endpoints)) {
110409
+ const apiRequest = parseApiRequest(
110410
+ requestContent,
110411
+ apiDefinitions.endpoints,
110412
+ apiDefinitions.headerGroups
110413
+ );
110414
+ if (apiRequest) {
110415
+ const endpoint = getEndpoint(apiDefinitions, apiRequest.endpointName);
110416
+ if (!endpoint) {
110417
+ throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
110418
+ }
110419
+ const resolvedParams = {};
110420
+ for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
110421
+ if (runtimeVariables[paramValue] !== void 0) {
110422
+ resolvedParams[paramName] = String(runtimeVariables[paramValue]);
110423
+ } else {
110424
+ resolvedParams[paramName] = substituteVariables(paramValue, runtimeVariables);
110425
+ }
110426
+ }
110427
+ const resolvedPath = substituteVariables(
110428
+ resolveEndpointPath(endpoint, resolvedParams),
110429
+ runtimeVariables
110430
+ );
110431
+ const combinedHeaders = {};
110432
+ for (const groupName of apiRequest.headerGroupNames) {
110433
+ const group = getHeaderGroup(apiDefinitions, groupName);
110434
+ if (group) {
110435
+ Object.assign(combinedHeaders, resolveHeaderValues(group, runtimeVariables));
110436
+ }
110437
+ }
110438
+ for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
110439
+ combinedHeaders[headerName] = substituteVariables(headerValue, runtimeVariables);
110440
+ }
110441
+ return {
110442
+ method: apiRequest.method,
110443
+ url: resolvedPath,
110444
+ headers: combinedHeaders,
110445
+ body: apiRequest.body ? substituteVariables(apiRequest.body, runtimeVariables) : void 0
110446
+ };
110447
+ }
110448
+ }
110449
+ let parsed = parserHttpRequest(requestContent, runtimeVariables);
110450
+ if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
110451
+ parsed = applyHeaderGroupsToRequest(parsed, requestContent, apiDefinitions.headerGroups, runtimeVariables);
110452
+ }
110453
+ return parsed;
110454
+ }
110292
110455
  function parseRunArguments(argsStr) {
110293
110456
  const args = [];
110294
110457
  if (!argsStr || !argsStr.trim()) {
@@ -111662,6 +111825,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111662
111825
  for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
111663
111826
  const step = steps[stepIdx];
111664
111827
  await emitBeforeStep(step, stepIdx);
111828
+ const stepStartTime = Date.now();
111665
111829
  try {
111666
111830
  if (step.type === "if") {
111667
111831
  if (shouldSkip()) {
@@ -111672,6 +111836,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111672
111836
  const stepResult = {
111673
111837
  type: "print",
111674
111838
  stepIndex: stepIdx,
111839
+ durationMs: Date.now() - stepStartTime,
111675
111840
  print: {
111676
111841
  title: `if ${step.content}: ${conditionMet ? "true" : "false"}`,
111677
111842
  timestamp: Date.now() - startTime
@@ -111713,6 +111878,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111713
111878
  const stepResult = {
111714
111879
  type: "assertion",
111715
111880
  stepIndex: stepIdx,
111881
+ durationMs: Date.now() - stepStartTime,
111716
111882
  assertion: result,
111717
111883
  lineNumber: step.lineNumber
111718
111884
  };
@@ -111721,13 +111887,6 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111721
111887
  reportProgress(stepIdx, "assertion", `${statusIcon} assert ${result.expression}`, stepResult);
111722
111888
  if (!result.passed) {
111723
111889
  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
111890
  await emitFailure(failMessage, step, stepIdx);
111732
111891
  return {
111733
111892
  name: "",
@@ -111751,6 +111910,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111751
111910
  const stepResult = {
111752
111911
  type: "print",
111753
111912
  stepIndex: stepIdx,
111913
+ durationMs: Date.now() - stepStartTime,
111754
111914
  print: {
111755
111915
  title,
111756
111916
  body,
@@ -111768,6 +111928,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111768
111928
  const stepResult = {
111769
111929
  type: "print",
111770
111930
  stepIndex: stepIdx,
111931
+ durationMs: Date.now() - stepStartTime,
111771
111932
  print: {
111772
111933
  title: `Waited ${durationMs >= 1e3 ? durationMs / 1e3 + "s" : durationMs + "ms"}`,
111773
111934
  timestamp: Date.now() - startTime
@@ -111796,6 +111957,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
111796
111957
  const stepResult = {
111797
111958
  type: "json",
111798
111959
  stepIndex: stepIdx,
111960
+ durationMs: Date.now() - stepStartTime,
111799
111961
  json: {
111800
111962
  varName: parsed.varName,
111801
111963
  filePath: result.filePath,
@@ -112074,6 +112236,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
112074
112236
  const stepResult = {
112075
112237
  type: "sql",
112076
112238
  stepIndex: stepIdx,
112239
+ durationMs: Date.now() - stepStartTime,
112077
112240
  sql: sqlResult,
112078
112241
  variableName: parsed.variableName,
112079
112242
  lineNumber: step.lineNumber
@@ -112479,60 +112642,7 @@ ${indentMultiline(userMessage)}`;
112479
112642
  let requestUrl = "";
112480
112643
  let requestMethod = "";
112481
112644
  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
- }
112645
+ const requestParsed = buildParsedNamedRequest(namedRequest.content, runtimeVariables, apiDefinitions);
112536
112646
  requestUrl = requestParsed.url;
112537
112647
  requestMethod = requestParsed.method;
112538
112648
  validatePreparedRequest(
@@ -112642,7 +112752,7 @@ ${indentMultiline(userMessage)}`;
112642
112752
  let requestUrl = "";
112643
112753
  let requestMethod = "";
112644
112754
  try {
112645
- const requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
112755
+ const requestParsed = buildParsedNamedRequest(namedRequest.content, runtimeVariables, apiDefinitions);
112646
112756
  requestUrl = requestParsed.url;
112647
112757
  requestMethod = requestParsed.method;
112648
112758
  validatePreparedRequest(
@@ -112802,12 +112912,7 @@ ${indentMultiline(userMessage)}`;
112802
112912
  const returnExpressions = extractReturnVariables(targetSequence.content);
112803
112913
  if (returnExpressions && returnExpressions.length > 0 && subResult.variables) {
112804
112914
  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
- }
112915
+ runtimeVariables[varName] = resolveSingleReturnValue(returnExpressions[0], subResult.variables);
112811
112916
  } else {
112812
112917
  const returnedObj = {};
112813
112918
  for (const expr of returnExpressions) {
@@ -113414,7 +113519,14 @@ function formatSequenceResult(result, options) {
113414
113519
  lines.push("");
113415
113520
  lines.push(colors.warning("Warnings/Errors:"));
113416
113521
  for (const err of result.errors) {
113417
- lines.push(` ${colors.bullet} ${err}`);
113522
+ const errLines = String(err ?? "").split("\n");
113523
+ if (errLines.length === 0) {
113524
+ continue;
113525
+ }
113526
+ lines.push(` ${colors.bullet} ${errLines[0]}`);
113527
+ for (const line2 of errLines.slice(1)) {
113528
+ lines.push(` ${line2}`);
113529
+ }
113418
113530
  }
113419
113531
  }
113420
113532
  return lines;
@@ -113976,7 +114088,7 @@ function generateErrorsHtml(errors, redaction) {
113976
114088
  <div class="errors">
113977
114089
  <h4>Errors</h4>
113978
114090
  <ul>
113979
- ${errors.map((e) => `<li>${escapeHtml(redactString(e, redaction))}</li>`).join("\n")}
114091
+ ${errors.map((e) => `<li>${escapeHtml(redactString(e, redaction)).replace(/\n/g, "<br>")}</li>`).join("\n")}
113980
114092
  </ul>
113981
114093
  </div>`;
113982
114094
  }
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.3",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"