norn-cli 2.4.0 → 2.6.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/AGENTS.md +2 -2
- package/CHANGELOG.md +26 -1
- package/dist/cli.js +330 -85
- package/package.json +24 -5
- package/schemas/norn.config.schema.json +43 -1
- package/scripts/__pycache__/reddit_signal_miner.cpython-312.pyc +0 -0
- package/scripts/reddit_signal_miner.py +482 -0
- package/.claude/settings.local.json +0 -18
- package/.claude/skills/norn-social-campaign/SKILL.md +0 -70
- package/out/apiResponseIntellisenseCache.js +0 -394
- package/out/assertionRunner.js +0 -567
- package/out/cacheDir.js +0 -136
- package/out/chatParticipant.js +0 -763
- package/out/cli/colors.js +0 -127
- package/out/cli/formatters/assertion.js +0 -102
- package/out/cli/formatters/index.js +0 -23
- package/out/cli/formatters/response.js +0 -106
- package/out/cli/formatters/summary.js +0 -246
- package/out/cli/redaction.js +0 -237
- package/out/cli/reporters/html.js +0 -689
- package/out/cli/reporters/index.js +0 -22
- package/out/cli/reporters/junit.js +0 -226
- package/out/codeLensProvider.js +0 -351
- package/out/compareContentProvider.js +0 -85
- package/out/completionProvider.js +0 -3739
- package/out/contractAssertionSummary.js +0 -225
- package/out/contractDecorationProvider.js +0 -243
- package/out/coverageCalculator.js +0 -879
- package/out/coveragePanel.js +0 -597
- package/out/debug/breakpointResolver.js +0 -84
- package/out/debug/breakpoints.js +0 -52
- package/out/debug/nornDebugAdapter.js +0 -166
- package/out/debug/nornDebugSession.js +0 -613
- package/out/debug/sequenceLocationIndex.js +0 -77
- package/out/debug/types.js +0 -3
- package/out/deepClone.js +0 -21
- package/out/diagnosticProvider.js +0 -2554
- package/out/environmentParser.js +0 -736
- package/out/environmentProvider.js +0 -544
- package/out/environmentTemplates.js +0 -146
- package/out/errors/formatError.js +0 -113
- package/out/errors/nornError.js +0 -29
- package/out/formUrlEncoded.js +0 -89
- package/out/httpClient.js +0 -348
- package/out/httpRuntimeOptions.js +0 -16
- package/out/importErrors.js +0 -31
- package/out/inlayHintResolver.js +0 -70
- package/out/jsonFileReader.js +0 -323
- package/out/mcpClient.js +0 -193
- package/out/mcpConfig.js +0 -184
- package/out/mcpToolIntellisenseCache.js +0 -96
- package/out/mcpToolSchema.js +0 -50
- package/out/nornConfig.js +0 -132
- package/out/nornHoverProvider.js +0 -124
- package/out/nornInlayHintsProvider.js +0 -191
- package/out/nornPrompt.js +0 -755
- package/out/nornSqlParser.js +0 -286
- package/out/nornapiHoverProvider.js +0 -135
- package/out/nornapiInlayHintsProvider.js +0 -94
- package/out/nornapiParser.js +0 -324
- package/out/nornenvCodeActionProvider.js +0 -101
- package/out/nornenvDecorationProvider.js +0 -239
- package/out/nornenvFoldingProvider.js +0 -63
- package/out/nornenvHoverProvider.js +0 -114
- package/out/nornenvInlayHintsProvider.js +0 -99
- package/out/nornenvLanguageModel.js +0 -187
- package/out/nornenvRegionRefactor.js +0 -267
- package/out/nornsqlHoverProvider.js +0 -95
- package/out/nornsqlInlayHintsProvider.js +0 -114
- package/out/parser.js +0 -839
- package/out/pathAccess.js +0 -28
- package/out/postmanImportPanel.js +0 -732
- package/out/postmanImportPlanner.js +0 -1155
- package/out/postmanImportSidebarView.js +0 -532
- package/out/quotedString.js +0 -35
- package/out/requestPreparation.js +0 -179
- package/out/requestValidation.js +0 -146
- package/out/responsePanel.js +0 -7754
- package/out/schemaGenerator.js +0 -562
- package/out/scriptRunner.js +0 -419
- package/out/secrets/cliSecrets.js +0 -415
- package/out/secrets/crypto.js +0 -105
- package/out/secrets/envFileSecrets.js +0 -177
- package/out/secrets/keyStore.js +0 -259
- package/out/sequenceDeclaration.js +0 -15
- package/out/sequenceRunner.js +0 -3590
- package/out/sqlAdapterRunner.js +0 -122
- package/out/sqlBuiltInAdapters.js +0 -604
- package/out/sqlConfig.js +0 -184
- package/out/starterCatalog.js +0 -554
- package/out/stringUtils.js +0 -25
- package/out/swaggerBodyIntellisenseCache.js +0 -114
- package/out/swaggerParser.js +0 -464
- package/out/testProvider.js +0 -767
- package/out/theoryCaseLoader.js +0 -113
- package/out/validationCache.js +0 -211
package/dist/cli.js
CHANGED
|
@@ -101832,6 +101832,123 @@ var path19 = __toESM(require("path"));
|
|
|
101832
101832
|
// src/parser.ts
|
|
101833
101833
|
var path = __toESM(require("path"));
|
|
101834
101834
|
|
|
101835
|
+
// src/quotedString.ts
|
|
101836
|
+
function decodeQuotedStringLiteral(literal2) {
|
|
101837
|
+
if (isVerbatimStringLiteral(literal2)) {
|
|
101838
|
+
return decodeVerbatimStringLiteral(literal2);
|
|
101839
|
+
}
|
|
101840
|
+
if (!isQuotedStringLiteral(literal2)) {
|
|
101841
|
+
return literal2;
|
|
101842
|
+
}
|
|
101843
|
+
const quoteChar = literal2[0];
|
|
101844
|
+
const inner = literal2.slice(1, -1);
|
|
101845
|
+
let decoded = "";
|
|
101846
|
+
for (let i = 0; i < inner.length; i++) {
|
|
101847
|
+
const char = inner[i];
|
|
101848
|
+
if (char !== "\\" || i === inner.length - 1) {
|
|
101849
|
+
decoded += char;
|
|
101850
|
+
continue;
|
|
101851
|
+
}
|
|
101852
|
+
const nextChar = inner[i + 1];
|
|
101853
|
+
if (nextChar === "\\" || nextChar === quoteChar) {
|
|
101854
|
+
decoded += nextChar;
|
|
101855
|
+
i++;
|
|
101856
|
+
continue;
|
|
101857
|
+
}
|
|
101858
|
+
decoded += char;
|
|
101859
|
+
}
|
|
101860
|
+
return decoded;
|
|
101861
|
+
}
|
|
101862
|
+
function isQuotedStringLiteral(literal2) {
|
|
101863
|
+
if (isVerbatimStringLiteral(literal2)) {
|
|
101864
|
+
return true;
|
|
101865
|
+
}
|
|
101866
|
+
if (literal2.length < 2) {
|
|
101867
|
+
return false;
|
|
101868
|
+
}
|
|
101869
|
+
const quoteChar = literal2[0];
|
|
101870
|
+
return (quoteChar === '"' || quoteChar === "'") && literal2[literal2.length - 1] === quoteChar;
|
|
101871
|
+
}
|
|
101872
|
+
function isVerbatimStringLiteral(literal2) {
|
|
101873
|
+
return literal2.length >= 3 && literal2.startsWith('@"') && literal2.endsWith('"');
|
|
101874
|
+
}
|
|
101875
|
+
function mapOutsideVerbatimStrings(text, transform2) {
|
|
101876
|
+
let output2 = "";
|
|
101877
|
+
let segmentStart = 0;
|
|
101878
|
+
let inStandardQuote = false;
|
|
101879
|
+
let standardQuoteChar = "";
|
|
101880
|
+
let escapeNext = false;
|
|
101881
|
+
for (let i = 0; i < text.length; i++) {
|
|
101882
|
+
const char = text[i];
|
|
101883
|
+
const nextChar = i + 1 < text.length ? text[i + 1] : "";
|
|
101884
|
+
if (inStandardQuote) {
|
|
101885
|
+
if (escapeNext) {
|
|
101886
|
+
escapeNext = false;
|
|
101887
|
+
continue;
|
|
101888
|
+
}
|
|
101889
|
+
if (char === "\\") {
|
|
101890
|
+
escapeNext = true;
|
|
101891
|
+
continue;
|
|
101892
|
+
}
|
|
101893
|
+
if (char === standardQuoteChar) {
|
|
101894
|
+
inStandardQuote = false;
|
|
101895
|
+
standardQuoteChar = "";
|
|
101896
|
+
}
|
|
101897
|
+
continue;
|
|
101898
|
+
}
|
|
101899
|
+
if (char === "@" && nextChar === '"') {
|
|
101900
|
+
const end = findVerbatimStringEnd(text, i);
|
|
101901
|
+
output2 += transform2(text.substring(segmentStart, i));
|
|
101902
|
+
output2 += text.substring(i, end);
|
|
101903
|
+
segmentStart = end;
|
|
101904
|
+
i = end - 1;
|
|
101905
|
+
continue;
|
|
101906
|
+
}
|
|
101907
|
+
if (char === '"' || char === "'") {
|
|
101908
|
+
inStandardQuote = true;
|
|
101909
|
+
standardQuoteChar = char;
|
|
101910
|
+
escapeNext = false;
|
|
101911
|
+
}
|
|
101912
|
+
}
|
|
101913
|
+
output2 += transform2(text.substring(segmentStart));
|
|
101914
|
+
return output2;
|
|
101915
|
+
}
|
|
101916
|
+
function findVerbatimStringEnd(text, start) {
|
|
101917
|
+
let i = start + 2;
|
|
101918
|
+
while (i < text.length) {
|
|
101919
|
+
const char = text[i];
|
|
101920
|
+
const nextChar = i + 1 < text.length ? text[i + 1] : "";
|
|
101921
|
+
if (char === '"' && nextChar === '"') {
|
|
101922
|
+
i += 2;
|
|
101923
|
+
continue;
|
|
101924
|
+
}
|
|
101925
|
+
if (char === '"' && isLikelyVerbatimStringTerminator(text, i)) {
|
|
101926
|
+
return i + 1;
|
|
101927
|
+
}
|
|
101928
|
+
i++;
|
|
101929
|
+
}
|
|
101930
|
+
return text.length;
|
|
101931
|
+
}
|
|
101932
|
+
function isLikelyVerbatimStringTerminator(text, quoteIndex) {
|
|
101933
|
+
const remainder = text.substring(quoteIndex + 1).trimStart();
|
|
101934
|
+
return remainder === "" || remainder.startsWith("|") || remainder.startsWith("#") || remainder.startsWith(",") || remainder.startsWith(")") || remainder.startsWith("]") || remainder.startsWith("}");
|
|
101935
|
+
}
|
|
101936
|
+
function decodeVerbatimStringLiteral(literal2) {
|
|
101937
|
+
const inner = literal2.slice(2, -1);
|
|
101938
|
+
let decoded = "";
|
|
101939
|
+
for (let i = 0; i < inner.length; i++) {
|
|
101940
|
+
const char = inner[i];
|
|
101941
|
+
const nextChar = i + 1 < inner.length ? inner[i + 1] : "";
|
|
101942
|
+
if (char === '"' && nextChar === '"') {
|
|
101943
|
+
decoded += '"';
|
|
101944
|
+
i++;
|
|
101945
|
+
continue;
|
|
101946
|
+
}
|
|
101947
|
+
decoded += char;
|
|
101948
|
+
}
|
|
101949
|
+
return decoded;
|
|
101950
|
+
}
|
|
101951
|
+
|
|
101835
101952
|
// src/nornapiParser.ts
|
|
101836
101953
|
function extractPathParameters(path20) {
|
|
101837
101954
|
const params = [];
|
|
@@ -102003,8 +102120,8 @@ function parseApiRequest(requestContent, endpoints, headerGroups) {
|
|
|
102003
102120
|
const headerMatch = line2.match(/^([a-zA-Z][a-zA-Z0-9\-]*)\s*:\s*(.+)$/);
|
|
102004
102121
|
if (headerMatch) {
|
|
102005
102122
|
let headerValue = headerMatch[2].trim();
|
|
102006
|
-
if (
|
|
102007
|
-
headerValue = headerValue
|
|
102123
|
+
if (isQuotedStringLiteral(headerValue)) {
|
|
102124
|
+
headerValue = decodeQuotedStringLiteral(headerValue);
|
|
102008
102125
|
}
|
|
102009
102126
|
inlineHeaders[headerMatch[1]] = headerValue;
|
|
102010
102127
|
continue;
|
|
@@ -102049,8 +102166,8 @@ function parseParamTokens(paramsStr) {
|
|
|
102049
102166
|
return tokens;
|
|
102050
102167
|
}
|
|
102051
102168
|
function unquote(value) {
|
|
102052
|
-
if (
|
|
102053
|
-
return value
|
|
102169
|
+
if (isQuotedStringLiteral(value)) {
|
|
102170
|
+
return decodeQuotedStringLiteral(value);
|
|
102054
102171
|
}
|
|
102055
102172
|
return value;
|
|
102056
102173
|
}
|
|
@@ -102367,14 +102484,21 @@ function getNestedPathValue(obj, path20) {
|
|
|
102367
102484
|
function stripInlineComment(line2) {
|
|
102368
102485
|
let inSingleQuote = false;
|
|
102369
102486
|
let inDoubleQuote = false;
|
|
102487
|
+
let inVerbatimString = false;
|
|
102370
102488
|
for (let index = 0; index < line2.length; index++) {
|
|
102371
102489
|
const char = line2[index];
|
|
102372
102490
|
const previousChar = index > 0 ? line2[index - 1] : "";
|
|
102373
|
-
|
|
102491
|
+
const nextChar = index + 1 < line2.length ? line2[index + 1] : "";
|
|
102492
|
+
if (inDoubleQuote && inVerbatimString && char === '"' && nextChar === '"') {
|
|
102493
|
+
index++;
|
|
102494
|
+
continue;
|
|
102495
|
+
}
|
|
102496
|
+
if (!inVerbatimString && previousChar === "\\") {
|
|
102374
102497
|
continue;
|
|
102375
102498
|
}
|
|
102376
102499
|
if (char === '"' && !inSingleQuote) {
|
|
102377
102500
|
inDoubleQuote = !inDoubleQuote;
|
|
102501
|
+
inVerbatimString = inDoubleQuote && previousChar === "@";
|
|
102378
102502
|
} else if (char === "'" && !inDoubleQuote) {
|
|
102379
102503
|
inSingleQuote = !inSingleQuote;
|
|
102380
102504
|
} else if (char === "#" && !inSingleQuote && !inDoubleQuote) {
|
|
@@ -102491,8 +102615,8 @@ function extractFileLevelVariables(text) {
|
|
|
102491
102615
|
if (isRuntimeComputedVariableValue(value)) {
|
|
102492
102616
|
continue;
|
|
102493
102617
|
}
|
|
102494
|
-
if (
|
|
102495
|
-
value = value
|
|
102618
|
+
if (isQuotedStringLiteral(value)) {
|
|
102619
|
+
value = decodeQuotedStringLiteral(value);
|
|
102496
102620
|
}
|
|
102497
102621
|
variables[match[1]] = value;
|
|
102498
102622
|
}
|
|
@@ -102598,8 +102722,8 @@ function parserHttpRequest(text, variables = {}) {
|
|
|
102598
102722
|
requestLine = cleanedLine;
|
|
102599
102723
|
const [method, ...urlParts] = requestLine.split(" ");
|
|
102600
102724
|
let url3 = urlParts.join(" ");
|
|
102601
|
-
if (
|
|
102602
|
-
url3 = url3
|
|
102725
|
+
if (isQuotedStringLiteral(url3)) {
|
|
102726
|
+
url3 = decodeQuotedStringLiteral(url3);
|
|
102603
102727
|
}
|
|
102604
102728
|
const headers = {};
|
|
102605
102729
|
let bodyStartIndex = -1;
|
|
@@ -108714,12 +108838,46 @@ function isNornError(error2) {
|
|
|
108714
108838
|
|
|
108715
108839
|
// src/httpRuntimeOptions.ts
|
|
108716
108840
|
var verifyTlsCertificates = true;
|
|
108841
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
108842
|
+
var requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
108717
108843
|
function setVerifyTlsCertificates(enabled) {
|
|
108718
108844
|
verifyTlsCertificates = enabled;
|
|
108719
108845
|
}
|
|
108720
108846
|
function getVerifyTlsCertificates() {
|
|
108721
108847
|
return verifyTlsCertificates;
|
|
108722
108848
|
}
|
|
108849
|
+
function setRequestTimeoutMs(timeoutMs) {
|
|
108850
|
+
if (timeoutMs === void 0) {
|
|
108851
|
+
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
108852
|
+
return;
|
|
108853
|
+
}
|
|
108854
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
108855
|
+
throw new Error(`Request timeout must be greater than 0 ms.`);
|
|
108856
|
+
}
|
|
108857
|
+
requestTimeoutMs = Math.ceil(timeoutMs);
|
|
108858
|
+
}
|
|
108859
|
+
function getRequestTimeoutMs() {
|
|
108860
|
+
return requestTimeoutMs;
|
|
108861
|
+
}
|
|
108862
|
+
function parseDurationToMs(rawValue, defaultUnit = "ms") {
|
|
108863
|
+
const trimmed = rawValue.trim().toLowerCase();
|
|
108864
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(ms|milliseconds?|s|sec|secs|seconds?|m|min|mins|minutes?)?$/);
|
|
108865
|
+
if (!match) {
|
|
108866
|
+
return void 0;
|
|
108867
|
+
}
|
|
108868
|
+
const amount = Number(match[1]);
|
|
108869
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
108870
|
+
return void 0;
|
|
108871
|
+
}
|
|
108872
|
+
const unit = match[2] ?? defaultUnit;
|
|
108873
|
+
if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
|
|
108874
|
+
return Math.ceil(amount);
|
|
108875
|
+
}
|
|
108876
|
+
if (unit === "m" || unit === "min" || unit === "mins" || unit === "minute" || unit === "minutes") {
|
|
108877
|
+
return Math.ceil(amount * 6e4);
|
|
108878
|
+
}
|
|
108879
|
+
return Math.ceil(amount * 1e3);
|
|
108880
|
+
}
|
|
108723
108881
|
|
|
108724
108882
|
// src/formUrlEncoded.ts
|
|
108725
108883
|
function parseEqualsField(segment) {
|
|
@@ -108910,7 +109068,7 @@ async function sendRequestWithJar(request, jar, retryOptions) {
|
|
|
108910
109068
|
adapter: "http",
|
|
108911
109069
|
headers,
|
|
108912
109070
|
data,
|
|
108913
|
-
timeout:
|
|
109071
|
+
timeout: getRequestTimeoutMs(),
|
|
108914
109072
|
maxRedirects: 0,
|
|
108915
109073
|
validateStatus: () => true,
|
|
108916
109074
|
httpsAgent: getHttpsAgent()
|
|
@@ -109661,34 +109819,6 @@ function validateAgainstSchemaObjectDetailed(value, schema) {
|
|
|
109661
109819
|
}
|
|
109662
109820
|
}
|
|
109663
109821
|
|
|
109664
|
-
// src/quotedString.ts
|
|
109665
|
-
function decodeQuotedStringLiteral(literal2) {
|
|
109666
|
-
if (literal2.length < 2) {
|
|
109667
|
-
return literal2;
|
|
109668
|
-
}
|
|
109669
|
-
const quoteChar = literal2[0];
|
|
109670
|
-
if (quoteChar !== '"' && quoteChar !== "'" || literal2[literal2.length - 1] !== quoteChar) {
|
|
109671
|
-
return literal2;
|
|
109672
|
-
}
|
|
109673
|
-
const inner = literal2.slice(1, -1);
|
|
109674
|
-
let decoded = "";
|
|
109675
|
-
for (let i = 0; i < inner.length; i++) {
|
|
109676
|
-
const char = inner[i];
|
|
109677
|
-
if (char !== "\\" || i === inner.length - 1) {
|
|
109678
|
-
decoded += char;
|
|
109679
|
-
continue;
|
|
109680
|
-
}
|
|
109681
|
-
const nextChar = inner[i + 1];
|
|
109682
|
-
if (nextChar === "\\" || nextChar === quoteChar) {
|
|
109683
|
-
decoded += nextChar;
|
|
109684
|
-
i++;
|
|
109685
|
-
continue;
|
|
109686
|
-
}
|
|
109687
|
-
decoded += char;
|
|
109688
|
-
}
|
|
109689
|
-
return decoded;
|
|
109690
|
-
}
|
|
109691
|
-
|
|
109692
109822
|
// src/assertionRunner.ts
|
|
109693
109823
|
function isAssertCommand(line2) {
|
|
109694
109824
|
return /^assert\s+/i.test(line2.trim());
|
|
@@ -109704,17 +109834,25 @@ function parseAssertCommand(line2) {
|
|
|
109704
109834
|
const pipeIndex = findUnquotedPipe(content);
|
|
109705
109835
|
if (pipeIndex !== -1) {
|
|
109706
109836
|
message = content.substring(pipeIndex + 1).trim();
|
|
109707
|
-
if (
|
|
109837
|
+
if (isQuotedStringLiteral(message)) {
|
|
109838
|
+
const messageWasVerbatim = isVerbatimStringLiteral(message);
|
|
109708
109839
|
message = decodeQuotedStringLiteral(message);
|
|
109840
|
+
if (messageWasVerbatim) {
|
|
109841
|
+
return parseAssertContent(content.substring(0, pipeIndex).trim(), message, true);
|
|
109842
|
+
}
|
|
109709
109843
|
}
|
|
109710
109844
|
content = content.substring(0, pipeIndex).trim();
|
|
109711
109845
|
}
|
|
109846
|
+
return parseAssertContent(content, message, false);
|
|
109847
|
+
}
|
|
109848
|
+
function parseAssertContent(content, message, messageIsVerbatim) {
|
|
109712
109849
|
const existsMatch = content.match(/^(.+?)\s+(exists|!exists)$/i);
|
|
109713
109850
|
if (existsMatch) {
|
|
109714
109851
|
return {
|
|
109715
109852
|
leftExpr: existsMatch[1].trim(),
|
|
109716
109853
|
operator: existsMatch[2].toLowerCase(),
|
|
109717
|
-
message
|
|
109854
|
+
message,
|
|
109855
|
+
messageIsVerbatim
|
|
109718
109856
|
};
|
|
109719
109857
|
}
|
|
109720
109858
|
const binaryOperators = [
|
|
@@ -109722,14 +109860,14 @@ function parseAssertCommand(line2) {
|
|
|
109722
109860
|
{ pattern: /^(.+?)\s*<=\s*(.+)$/, op: "<=" },
|
|
109723
109861
|
{ pattern: /^(.+?)\s*==\s*(.+)$/, op: "==" },
|
|
109724
109862
|
{ pattern: /^(.+?)\s*!=\s*(.+)$/, op: "!=" },
|
|
109725
|
-
{ pattern: /^(.+?)\s*>\s*(.+)$/, op: ">" },
|
|
109726
|
-
{ pattern: /^(.+?)\s*<\s*(.+)$/, op: "<" },
|
|
109727
109863
|
{ pattern: /^(.+?)\s+contains\s+(.+)$/i, op: "contains" },
|
|
109728
109864
|
{ pattern: /^(.+?)\s+startsWith\s+(.+)$/i, op: "startsWith" },
|
|
109729
109865
|
{ pattern: /^(.+?)\s+endsWith\s+(.+)$/i, op: "endsWith" },
|
|
109730
|
-
{ pattern: /^(.+?)\s+matches\s+(.+)$/i, op: "matches" },
|
|
109731
109866
|
{ pattern: /^(.+?)\s+matchesSchema\s+(.+)$/i, op: "matchesSchema" },
|
|
109732
|
-
{ pattern: /^(.+?)\s+
|
|
109867
|
+
{ pattern: /^(.+?)\s+matches\s+(.+)$/i, op: "matches" },
|
|
109868
|
+
{ pattern: /^(.+?)\s+isType\s+(.+)$/i, op: "isType" },
|
|
109869
|
+
{ pattern: /^(.+?)\s*>\s*(.+)$/, op: ">" },
|
|
109870
|
+
{ pattern: /^(.+?)\s*<\s*(.+)$/, op: "<" }
|
|
109733
109871
|
];
|
|
109734
109872
|
for (const { pattern, op } of binaryOperators) {
|
|
109735
109873
|
const binaryMatch = content.match(pattern);
|
|
@@ -109738,7 +109876,8 @@ function parseAssertCommand(line2) {
|
|
|
109738
109876
|
leftExpr: binaryMatch[1].trim(),
|
|
109739
109877
|
operator: op,
|
|
109740
109878
|
rightExpr: binaryMatch[2].trim(),
|
|
109741
|
-
message
|
|
109879
|
+
message,
|
|
109880
|
+
messageIsVerbatim
|
|
109742
109881
|
};
|
|
109743
109882
|
}
|
|
109744
109883
|
}
|
|
@@ -109748,28 +109887,44 @@ function findUnquotedPipe(str) {
|
|
|
109748
109887
|
let inQuote = false;
|
|
109749
109888
|
let quoteChar = "";
|
|
109750
109889
|
let escapeNext = false;
|
|
109890
|
+
let inVerbatimString = false;
|
|
109751
109891
|
for (let i = 0; i < str.length; i++) {
|
|
109752
109892
|
const char = str[i];
|
|
109893
|
+
if (inQuote && inVerbatimString && char === '"') {
|
|
109894
|
+
if (str[i + 1] === '"') {
|
|
109895
|
+
i++;
|
|
109896
|
+
continue;
|
|
109897
|
+
}
|
|
109898
|
+
if (!isVerbatimAssertionStringTerminator(str, i)) {
|
|
109899
|
+
continue;
|
|
109900
|
+
}
|
|
109901
|
+
}
|
|
109753
109902
|
if (escapeNext) {
|
|
109754
109903
|
escapeNext = false;
|
|
109755
109904
|
continue;
|
|
109756
109905
|
}
|
|
109757
|
-
if (inQuote && char === "\\") {
|
|
109906
|
+
if (inQuote && !inVerbatimString && char === "\\") {
|
|
109758
109907
|
escapeNext = true;
|
|
109759
109908
|
continue;
|
|
109760
109909
|
}
|
|
109761
109910
|
if ((char === '"' || char === "'") && !inQuote) {
|
|
109762
109911
|
inQuote = true;
|
|
109763
109912
|
quoteChar = char;
|
|
109913
|
+
inVerbatimString = char === '"' && i > 0 && str[i - 1] === "@";
|
|
109764
109914
|
} else if (char === quoteChar && inQuote) {
|
|
109765
109915
|
inQuote = false;
|
|
109766
109916
|
quoteChar = "";
|
|
109917
|
+
inVerbatimString = false;
|
|
109767
109918
|
} else if (char === "|" && !inQuote) {
|
|
109768
109919
|
return i;
|
|
109769
109920
|
}
|
|
109770
109921
|
}
|
|
109771
109922
|
return -1;
|
|
109772
109923
|
}
|
|
109924
|
+
function isVerbatimAssertionStringTerminator(str, quoteIndex) {
|
|
109925
|
+
const remainder = str.substring(quoteIndex + 1).trimStart();
|
|
109926
|
+
return remainder === "" || remainder.startsWith("|");
|
|
109927
|
+
}
|
|
109773
109928
|
function resolveValue(expr, responses, variables, getValueByPath2, responseIndexToVariable) {
|
|
109774
109929
|
const trimmed = expr.trim();
|
|
109775
109930
|
const wrappedResponseRefMatch = trimmed.match(/^\{\{(\$\d+(?:\..+)?)\}\}$/);
|
|
@@ -109875,8 +110030,9 @@ function resolveValue(expr, responses, variables, getValueByPath2, responseIndex
|
|
|
109875
110030
|
}
|
|
109876
110031
|
return { value: void 0, error: `Variable {{${varName}}} is not defined` };
|
|
109877
110032
|
}
|
|
109878
|
-
if (
|
|
109879
|
-
|
|
110033
|
+
if (isQuotedStringLiteral(trimmed)) {
|
|
110034
|
+
const decoded = decodeQuotedStringLiteral(trimmed);
|
|
110035
|
+
return { value: isVerbatimStringLiteral(trimmed) ? decoded : substituteVariables(decoded, variables) };
|
|
109880
110036
|
}
|
|
109881
110037
|
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
109882
110038
|
return { value: parseFloat(trimmed) };
|
|
@@ -109949,7 +110105,7 @@ function areValuesEqual(leftValue, rightValue) {
|
|
|
109949
110105
|
}
|
|
109950
110106
|
function evaluateAssertion(assertion, responses, variables, getValueByPath2, responseIndexToVariable, basePath) {
|
|
109951
110107
|
const leftResult = resolveValue(assertion.leftExpr, responses, variables, getValueByPath2, responseIndexToVariable);
|
|
109952
|
-
const message = assertion.message === void 0 ? void 0 : substituteVariables(assertion.message, variables);
|
|
110108
|
+
const message = assertion.message === void 0 ? void 0 : assertion.messageIsVerbatim ? assertion.message : substituteVariables(assertion.message, variables);
|
|
109953
110109
|
const expression = formatExpression(assertion, variables);
|
|
109954
110110
|
const buildFailureContext = () => ({
|
|
109955
110111
|
responseIndex: leftResult.responseIndex,
|
|
@@ -110032,10 +110188,14 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
|
|
|
110032
110188
|
}
|
|
110033
110189
|
if (assertion.operator === "matchesSchema") {
|
|
110034
110190
|
let schemaPath = assertion.rightExpr;
|
|
110035
|
-
|
|
110036
|
-
|
|
110191
|
+
let schemaPathIsVerbatim = false;
|
|
110192
|
+
if (isQuotedStringLiteral(schemaPath)) {
|
|
110193
|
+
schemaPathIsVerbatim = isVerbatimStringLiteral(schemaPath);
|
|
110194
|
+
schemaPath = decodeQuotedStringLiteral(schemaPath);
|
|
110195
|
+
}
|
|
110196
|
+
if (!schemaPathIsVerbatim) {
|
|
110197
|
+
schemaPath = substituteVariables(schemaPath, variables);
|
|
110037
110198
|
}
|
|
110038
|
-
schemaPath = substituteVariables(schemaPath, variables);
|
|
110039
110199
|
const validationResult = validateAgainstSchemaDetailed(leftValue, schemaPath, basePath);
|
|
110040
110200
|
const passed2 = validationResult.valid;
|
|
110041
110201
|
return {
|
|
@@ -110102,15 +110262,8 @@ function evaluateAssertion(assertion, responses, variables, getValueByPath2, res
|
|
|
110102
110262
|
break;
|
|
110103
110263
|
case "matches":
|
|
110104
110264
|
try {
|
|
110105
|
-
|
|
110106
|
-
|
|
110107
|
-
const lastSlash = pattern.lastIndexOf("/");
|
|
110108
|
-
const flags = pattern.substring(lastSlash + 1);
|
|
110109
|
-
pattern = pattern.substring(1, lastSlash);
|
|
110110
|
-
passed = new RegExp(pattern, flags).test(String(leftValue));
|
|
110111
|
-
} else {
|
|
110112
|
-
passed = new RegExp(pattern).test(String(leftValue));
|
|
110113
|
-
}
|
|
110265
|
+
const pattern = String(rightValue);
|
|
110266
|
+
passed = new RegExp(pattern).test(String(leftValue));
|
|
110114
110267
|
} catch (e) {
|
|
110115
110268
|
return {
|
|
110116
110269
|
passed: false,
|
|
@@ -110146,7 +110299,7 @@ function formatExpression(assertion, variables) {
|
|
|
110146
110299
|
return substituteAssertionDisplayTemplates(`${assertion.leftExpr} ${assertion.operator}`, variables);
|
|
110147
110300
|
}
|
|
110148
110301
|
function substituteAssertionDisplayTemplates(expression, variables) {
|
|
110149
|
-
return variables ?
|
|
110302
|
+
return variables ? mapOutsideVerbatimStrings(expression, (segment) => substituteVariables(segment, variables)) : expression;
|
|
110150
110303
|
}
|
|
110151
110304
|
|
|
110152
110305
|
// src/jsonFileReader.ts
|
|
@@ -110163,8 +110316,8 @@ function parseJsonCommand(line2) {
|
|
|
110163
110316
|
return null;
|
|
110164
110317
|
}
|
|
110165
110318
|
let filePath = match[2].trim();
|
|
110166
|
-
if (
|
|
110167
|
-
filePath = filePath
|
|
110319
|
+
if (isQuotedStringLiteral(filePath)) {
|
|
110320
|
+
filePath = decodeQuotedStringLiteral(filePath);
|
|
110168
110321
|
}
|
|
110169
110322
|
return {
|
|
110170
110323
|
varName: match[1],
|
|
@@ -110546,6 +110699,18 @@ function isStringRecord(value) {
|
|
|
110546
110699
|
function isKnownSection(value) {
|
|
110547
110700
|
return value === void 0 || isObjectRecord(value);
|
|
110548
110701
|
}
|
|
110702
|
+
function isNornHttpConfig(value) {
|
|
110703
|
+
if (value === void 0) {
|
|
110704
|
+
return true;
|
|
110705
|
+
}
|
|
110706
|
+
if (!isObjectRecord(value)) {
|
|
110707
|
+
return false;
|
|
110708
|
+
}
|
|
110709
|
+
if (value._comment !== void 0 && typeof value._comment !== "string" && (!Array.isArray(value._comment) || !value._comment.every((item) => typeof item === "string"))) {
|
|
110710
|
+
return false;
|
|
110711
|
+
}
|
|
110712
|
+
return value.timeoutMs === void 0 || typeof value.timeoutMs === "number" && Number.isFinite(value.timeoutMs) && value.timeoutMs > 0;
|
|
110713
|
+
}
|
|
110549
110714
|
function isNornProjectConfig(value) {
|
|
110550
110715
|
if (!isObjectRecord(value)) {
|
|
110551
110716
|
return false;
|
|
@@ -110553,7 +110718,7 @@ function isNornProjectConfig(value) {
|
|
|
110553
110718
|
if (value.version !== 1) {
|
|
110554
110719
|
return false;
|
|
110555
110720
|
}
|
|
110556
|
-
return isKnownSection(value.sql) && isKnownSection(value.mcp);
|
|
110721
|
+
return isNornHttpConfig(value.http) && isKnownSection(value.sql) && isKnownSection(value.mcp);
|
|
110557
110722
|
}
|
|
110558
110723
|
function loadNornConfig(startPath) {
|
|
110559
110724
|
const filePath = findNearestConfigFile(startPath, NORN_CONFIG_FILENAME);
|
|
@@ -128016,7 +128181,7 @@ function parseRunArguments(argsStr) {
|
|
|
128016
128181
|
if (!argsStr || !argsStr.trim()) {
|
|
128017
128182
|
return args;
|
|
128018
128183
|
}
|
|
128019
|
-
const parts = argsStr
|
|
128184
|
+
const parts = splitNamedArgumentList(argsStr);
|
|
128020
128185
|
for (const part of parts) {
|
|
128021
128186
|
const trimmed = part.trim();
|
|
128022
128187
|
if (!trimmed) {
|
|
@@ -128025,7 +128190,7 @@ function parseRunArguments(argsStr) {
|
|
|
128025
128190
|
const namedMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
|
|
128026
128191
|
if (namedMatch) {
|
|
128027
128192
|
let value = namedMatch[2].trim();
|
|
128028
|
-
if (
|
|
128193
|
+
if (isQuotedStringLiteral(value)) {
|
|
128029
128194
|
value = decodeQuotedStringLiteral(value);
|
|
128030
128195
|
}
|
|
128031
128196
|
args.push({
|
|
@@ -128034,7 +128199,7 @@ function parseRunArguments(argsStr) {
|
|
|
128034
128199
|
});
|
|
128035
128200
|
} else {
|
|
128036
128201
|
let value = trimmed;
|
|
128037
|
-
if (
|
|
128202
|
+
if (isQuotedStringLiteral(value)) {
|
|
128038
128203
|
value = decodeQuotedStringLiteral(value);
|
|
128039
128204
|
}
|
|
128040
128205
|
args.push({ value });
|
|
@@ -128046,6 +128211,7 @@ function splitNamedArgumentList(argsStr) {
|
|
|
128046
128211
|
const parts = [];
|
|
128047
128212
|
let current = "";
|
|
128048
128213
|
let quoteChar = null;
|
|
128214
|
+
let inVerbatimString = false;
|
|
128049
128215
|
let templateDepth = 0;
|
|
128050
128216
|
let bracketDepth = 0;
|
|
128051
128217
|
for (let i = 0; i < argsStr.length; i++) {
|
|
@@ -128054,13 +128220,20 @@ function splitNamedArgumentList(argsStr) {
|
|
|
128054
128220
|
const prev = i > 0 ? argsStr[i - 1] : "";
|
|
128055
128221
|
if (quoteChar) {
|
|
128056
128222
|
current += char;
|
|
128223
|
+
if (inVerbatimString && char === '"' && next === '"') {
|
|
128224
|
+
current += next;
|
|
128225
|
+
i++;
|
|
128226
|
+
continue;
|
|
128227
|
+
}
|
|
128057
128228
|
if (char === quoteChar && prev !== "\\") {
|
|
128058
128229
|
quoteChar = null;
|
|
128230
|
+
inVerbatimString = false;
|
|
128059
128231
|
}
|
|
128060
128232
|
continue;
|
|
128061
128233
|
}
|
|
128062
128234
|
if (char === '"' || char === "'") {
|
|
128063
128235
|
quoteChar = char;
|
|
128236
|
+
inVerbatimString = char === '"' && prev === "@";
|
|
128064
128237
|
current += char;
|
|
128065
128238
|
continue;
|
|
128066
128239
|
}
|
|
@@ -128520,7 +128693,7 @@ function parseSequenceParameters(line2) {
|
|
|
128520
128693
|
if (!paramsStr) {
|
|
128521
128694
|
return params;
|
|
128522
128695
|
}
|
|
128523
|
-
const paramParts = paramsStr
|
|
128696
|
+
const paramParts = splitNamedArgumentList(paramsStr);
|
|
128524
128697
|
let hasSeenDefault = false;
|
|
128525
128698
|
for (const part of paramParts) {
|
|
128526
128699
|
const trimmed = part.trim();
|
|
@@ -128531,7 +128704,7 @@ function parseSequenceParameters(line2) {
|
|
|
128531
128704
|
if (defaultMatch) {
|
|
128532
128705
|
hasSeenDefault = true;
|
|
128533
128706
|
let defaultValue = defaultMatch[2].trim();
|
|
128534
|
-
if (
|
|
128707
|
+
if (isQuotedStringLiteral(defaultValue)) {
|
|
128535
128708
|
defaultValue = decodeQuotedStringLiteral(defaultValue);
|
|
128536
128709
|
}
|
|
128537
128710
|
params.push({
|
|
@@ -128608,14 +128781,21 @@ function parseDataValues(valuesStr) {
|
|
|
128608
128781
|
const values = [];
|
|
128609
128782
|
let current = "";
|
|
128610
128783
|
let inQuote = null;
|
|
128784
|
+
let inVerbatimString = false;
|
|
128611
128785
|
let i = 0;
|
|
128612
128786
|
while (i < valuesStr.length) {
|
|
128613
128787
|
const char = valuesStr[i];
|
|
128614
128788
|
if (inQuote) {
|
|
128789
|
+
if (inVerbatimString && char === '"' && valuesStr[i + 1] === '"') {
|
|
128790
|
+
current += '""';
|
|
128791
|
+
i += 2;
|
|
128792
|
+
continue;
|
|
128793
|
+
}
|
|
128615
128794
|
if (char === inQuote) {
|
|
128616
|
-
values.push(current);
|
|
128795
|
+
values.push(inVerbatimString ? decodeQuotedStringLiteral(`@"${current}"`) : current);
|
|
128617
128796
|
current = "";
|
|
128618
128797
|
inQuote = null;
|
|
128798
|
+
inVerbatimString = false;
|
|
128619
128799
|
i++;
|
|
128620
128800
|
while (i < valuesStr.length && valuesStr[i] !== ",") {
|
|
128621
128801
|
i++;
|
|
@@ -128626,8 +128806,15 @@ function parseDataValues(valuesStr) {
|
|
|
128626
128806
|
current += char;
|
|
128627
128807
|
}
|
|
128628
128808
|
} else {
|
|
128629
|
-
if (char ===
|
|
128809
|
+
if (char === "@" && valuesStr[i + 1] === '"') {
|
|
128810
|
+
inQuote = '"';
|
|
128811
|
+
inVerbatimString = true;
|
|
128812
|
+
current = "";
|
|
128813
|
+
i += 2;
|
|
128814
|
+
continue;
|
|
128815
|
+
} else if (char === '"' || char === "'") {
|
|
128630
128816
|
inQuote = char;
|
|
128817
|
+
inVerbatimString = false;
|
|
128631
128818
|
current = "";
|
|
128632
128819
|
} else if (char === ",") {
|
|
128633
128820
|
const trimmed = current.trim();
|
|
@@ -128664,7 +128851,7 @@ function parseTypedValue(value) {
|
|
|
128664
128851
|
if (/^-?\d+(\.\d+)?$/.test(value)) {
|
|
128665
128852
|
return parseFloat(value);
|
|
128666
128853
|
}
|
|
128667
|
-
if (
|
|
128854
|
+
if (isQuotedStringLiteral(value)) {
|
|
128668
128855
|
return decodeQuotedStringLiteral(value);
|
|
128669
128856
|
}
|
|
128670
128857
|
return value;
|
|
@@ -128790,9 +128977,9 @@ function parseVarAssignCommand(line2) {
|
|
|
128790
128977
|
}
|
|
128791
128978
|
function evaluateValueExpression(expr, runtimeVariables) {
|
|
128792
128979
|
const trimmed = expr.trim();
|
|
128793
|
-
if (
|
|
128980
|
+
if (isQuotedStringLiteral(trimmed)) {
|
|
128794
128981
|
const inner = decodeQuotedStringLiteral(trimmed);
|
|
128795
|
-
const substituted = substituteVariables(inner, runtimeVariables);
|
|
128982
|
+
const substituted = isVerbatimStringLiteral(trimmed) ? inner : substituteVariables(inner, runtimeVariables);
|
|
128796
128983
|
return { value: substituted };
|
|
128797
128984
|
}
|
|
128798
128985
|
if (trimmed === "true" || trimmed === "false" || trimmed === "null") {
|
|
@@ -128840,9 +129027,9 @@ function evaluateSqlArgumentExpression(expr, runtimeVariables) {
|
|
|
128840
129027
|
if (!trimmed) {
|
|
128841
129028
|
return { value: "" };
|
|
128842
129029
|
}
|
|
128843
|
-
if (
|
|
129030
|
+
if (isQuotedStringLiteral(trimmed)) {
|
|
128844
129031
|
const inner = decodeQuotedStringLiteral(trimmed);
|
|
128845
|
-
return { value: substituteVariables(inner, runtimeVariables) };
|
|
129032
|
+
return { value: isVerbatimStringLiteral(trimmed) ? inner : substituteVariables(inner, runtimeVariables) };
|
|
128846
129033
|
}
|
|
128847
129034
|
if (trimmed.startsWith("{{") && trimmed.endsWith("}}")) {
|
|
128848
129035
|
return evaluateSqlArgumentExpression(trimmed.slice(2, -2).trim(), runtimeVariables);
|
|
@@ -128952,7 +129139,7 @@ function resolveBareVariables(text, variables) {
|
|
|
128952
129139
|
const parts = splitExpressionParts(text);
|
|
128953
129140
|
const resolvedParts = parts.map((part) => {
|
|
128954
129141
|
const partTrimmed = part.trim();
|
|
128955
|
-
if (
|
|
129142
|
+
if (isQuotedStringLiteral(partTrimmed)) {
|
|
128956
129143
|
return decodeQuotedStringLiteral(partTrimmed);
|
|
128957
129144
|
}
|
|
128958
129145
|
const varMatch = partTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)$/);
|
|
@@ -128980,6 +129167,7 @@ function splitExpressionParts(expr) {
|
|
|
128980
129167
|
let inString = false;
|
|
128981
129168
|
let stringChar = "";
|
|
128982
129169
|
let escapeNext = false;
|
|
129170
|
+
let inVerbatimString = false;
|
|
128983
129171
|
for (let i = 0; i < expr.length; i++) {
|
|
128984
129172
|
const char = expr[i];
|
|
128985
129173
|
if (escapeNext) {
|
|
@@ -128987,7 +129175,12 @@ function splitExpressionParts(expr) {
|
|
|
128987
129175
|
escapeNext = false;
|
|
128988
129176
|
continue;
|
|
128989
129177
|
}
|
|
128990
|
-
if (inString && char === "
|
|
129178
|
+
if (inString && inVerbatimString && char === '"' && i + 1 < expr.length && expr[i + 1] === '"') {
|
|
129179
|
+
current += char + expr[i + 1];
|
|
129180
|
+
i++;
|
|
129181
|
+
continue;
|
|
129182
|
+
}
|
|
129183
|
+
if (inString && !inVerbatimString && char === "\\") {
|
|
128991
129184
|
current += char;
|
|
128992
129185
|
escapeNext = true;
|
|
128993
129186
|
continue;
|
|
@@ -128995,9 +129188,11 @@ function splitExpressionParts(expr) {
|
|
|
128995
129188
|
if (!inString && (char === '"' || char === "'")) {
|
|
128996
129189
|
inString = true;
|
|
128997
129190
|
stringChar = char;
|
|
129191
|
+
inVerbatimString = char === '"' && i > 0 && expr[i - 1] === "@";
|
|
128998
129192
|
current += char;
|
|
128999
129193
|
} else if (inString && char === stringChar) {
|
|
129000
129194
|
inString = false;
|
|
129195
|
+
inVerbatimString = false;
|
|
129001
129196
|
current += char;
|
|
129002
129197
|
} else if (!inString && char === "+") {
|
|
129003
129198
|
parts.push(current);
|
|
@@ -131444,7 +131639,8 @@ function formatAssertion(assertion, options) {
|
|
|
131444
131639
|
if (assertion.error) {
|
|
131445
131640
|
lines.push(` ${colors.error(`Error: ${assertion.error}`)}`);
|
|
131446
131641
|
} else if (!assertion.passed) {
|
|
131447
|
-
|
|
131642
|
+
const expectedValue = assertion.rightValue !== void 0 ? assertion.rightValue : assertion.rightExpression ?? assertion.operator;
|
|
131643
|
+
lines.push(` ${colors.dim("Expected:")} ${formatValue(expectedValue)}`);
|
|
131448
131644
|
lines.push(` ${colors.dim("Actual:")} ${formatValue(assertion.leftValue)}`);
|
|
131449
131645
|
if (verbose && assertion.leftExpression !== String(assertion.leftValue)) {
|
|
131450
131646
|
lines.push(` ${colors.dim("Expression:")} ${assertion.leftExpression}`);
|
|
@@ -131738,12 +131934,13 @@ function generateTestCase(sequenceName, assertion, redaction) {
|
|
|
131738
131934
|
let xml = ` <testcase name="${escapeXml(testName)}" classname="${escapeXml(className)}">
|
|
131739
131935
|
`;
|
|
131740
131936
|
if (!assertion.passed) {
|
|
131741
|
-
const
|
|
131937
|
+
const expectedValue = assertion.rightValue !== void 0 ? assertion.rightValue : assertion.rightExpression;
|
|
131938
|
+
const message = assertion.error || `Expected ${JSON.stringify(expectedValue)}, got ${JSON.stringify(assertion.leftValue)}`;
|
|
131742
131939
|
xml += ` <failure message="${escapeXml(redactString(message, redaction))}" type="AssertionError">
|
|
131743
131940
|
`;
|
|
131744
131941
|
xml += `<![CDATA[Expression: ${assertion.expression}
|
|
131745
131942
|
`;
|
|
131746
|
-
xml += `Expected: ${JSON.stringify(
|
|
131943
|
+
xml += `Expected: ${JSON.stringify(expectedValue)}
|
|
131747
131944
|
`;
|
|
131748
131945
|
xml += `Actual: ${JSON.stringify(assertion.leftValue)}]]>
|
|
131749
131946
|
`;
|
|
@@ -132115,11 +132312,13 @@ function generateAssertionHtml(step, redaction) {
|
|
|
132115
132312
|
} else {
|
|
132116
132313
|
actualDisplay = String(assertion.leftValue);
|
|
132117
132314
|
}
|
|
132315
|
+
const expectedValue = assertion.rightValue !== void 0 ? assertion.rightValue : assertion.rightExpression;
|
|
132316
|
+
const expectedDisplay = typeof expectedValue === "object" ? JSON.stringify(expectedValue, null, 2) : String(expectedValue);
|
|
132118
132317
|
const pathInfo = assertion.jsonPath ? `<div class="assertion-path"><strong>Path:</strong> <code>${escapeHtml(assertion.jsonPath)}</code></div>` : "";
|
|
132119
132318
|
detailsHtml = `
|
|
132120
132319
|
<div class="assertion-details">
|
|
132121
132320
|
${pathInfo}
|
|
132122
|
-
<div><strong>Expected:</strong> <code>${escapeHtml(
|
|
132321
|
+
<div><strong>Expected:</strong> <code>${escapeHtml(expectedDisplay)}</code></div>
|
|
132123
132322
|
<div><strong>Actual:</strong> <code>${escapeHtml(actualDisplay)}</code></div>
|
|
132124
132323
|
${assertion.error ? `<div class="error"><strong>Error:</strong> ${escapeHtml(redactString(assertion.error, redaction))}</div>` : ""}
|
|
132125
132324
|
</div>`;
|
|
@@ -134058,6 +134257,32 @@ function applyRegionRefactorToText(text, pattern) {
|
|
|
134058
134257
|
` : result;
|
|
134059
134258
|
}
|
|
134060
134259
|
|
|
134260
|
+
// src/requestTimeoutConfig.ts
|
|
134261
|
+
function getProjectRequestTimeoutMs(startPath) {
|
|
134262
|
+
if (!findNearestConfigFile(startPath, NORN_CONFIG_FILENAME)) {
|
|
134263
|
+
return void 0;
|
|
134264
|
+
}
|
|
134265
|
+
const { config: config2 } = loadNornConfig(startPath);
|
|
134266
|
+
return config2.http?.timeoutMs;
|
|
134267
|
+
}
|
|
134268
|
+
function resolveRequestTimeoutMs(startPath, overrideMs) {
|
|
134269
|
+
if (overrideMs !== void 0) {
|
|
134270
|
+
return overrideMs;
|
|
134271
|
+
}
|
|
134272
|
+
if (startPath) {
|
|
134273
|
+
const projectTimeoutMs = getProjectRequestTimeoutMs(startPath);
|
|
134274
|
+
if (projectTimeoutMs !== void 0) {
|
|
134275
|
+
return projectTimeoutMs;
|
|
134276
|
+
}
|
|
134277
|
+
}
|
|
134278
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
134279
|
+
}
|
|
134280
|
+
function applyRequestTimeoutForPath(startPath, overrideMs) {
|
|
134281
|
+
const timeoutMs = resolveRequestTimeoutMs(startPath, overrideMs);
|
|
134282
|
+
setRequestTimeoutMs(timeoutMs);
|
|
134283
|
+
return timeoutMs;
|
|
134284
|
+
}
|
|
134285
|
+
|
|
134061
134286
|
// src/cli.ts
|
|
134062
134287
|
function handleImportResolutionErrors(errors, colors) {
|
|
134063
134288
|
const { blockingErrors, warningErrors } = splitImportResolutionErrors(errors);
|
|
@@ -134146,6 +134371,14 @@ function buildCliEnvironmentValidationContext(resolvedEnv, selectedEnv) {
|
|
|
134146
134371
|
availableEnvironments: resolvedEnv.availableEnvironments
|
|
134147
134372
|
};
|
|
134148
134373
|
}
|
|
134374
|
+
function applyCliRequestTimeout(filePath, options, colors) {
|
|
134375
|
+
try {
|
|
134376
|
+
applyRequestTimeoutForPath(filePath, options.timeoutMs);
|
|
134377
|
+
} catch (error2) {
|
|
134378
|
+
console.error(colors.error(`Invalid request timeout configuration: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
134379
|
+
process.exit(1);
|
|
134380
|
+
}
|
|
134381
|
+
}
|
|
134149
134382
|
function generateTimestamp() {
|
|
134150
134383
|
const now = /* @__PURE__ */ new Date();
|
|
134151
134384
|
const year = now.getFullYear();
|
|
@@ -134189,7 +134422,17 @@ function parseArgs(args) {
|
|
|
134189
134422
|
} else if (arg === "--env" || arg === "-e") {
|
|
134190
134423
|
options.env = args[++i];
|
|
134191
134424
|
} else if (arg === "--timeout" || arg === "-t") {
|
|
134192
|
-
|
|
134425
|
+
const rawTimeout = args[++i];
|
|
134426
|
+
if (!rawTimeout) {
|
|
134427
|
+
console.error("Error: --timeout requires a value, e.g. 180s, 3m, or 300000ms.");
|
|
134428
|
+
process.exit(1);
|
|
134429
|
+
}
|
|
134430
|
+
const timeoutMs = parseDurationToMs(rawTimeout, "s");
|
|
134431
|
+
if (timeoutMs === void 0) {
|
|
134432
|
+
console.error(`Error: Invalid timeout '${rawTimeout}'. Use values like 180s, 3m, or 300000ms.`);
|
|
134433
|
+
process.exit(1);
|
|
134434
|
+
}
|
|
134435
|
+
options.timeoutMs = timeoutMs;
|
|
134193
134436
|
} else if (arg === "--insecure") {
|
|
134194
134437
|
options.insecure = true;
|
|
134195
134438
|
} else if (arg === "--refactor-region-pattern" || arg === "--refactor-nornenv-region-pattern") {
|
|
@@ -134240,7 +134483,7 @@ Options:
|
|
|
134240
134483
|
-s, --sequence <name> Run a specific sequence by name (single file only)
|
|
134241
134484
|
-r, --request <name> Run a specific named request (single file only)
|
|
134242
134485
|
-e, --env <name> Use environment from .nornenv (e.g., dev, prod)
|
|
134243
|
-
-t, --timeout <
|
|
134486
|
+
-t, --timeout <time> Request timeout override (e.g. 180s, 3m, 300000ms; default: norn.config.json or 30s)
|
|
134244
134487
|
--insecure Disable TLS certificate verification (dev/self-signed only)
|
|
134245
134488
|
-j, --json Output results as JSON (for CI/CD)
|
|
134246
134489
|
-v, --verbose Show detailed output (headers, request/response bodies)
|
|
@@ -134565,6 +134808,7 @@ async function main() {
|
|
|
134565
134808
|
}
|
|
134566
134809
|
if (options.sequence || options.request) {
|
|
134567
134810
|
const filePath = filesToRun[0];
|
|
134811
|
+
applyCliRequestTimeout(filePath, options, colors);
|
|
134568
134812
|
const secretUnlockResult = await ensureCliSecretsUnlocked(filePath);
|
|
134569
134813
|
if (!secretUnlockResult.ok) {
|
|
134570
134814
|
if (secretUnlockResult.errors.length > 0) {
|
|
@@ -134769,6 +135013,7 @@ ${fileContent}` : fileContent;
|
|
|
134769
135013
|
console.log("");
|
|
134770
135014
|
}
|
|
134771
135015
|
for (const filePath of filesToRun) {
|
|
135016
|
+
applyCliRequestTimeout(filePath, options, colors);
|
|
134772
135017
|
const secretUnlockResult = await ensureCliSecretsUnlocked(filePath);
|
|
134773
135018
|
if (!secretUnlockResult.ok) {
|
|
134774
135019
|
if (secretUnlockResult.errors.length > 0) {
|