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.
- package/CHANGELOG.md +22 -0
- package/dist/cli.js +49 -37
- 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
|
|
109958
|
-
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
|
|
109973
|
-
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
|
|
109985
|
-
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
|
|
109996
|
-
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
|
|
110018
|
-
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
|
|
110037
|
-
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
|
|
110056
|
-
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
|
|
110111
|
-
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
|
|
110126
|
-
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
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
111
|
-
"title": "
|
|
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.
|
|
172
|
+
"command": "norn.createStarterCatalog",
|
|
172
173
|
"when": "explorerResourceIsFolder",
|
|
173
174
|
"group": "norn@1"
|
|
174
175
|
},
|