norn-cli 2.1.0 → 2.2.0

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 +22 -0
  2. package/dist/cli.js +49 -37
  3. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -4,6 +4,28 @@ All notable changes to the "Norn" extension will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.2.0] - 2026-05-09
8
+
9
+ ### Added
10
+ - **Starter Catalogue (VS Code)**:
11
+ - Added a Project Setup starter catalogue in the sidebar with a Back button and readable stacked sample titles/descriptions.
12
+ - Added Explorer folder context-menu parity for opening the same starter catalogue.
13
+ - Added self-contained generated starter folders for simple requests, scripts plus JSON data, a runnable SQL demo, a real SQL connection starter, and an advanced end-to-end flow with `.nornenv`, `.nornapi`, helper sequences, tests, and schema contracts.
14
+
15
+ ### Fixed
16
+ - **Environment Template Diagnostics (VS Code)**:
17
+ - Allowed `.nornenv` variables such as `{{primaryUserId}}` and `{{postTitle}}` in assertion and condition expression contexts when those values are intentionally resolved from the environment file.
18
+ - Kept the bare-variable diagnostic for local, file, and sequence variables so runtime values are still read directly in expressions.
19
+
20
+ - **Sequence Argument Resolution (Extension + CLI)**:
21
+ - Resolved bare variable paths such as `user.id` and array-indexed paths such as `todosResponse.body[0]` before binding `run SomeSequence(...)` parameters.
22
+ - Fixed advanced schema-validation flows where unresolved literal argument text could produce empty responses and misleading `matchesSchema` object errors.
23
+
24
+ ### Changed
25
+ - **Website Docs and Skills**:
26
+ - Updated website VS Code docs for the starter catalogue, self-contained samples, SQL starter split, advanced flow, and Explorer context-menu behavior.
27
+ - Added starter-catalog maintenance guidance and updated value-resolution guidance for sequence argument binding.
28
+
7
29
  ## [2.1.0] - 2026-05-08
8
30
 
9
31
  ### Improved
package/dist/cli.js CHANGED
@@ -109772,6 +109772,10 @@ function findUnquotedPipe(str) {
109772
109772
  }
109773
109773
  function resolveValue(expr, responses, variables, getValueByPath2, responseIndexToVariable) {
109774
109774
  const trimmed = expr.trim();
109775
+ const wrappedResponseRefMatch = trimmed.match(/^\{\{(\$\d+(?:\..+)?)\}\}$/);
109776
+ if (wrappedResponseRefMatch) {
109777
+ return resolveValue(wrappedResponseRefMatch[1], responses, variables, getValueByPath2, responseIndexToVariable);
109778
+ }
109775
109779
  const refMatch = trimmed.match(/^\$(\d+)\.(.+)$/);
109776
109780
  if (refMatch) {
109777
109781
  const responseIdx = parseInt(refMatch[1], 10);
@@ -109872,7 +109876,7 @@ function resolveValue(expr, responses, variables, getValueByPath2, responseIndex
109872
109876
  return { value: void 0, error: `Variable {{${varName}}} is not defined` };
109873
109877
  }
109874
109878
  if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
109875
- return { value: decodeQuotedStringLiteral(trimmed) };
109879
+ return { value: substituteVariables(decodeQuotedStringLiteral(trimmed), variables) };
109876
109880
  }
109877
109881
  if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
109878
109882
  return { value: parseFloat(trimmed) };
@@ -109945,6 +109949,8 @@ function areValuesEqual(leftValue, rightValue) {
109945
109949
  }
109946
109950
  function evaluateAssertion(assertion, responses, variables, getValueByPath2, responseIndexToVariable, basePath) {
109947
109951
  const leftResult = resolveValue(assertion.leftExpr, responses, variables, getValueByPath2, responseIndexToVariable);
109952
+ const message = assertion.message === void 0 ? void 0 : substituteVariables(assertion.message, variables);
109953
+ const expression = formatExpression(assertion, variables);
109948
109954
  const buildFailureContext = () => ({
109949
109955
  responseIndex: leftResult.responseIndex,
109950
109956
  friendlyName: leftResult.variableName,
@@ -109954,8 +109960,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
109954
109960
  if (leftResult.error) {
109955
109961
  return {
109956
109962
  passed: false,
109957
- expression: formatExpression(assertion),
109958
- message: assertion.message,
109963
+ expression,
109964
+ message,
109959
109965
  operator: assertion.operator,
109960
109966
  leftValue: void 0,
109961
109967
  leftExpression: assertion.leftExpr,
@@ -109969,8 +109975,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
109969
109975
  const passed2 = leftValue !== void 0 && leftValue !== null;
109970
109976
  return {
109971
109977
  passed: passed2,
109972
- expression: formatExpression(assertion),
109973
- message: assertion.message,
109978
+ expression,
109979
+ message,
109974
109980
  operator: assertion.operator,
109975
109981
  leftValue,
109976
109982
  leftExpression: assertion.leftExpr,
@@ -109981,8 +109987,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
109981
109987
  const passed2 = leftValue === void 0 || leftValue === null;
109982
109988
  return {
109983
109989
  passed: passed2,
109984
- expression: formatExpression(assertion),
109985
- message: assertion.message,
109990
+ expression,
109991
+ message,
109986
109992
  operator: assertion.operator,
109987
109993
  leftValue,
109988
109994
  leftExpression: assertion.leftExpr,
@@ -109992,8 +109998,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
109992
109998
  if (!assertion.rightExpr) {
109993
109999
  return {
109994
110000
  passed: false,
109995
- expression: formatExpression(assertion),
109996
- message: assertion.message,
110001
+ expression,
110002
+ message,
109997
110003
  operator: assertion.operator,
109998
110004
  leftValue,
109999
110005
  leftExpression: assertion.leftExpr,
@@ -110014,8 +110020,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
110014
110020
  const passed2 = actualType === expectedType;
110015
110021
  return {
110016
110022
  passed: passed2,
110017
- expression: formatExpression(assertion),
110018
- message: assertion.message,
110023
+ expression,
110024
+ message,
110019
110025
  operator: assertion.operator,
110020
110026
  leftValue,
110021
110027
  rightValue: expectedType,
@@ -110029,12 +110035,13 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
110029
110035
  if (schemaPath.startsWith('"') && schemaPath.endsWith('"') || schemaPath.startsWith("'") && schemaPath.endsWith("'")) {
110030
110036
  schemaPath = schemaPath.slice(1, -1);
110031
110037
  }
110038
+ schemaPath = substituteVariables(schemaPath, variables);
110032
110039
  const validationResult = validateAgainstSchemaDetailed(leftValue, schemaPath, basePath);
110033
110040
  const passed2 = validationResult.valid;
110034
110041
  return {
110035
110042
  passed: passed2,
110036
- expression: formatExpression(assertion),
110037
- message: assertion.message,
110043
+ expression,
110044
+ message,
110038
110045
  operator: assertion.operator,
110039
110046
  leftValue,
110040
110047
  rightValue: schemaPath,
@@ -110052,8 +110059,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
110052
110059
  if (rightResult.error) {
110053
110060
  return {
110054
110061
  passed: false,
110055
- expression: formatExpression(assertion),
110056
- message: assertion.message,
110062
+ expression,
110063
+ message,
110057
110064
  operator: assertion.operator,
110058
110065
  leftValue,
110059
110066
  rightValue: void 0,
@@ -110107,8 +110114,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
110107
110114
  } catch (e) {
110108
110115
  return {
110109
110116
  passed: false,
110110
- expression: formatExpression(assertion),
110111
- message: assertion.message,
110117
+ expression,
110118
+ message,
110112
110119
  operator: assertion.operator,
110113
110120
  leftValue,
110114
110121
  rightValue,
@@ -110122,8 +110129,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
110122
110129
  }
110123
110130
  return {
110124
110131
  passed,
110125
- expression: formatExpression(assertion),
110126
- message: assertion.message,
110132
+ expression,
110133
+ message,
110127
110134
  operator: assertion.operator,
110128
110135
  leftValue,
110129
110136
  rightValue,
@@ -110132,11 +110139,14 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
110132
110139
  ...!passed ? buildFailureContext() : {}
110133
110140
  };
110134
110141
  }
110135
- function formatExpression(assertion) {
110142
+ function formatExpression(assertion, variables) {
110136
110143
  if (assertion.rightExpr) {
110137
- return `${assertion.leftExpr} ${assertion.operator} ${assertion.rightExpr}`;
110144
+ return substituteAssertionDisplayTemplates(`${assertion.leftExpr} ${assertion.operator} ${assertion.rightExpr}`, variables);
110138
110145
  }
110139
- return `${assertion.leftExpr} ${assertion.operator}`;
110146
+ return substituteAssertionDisplayTemplates(`${assertion.leftExpr} ${assertion.operator}`, variables);
110147
+ }
110148
+ function substituteAssertionDisplayTemplates(expression, variables) {
110149
+ return variables ? substituteVariables(expression, variables) : expression;
110140
110150
  }
110141
110151
 
110142
110152
  // src/jsonFileReader.ts
@@ -128371,13 +128381,7 @@ function bindSequenceArguments(params, args, runtimeVariables) {
128371
128381
  if (boundParams.has(arg.name)) {
128372
128382
  return { error: `Parameter "${arg.name}" provided multiple times` };
128373
128383
  }
128374
- let value = arg.value;
128375
- if (value.startsWith("{{") && value.endsWith("}}")) {
128376
- const varName = value.slice(2, -2);
128377
- value = getVariableValueByPath(varName, runtimeVariables);
128378
- } else if (runtimeVariables[value] !== void 0) {
128379
- value = runtimeVariables[value];
128380
- }
128384
+ const value = resolveSequenceArgumentValue(arg.value, runtimeVariables);
128381
128385
  result[arg.name] = value;
128382
128386
  boundParams.add(arg.name);
128383
128387
  } else {
@@ -128388,13 +128392,7 @@ function bindSequenceArguments(params, args, runtimeVariables) {
128388
128392
  return { error: `Too many arguments: expected ${params.length}, got more` };
128389
128393
  }
128390
128394
  const param = params[positionalIndex];
128391
- let value = arg.value;
128392
- if (value.startsWith("{{") && value.endsWith("}}")) {
128393
- const varName = value.slice(2, -2);
128394
- value = getVariableValueByPath(varName, runtimeVariables);
128395
- } else if (runtimeVariables[value] !== void 0) {
128396
- value = runtimeVariables[value];
128397
- }
128395
+ const value = resolveSequenceArgumentValue(arg.value, runtimeVariables);
128398
128396
  result[param.name] = value;
128399
128397
  boundParams.add(param.name);
128400
128398
  positionalIndex++;
@@ -128441,6 +128439,20 @@ function getVariableValueByPath(path20, variables) {
128441
128439
  }
128442
128440
  return current;
128443
128441
  }
128442
+ function resolveSequenceArgumentValue(value, runtimeVariables) {
128443
+ const trimmed = value.trim();
128444
+ if (trimmed.startsWith("{{") && trimmed.endsWith("}}")) {
128445
+ return getVariableValueByPath(trimmed.slice(2, -2).trim(), runtimeVariables);
128446
+ }
128447
+ if (trimmed in runtimeVariables) {
128448
+ return runtimeVariables[trimmed];
128449
+ }
128450
+ const pathMatch2 = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])+)$/);
128451
+ if (pathMatch2 && pathMatch2[1] in runtimeVariables) {
128452
+ return getVariableValueByPath(trimmed, runtimeVariables);
128453
+ }
128454
+ return value;
128455
+ }
128444
128456
  function createPrintStepResult(stepIndex, stepStartTime, sequenceStartTime, title, body) {
128445
128457
  const now = Date.now();
128446
128458
  return {
@@ -129590,7 +129602,7 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
129590
129602
  continue;
129591
129603
  }
129592
129604
  if (step.type === "assertion") {
129593
- const parsed = parseAssertCommand(substituteVariables(step.content, runtimeVariables));
129605
+ const parsed = parseAssertCommand(step.content);
129594
129606
  if (!parsed) {
129595
129607
  const invalidAssertMessage = `Step ${stepIdx + 1}: Invalid assert command: ${step.content}`;
129596
129608
  errors.push(invalidAssertMessage);
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": "2.1.0",
5
+ "version": "2.2.0",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"
@@ -48,6 +48,7 @@
48
48
  "onView:norn.sidebarHome",
49
49
  "onCommand:norn.importPostmanCollection",
50
50
  "onCommand:norn.importPostmanEnvironment",
51
+ "onCommand:norn.createStarterCatalog",
51
52
  "onCommand:norn.createSqlStarterFiles",
52
53
  "onCommand:norn.createMcpStarterConfig",
53
54
  "onCommand:norn.debugSequence",
@@ -107,8 +108,8 @@
107
108
  "category": "Norn"
108
109
  },
109
110
  {
110
- "command": "norn.createSqlStarterFiles",
111
- "title": "Create SQL Starter Files",
111
+ "command": "norn.createStarterCatalog",
112
+ "title": "Open Norn Starter Catalogue",
112
113
  "category": "Norn"
113
114
  },
114
115
  {
@@ -168,7 +169,7 @@
168
169
  "menus": {
169
170
  "explorer/context": [
170
171
  {
171
- "command": "norn.createSqlStarterFiles",
172
+ "command": "norn.createStarterCatalog",
172
173
  "when": "explorerResourceIsFolder",
173
174
  "group": "norn@1"
174
175
  },