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.
Files changed (96) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +26 -1
  3. package/dist/cli.js +330 -85
  4. package/package.json +24 -5
  5. package/schemas/norn.config.schema.json +43 -1
  6. package/scripts/__pycache__/reddit_signal_miner.cpython-312.pyc +0 -0
  7. package/scripts/reddit_signal_miner.py +482 -0
  8. package/.claude/settings.local.json +0 -18
  9. package/.claude/skills/norn-social-campaign/SKILL.md +0 -70
  10. package/out/apiResponseIntellisenseCache.js +0 -394
  11. package/out/assertionRunner.js +0 -567
  12. package/out/cacheDir.js +0 -136
  13. package/out/chatParticipant.js +0 -763
  14. package/out/cli/colors.js +0 -127
  15. package/out/cli/formatters/assertion.js +0 -102
  16. package/out/cli/formatters/index.js +0 -23
  17. package/out/cli/formatters/response.js +0 -106
  18. package/out/cli/formatters/summary.js +0 -246
  19. package/out/cli/redaction.js +0 -237
  20. package/out/cli/reporters/html.js +0 -689
  21. package/out/cli/reporters/index.js +0 -22
  22. package/out/cli/reporters/junit.js +0 -226
  23. package/out/codeLensProvider.js +0 -351
  24. package/out/compareContentProvider.js +0 -85
  25. package/out/completionProvider.js +0 -3739
  26. package/out/contractAssertionSummary.js +0 -225
  27. package/out/contractDecorationProvider.js +0 -243
  28. package/out/coverageCalculator.js +0 -879
  29. package/out/coveragePanel.js +0 -597
  30. package/out/debug/breakpointResolver.js +0 -84
  31. package/out/debug/breakpoints.js +0 -52
  32. package/out/debug/nornDebugAdapter.js +0 -166
  33. package/out/debug/nornDebugSession.js +0 -613
  34. package/out/debug/sequenceLocationIndex.js +0 -77
  35. package/out/debug/types.js +0 -3
  36. package/out/deepClone.js +0 -21
  37. package/out/diagnosticProvider.js +0 -2554
  38. package/out/environmentParser.js +0 -736
  39. package/out/environmentProvider.js +0 -544
  40. package/out/environmentTemplates.js +0 -146
  41. package/out/errors/formatError.js +0 -113
  42. package/out/errors/nornError.js +0 -29
  43. package/out/formUrlEncoded.js +0 -89
  44. package/out/httpClient.js +0 -348
  45. package/out/httpRuntimeOptions.js +0 -16
  46. package/out/importErrors.js +0 -31
  47. package/out/inlayHintResolver.js +0 -70
  48. package/out/jsonFileReader.js +0 -323
  49. package/out/mcpClient.js +0 -193
  50. package/out/mcpConfig.js +0 -184
  51. package/out/mcpToolIntellisenseCache.js +0 -96
  52. package/out/mcpToolSchema.js +0 -50
  53. package/out/nornConfig.js +0 -132
  54. package/out/nornHoverProvider.js +0 -124
  55. package/out/nornInlayHintsProvider.js +0 -191
  56. package/out/nornPrompt.js +0 -755
  57. package/out/nornSqlParser.js +0 -286
  58. package/out/nornapiHoverProvider.js +0 -135
  59. package/out/nornapiInlayHintsProvider.js +0 -94
  60. package/out/nornapiParser.js +0 -324
  61. package/out/nornenvCodeActionProvider.js +0 -101
  62. package/out/nornenvDecorationProvider.js +0 -239
  63. package/out/nornenvFoldingProvider.js +0 -63
  64. package/out/nornenvHoverProvider.js +0 -114
  65. package/out/nornenvInlayHintsProvider.js +0 -99
  66. package/out/nornenvLanguageModel.js +0 -187
  67. package/out/nornenvRegionRefactor.js +0 -267
  68. package/out/nornsqlHoverProvider.js +0 -95
  69. package/out/nornsqlInlayHintsProvider.js +0 -114
  70. package/out/parser.js +0 -839
  71. package/out/pathAccess.js +0 -28
  72. package/out/postmanImportPanel.js +0 -732
  73. package/out/postmanImportPlanner.js +0 -1155
  74. package/out/postmanImportSidebarView.js +0 -532
  75. package/out/quotedString.js +0 -35
  76. package/out/requestPreparation.js +0 -179
  77. package/out/requestValidation.js +0 -146
  78. package/out/responsePanel.js +0 -7754
  79. package/out/schemaGenerator.js +0 -562
  80. package/out/scriptRunner.js +0 -419
  81. package/out/secrets/cliSecrets.js +0 -415
  82. package/out/secrets/crypto.js +0 -105
  83. package/out/secrets/envFileSecrets.js +0 -177
  84. package/out/secrets/keyStore.js +0 -259
  85. package/out/sequenceDeclaration.js +0 -15
  86. package/out/sequenceRunner.js +0 -3590
  87. package/out/sqlAdapterRunner.js +0 -122
  88. package/out/sqlBuiltInAdapters.js +0 -604
  89. package/out/sqlConfig.js +0 -184
  90. package/out/starterCatalog.js +0 -554
  91. package/out/stringUtils.js +0 -25
  92. package/out/swaggerBodyIntellisenseCache.js +0 -114
  93. package/out/swaggerParser.js +0 -464
  94. package/out/testProvider.js +0 -767
  95. package/out/theoryCaseLoader.js +0 -113
  96. package/out/validationCache.js +0 -211
@@ -1,567 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isAssertCommand = isAssertCommand;
4
- exports.parseAssertCommand = parseAssertCommand;
5
- exports.resolveValue = resolveValue;
6
- exports.evaluateAssertion = evaluateAssertion;
7
- const schemaGenerator_1 = require("./schemaGenerator");
8
- const pathAccess_1 = require("./pathAccess");
9
- const quotedString_1 = require("./quotedString");
10
- const parser_1 = require("./parser");
11
- /**
12
- * Checks if a line is an assert command
13
- */
14
- function isAssertCommand(line) {
15
- return /^assert\s+/i.test(line.trim());
16
- }
17
- /**
18
- * Parses an assert command line.
19
- *
20
- * Supported formats:
21
- * - assert $1.status == 200
22
- * - assert $1.body.name == "John"
23
- * - assert $1.body.email contains "@"
24
- * - assert $1.body.token exists
25
- * - assert $1.status >= 200 | "Should return success status"
26
- *
27
- * @param line The assert command line
28
- * @returns Parsed assertion or null if invalid
29
- */
30
- function parseAssertCommand(line) {
31
- const trimmed = line.trim();
32
- const match = trimmed.match(/^assert\s+(.+)$/i);
33
- if (!match) {
34
- return null;
35
- }
36
- let content = match[1].trim();
37
- let message;
38
- // Check for optional message after |
39
- const pipeIndex = findUnquotedPipe(content);
40
- if (pipeIndex !== -1) {
41
- message = content.substring(pipeIndex + 1).trim();
42
- // Remove surrounding quotes from message if present
43
- if ((message.startsWith('"') && message.endsWith('"')) ||
44
- (message.startsWith("'") && message.endsWith("'"))) {
45
- message = (0, quotedString_1.decodeQuotedStringLiteral)(message);
46
- }
47
- content = content.substring(0, pipeIndex).trim();
48
- }
49
- // Try to parse unary operators first (exists, !exists)
50
- const existsMatch = content.match(/^(.+?)\s+(exists|!exists)$/i);
51
- if (existsMatch) {
52
- return {
53
- leftExpr: existsMatch[1].trim(),
54
- operator: existsMatch[2].toLowerCase(),
55
- message
56
- };
57
- }
58
- // Parse binary operators
59
- // Order matters: check multi-char operators first
60
- const binaryOperators = [
61
- { pattern: /^(.+?)\s*>=\s*(.+)$/, op: '>=' },
62
- { pattern: /^(.+?)\s*<=\s*(.+)$/, op: '<=' },
63
- { pattern: /^(.+?)\s*==\s*(.+)$/, op: '==' },
64
- { pattern: /^(.+?)\s*!=\s*(.+)$/, op: '!=' },
65
- { pattern: /^(.+?)\s*>\s*(.+)$/, op: '>' },
66
- { pattern: /^(.+?)\s*<\s*(.+)$/, op: '<' },
67
- { pattern: /^(.+?)\s+contains\s+(.+)$/i, op: 'contains' },
68
- { pattern: /^(.+?)\s+startsWith\s+(.+)$/i, op: 'startsWith' },
69
- { pattern: /^(.+?)\s+endsWith\s+(.+)$/i, op: 'endsWith' },
70
- { pattern: /^(.+?)\s+matches\s+(.+)$/i, op: 'matches' },
71
- { pattern: /^(.+?)\s+matchesSchema\s+(.+)$/i, op: 'matchesSchema' },
72
- { pattern: /^(.+?)\s+isType\s+(.+)$/i, op: 'isType' },
73
- ];
74
- for (const { pattern, op } of binaryOperators) {
75
- const binaryMatch = content.match(pattern);
76
- if (binaryMatch) {
77
- return {
78
- leftExpr: binaryMatch[1].trim(),
79
- operator: op,
80
- rightExpr: binaryMatch[2].trim(),
81
- message
82
- };
83
- }
84
- }
85
- return null;
86
- }
87
- /**
88
- * Find the index of an unquoted pipe character
89
- */
90
- function findUnquotedPipe(str) {
91
- let inQuote = false;
92
- let quoteChar = '';
93
- let escapeNext = false;
94
- for (let i = 0; i < str.length; i++) {
95
- const char = str[i];
96
- if (escapeNext) {
97
- escapeNext = false;
98
- continue;
99
- }
100
- if (inQuote && char === '\\') {
101
- escapeNext = true;
102
- continue;
103
- }
104
- if ((char === '"' || char === "'") && !inQuote) {
105
- inQuote = true;
106
- quoteChar = char;
107
- }
108
- else if (char === quoteChar && inQuote) {
109
- inQuote = false;
110
- quoteChar = '';
111
- }
112
- else if (char === '|' && !inQuote) {
113
- return i;
114
- }
115
- }
116
- return -1;
117
- }
118
- /**
119
- * Resolves a value expression to its actual value.
120
- *
121
- * Supports:
122
- * - $N.path references (e.g., $1.status, $1.body.user.id)
123
- * - String literals ("hello" or 'hello')
124
- * - Number literals (200, 3.14)
125
- * - Boolean literals (true, false)
126
- * - null
127
- * - Variable references {{varName}}
128
- *
129
- * @param expr The expression to resolve
130
- * @param responses Array of responses from the sequence (indexed by $N where N is 1-based)
131
- * @param variables Current runtime variables
132
- * @param getValueByPath Function to extract value from response
133
- * @param responseIndexToVariable Optional mapping from response index to variable name
134
- */
135
- function resolveValue(expr, responses, variables, getValueByPath, responseIndexToVariable) {
136
- const trimmed = expr.trim();
137
- const wrappedResponseRefMatch = trimmed.match(/^\{\{(\$\d+(?:\..+)?)\}\}$/);
138
- if (wrappedResponseRefMatch) {
139
- return resolveValue(wrappedResponseRefMatch[1], responses, variables, getValueByPath, responseIndexToVariable);
140
- }
141
- // Check for $N.path reference
142
- const refMatch = trimmed.match(/^\$(\d+)\.(.+)$/);
143
- if (refMatch) {
144
- const responseIdx = parseInt(refMatch[1], 10); // Keep 1-based for tracking
145
- const responseIndex = responseIdx - 1; // Convert to 0-based for array access
146
- const path = refMatch[2];
147
- if (responseIndex < 0 || responseIndex >= responses.length) {
148
- return {
149
- value: undefined,
150
- error: `Response $${refMatch[1]} does not exist (only ${responses.length} responses so far)`
151
- };
152
- }
153
- const response = responses[responseIndex];
154
- const value = getValueByPath(response, path);
155
- return {
156
- value,
157
- responseIndex: responseIdx,
158
- response,
159
- jsonPath: path,
160
- variableName: responseIndexToVariable?.get(responseIdx)
161
- };
162
- }
163
- // Check for $N without path (invalid in new syntax)
164
- if (/^\$\d+$/.test(trimmed)) {
165
- return {
166
- value: undefined,
167
- error: `Invalid reference: ${trimmed}. Use $N.body for full body or $N.status, $N.headers, etc.`
168
- };
169
- }
170
- // Check for variable.path reference (e.g., user.body.username where user is a captured response)
171
- const varPathMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])+)$/);
172
- if (varPathMatch) {
173
- const varName = varPathMatch[1];
174
- const pathPart = varPathMatch[2];
175
- if (varName in variables) {
176
- const varValue = variables[varName];
177
- // If the variable is an object (like HttpResponse), navigate the path
178
- if (typeof varValue === 'object' && varValue !== null) {
179
- const path = pathPart.replace(/^\./, ''); // Remove leading dot
180
- const value = getNestedValue(varValue, path);
181
- // Check if this looks like an HttpResponse (has status and body)
182
- const isHttpResponse = 'status' in varValue && 'body' in varValue;
183
- return {
184
- value,
185
- variableName: varName,
186
- jsonPath: path,
187
- response: isHttpResponse ? varValue : undefined
188
- };
189
- }
190
- // If it's a string, try to parse as JSON
191
- if (typeof varValue === 'string') {
192
- try {
193
- const parsed = JSON.parse(varValue);
194
- const path = pathPart.replace(/^\./, '');
195
- const value = getNestedValue(parsed, path);
196
- return { value, variableName: varName, jsonPath: path };
197
- }
198
- catch {
199
- return { value: undefined, error: `Cannot access path on non-object variable: ${varName}` };
200
- }
201
- }
202
- }
203
- return { value: undefined, error: `Variable '${varName}' is not defined` };
204
- }
205
- // Check for simple variable reference (no path) - e.g., just "user" or "token"
206
- const simpleVarMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)$/);
207
- if (simpleVarMatch) {
208
- const varName = simpleVarMatch[1];
209
- // Don't treat type keywords as variables
210
- if (!['true', 'false', 'null', 'string', 'number', 'boolean', 'object', 'array', 'undefined'].includes(varName.toLowerCase())) {
211
- if (varName in variables) {
212
- const varValue = variables[varName];
213
- // Try to parse numbers
214
- if (typeof varValue === 'string' && /^-?\d+(\.\d+)?$/.test(varValue)) {
215
- return { value: parseFloat(varValue) };
216
- }
217
- return { value: varValue };
218
- }
219
- // Variable not found - but could also be a type name for isType, so don't error here
220
- }
221
- }
222
- // Check for variable reference {{varName}}, {{varName.path}}, or {{$env.name}}
223
- const varMatch = trimmed.match(/^\{\{(\$env|[a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)\}\}$/);
224
- if (varMatch) {
225
- const varName = varMatch[1];
226
- const pathPart = varMatch[2] || '';
227
- if (varName in variables) {
228
- const varValue = variables[varName];
229
- if (pathPart) {
230
- if (typeof varValue === 'object' && varValue !== null) {
231
- const path = pathPart.replace(/^\./, '');
232
- return { value: getNestedValue(varValue, path) };
233
- }
234
- if (typeof varValue === 'string') {
235
- try {
236
- const parsed = JSON.parse(varValue);
237
- const path = pathPart.replace(/^\./, '');
238
- return { value: getNestedValue(parsed, path) };
239
- }
240
- catch {
241
- return { value: undefined, error: `Cannot access path on non-object variable: ${varName}` };
242
- }
243
- }
244
- return { value: undefined, error: `Cannot access path on non-object variable: ${varName}` };
245
- }
246
- if (typeof varValue === 'string' && /^-?\d+(\.\d+)?$/.test(varValue)) {
247
- return { value: parseFloat(varValue) };
248
- }
249
- return { value: varValue };
250
- }
251
- return { value: undefined, error: `Variable {{${varName}}} is not defined` };
252
- }
253
- // Check for string literal
254
- if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
255
- (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
256
- return { value: (0, parser_1.substituteVariables)((0, quotedString_1.decodeQuotedStringLiteral)(trimmed), variables) };
257
- }
258
- // Check for number literal
259
- if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
260
- return { value: parseFloat(trimmed) };
261
- }
262
- // Check for boolean literals
263
- if (trimmed.toLowerCase() === 'true') {
264
- return { value: true };
265
- }
266
- if (trimmed.toLowerCase() === 'false') {
267
- return { value: false };
268
- }
269
- // Check for null
270
- if (trimmed.toLowerCase() === 'null') {
271
- return { value: null };
272
- }
273
- // Check for empty array literal
274
- if (trimmed === '[]') {
275
- return { value: [] };
276
- }
277
- // Check for empty object literal
278
- if (trimmed === '{}') {
279
- return { value: {} };
280
- }
281
- // Unknown expression type
282
- return { value: undefined, error: `Cannot resolve expression: ${trimmed}` };
283
- }
284
- /**
285
- * Helper function to get nested value from an object using dot notation path
286
- */
287
- function getNestedValue(obj, path) {
288
- return (0, pathAccess_1.getNestedPathValue)(obj, path);
289
- }
290
- function areValuesEqual(leftValue, rightValue) {
291
- if (leftValue === rightValue) {
292
- return true;
293
- }
294
- if (leftValue === null || rightValue === null || leftValue === undefined || rightValue === undefined) {
295
- return leftValue === rightValue;
296
- }
297
- const leftIsArray = Array.isArray(leftValue);
298
- const rightIsArray = Array.isArray(rightValue);
299
- if (leftIsArray || rightIsArray) {
300
- if (!leftIsArray || !rightIsArray) {
301
- return false;
302
- }
303
- if (leftValue.length !== rightValue.length) {
304
- return false;
305
- }
306
- for (let i = 0; i < leftValue.length; i++) {
307
- if (!areValuesEqual(leftValue[i], rightValue[i])) {
308
- return false;
309
- }
310
- }
311
- return true;
312
- }
313
- const leftIsObject = typeof leftValue === 'object';
314
- const rightIsObject = typeof rightValue === 'object';
315
- if (leftIsObject || rightIsObject) {
316
- if (!leftIsObject || !rightIsObject) {
317
- return false;
318
- }
319
- const leftKeys = Object.keys(leftValue);
320
- const rightKeys = Object.keys(rightValue);
321
- if (leftKeys.length !== rightKeys.length) {
322
- return false;
323
- }
324
- for (const key of leftKeys) {
325
- if (!Object.prototype.hasOwnProperty.call(rightValue, key)) {
326
- return false;
327
- }
328
- if (!areValuesEqual(leftValue[key], rightValue[key])) {
329
- return false;
330
- }
331
- }
332
- return true;
333
- }
334
- // Preserve assertion language coercion after structural checks, e.g. "200" equals 200.
335
- // eslint-disable-next-line eqeqeq
336
- return leftValue == rightValue;
337
- }
338
- /**
339
- * Evaluates an assertion.
340
- *
341
- * @param assertion Parsed assertion
342
- * @param responses Array of responses from the sequence
343
- * @param variables Current runtime variables (can contain objects like HttpResponse)
344
- * @param getValueByPath Function to extract value from response
345
- * @param responseIndexToVariable Optional mapping from response index (1-based) to variable name
346
- * @param basePath Optional base path for resolving relative schema file paths
347
- */
348
- function evaluateAssertion(assertion, responses, variables, getValueByPath, responseIndexToVariable, basePath) {
349
- const leftResult = resolveValue(assertion.leftExpr, responses, variables, getValueByPath, responseIndexToVariable);
350
- const message = assertion.message === undefined ? undefined : (0, parser_1.substituteVariables)(assertion.message, variables);
351
- const expression = formatExpression(assertion, variables);
352
- // Helper to build failure context from left result
353
- const buildFailureContext = () => ({
354
- responseIndex: leftResult.responseIndex,
355
- friendlyName: leftResult.variableName,
356
- relatedResponse: leftResult.response,
357
- jsonPath: leftResult.jsonPath
358
- });
359
- // Handle resolution errors
360
- if (leftResult.error) {
361
- return {
362
- passed: false,
363
- expression,
364
- message,
365
- operator: assertion.operator,
366
- leftValue: undefined,
367
- leftExpression: assertion.leftExpr,
368
- rightExpression: assertion.rightExpr,
369
- error: leftResult.error,
370
- ...buildFailureContext()
371
- };
372
- }
373
- const leftValue = leftResult.value;
374
- // Handle unary operators
375
- if (assertion.operator === 'exists') {
376
- const passed = leftValue !== undefined && leftValue !== null;
377
- return {
378
- passed,
379
- expression,
380
- message,
381
- operator: assertion.operator,
382
- leftValue,
383
- leftExpression: assertion.leftExpr,
384
- ...(!passed ? buildFailureContext() : {})
385
- };
386
- }
387
- if (assertion.operator === '!exists') {
388
- const passed = leftValue === undefined || leftValue === null;
389
- return {
390
- passed,
391
- expression,
392
- message,
393
- operator: assertion.operator,
394
- leftValue,
395
- leftExpression: assertion.leftExpr,
396
- ...(!passed ? buildFailureContext() : {})
397
- };
398
- }
399
- // Binary operators require right expression
400
- if (!assertion.rightExpr) {
401
- return {
402
- passed: false,
403
- expression,
404
- message,
405
- operator: assertion.operator,
406
- leftValue,
407
- leftExpression: assertion.leftExpr,
408
- error: `Operator ${assertion.operator} requires a right-hand value`,
409
- ...buildFailureContext()
410
- };
411
- }
412
- // For isType operator, the right side is a literal type name, not a value to resolve
413
- if (assertion.operator === 'isType') {
414
- const expectedType = assertion.rightExpr.toLowerCase();
415
- let actualType;
416
- if (leftValue === null) {
417
- actualType = 'null';
418
- }
419
- else if (Array.isArray(leftValue)) {
420
- actualType = 'array';
421
- }
422
- else {
423
- actualType = typeof leftValue;
424
- }
425
- const passed = actualType === expectedType;
426
- return {
427
- passed,
428
- expression,
429
- message,
430
- operator: assertion.operator,
431
- leftValue,
432
- rightValue: expectedType,
433
- leftExpression: assertion.leftExpr,
434
- rightExpression: assertion.rightExpr,
435
- ...(!passed ? buildFailureContext() : {})
436
- };
437
- }
438
- // For matchesSchema operator, validate against a JSON Schema file
439
- if (assertion.operator === 'matchesSchema') {
440
- // Right side is a file path (with or without quotes)
441
- let schemaPath = assertion.rightExpr;
442
- // Remove surrounding quotes if present
443
- if ((schemaPath.startsWith('"') && schemaPath.endsWith('"')) ||
444
- (schemaPath.startsWith("'") && schemaPath.endsWith("'"))) {
445
- schemaPath = schemaPath.slice(1, -1);
446
- }
447
- schemaPath = (0, parser_1.substituteVariables)(schemaPath, variables);
448
- const validationResult = (0, schemaGenerator_1.validateAgainstSchemaDetailed)(leftValue, schemaPath, basePath);
449
- const passed = validationResult.valid;
450
- return {
451
- passed,
452
- expression,
453
- message,
454
- operator: assertion.operator,
455
- leftValue,
456
- rightValue: schemaPath,
457
- leftExpression: assertion.leftExpr,
458
- rightExpression: assertion.rightExpr,
459
- error: !passed && validationResult.errorStrings ? validationResult.errorStrings.join('; ') : undefined,
460
- schemaErrors: validationResult.errors,
461
- schemaPath: validationResult.resolvedSchemaPath,
462
- schema: validationResult.schema,
463
- schemaSummary: validationResult.summary,
464
- ...(!passed ? buildFailureContext() : {})
465
- };
466
- }
467
- const rightResult = resolveValue(assertion.rightExpr, responses, variables, getValueByPath, responseIndexToVariable);
468
- if (rightResult.error) {
469
- return {
470
- passed: false,
471
- expression,
472
- message,
473
- operator: assertion.operator,
474
- leftValue,
475
- rightValue: undefined,
476
- leftExpression: assertion.leftExpr,
477
- rightExpression: assertion.rightExpr,
478
- error: rightResult.error,
479
- ...buildFailureContext()
480
- };
481
- }
482
- const rightValue = rightResult.value;
483
- let passed = false;
484
- switch (assertion.operator) {
485
- case '==':
486
- passed = areValuesEqual(leftValue, rightValue);
487
- break;
488
- case '!=':
489
- passed = !areValuesEqual(leftValue, rightValue);
490
- break;
491
- case '>':
492
- passed = Number(leftValue) > Number(rightValue);
493
- break;
494
- case '>=':
495
- passed = Number(leftValue) >= Number(rightValue);
496
- break;
497
- case '<':
498
- passed = Number(leftValue) < Number(rightValue);
499
- break;
500
- case '<=':
501
- passed = Number(leftValue) <= Number(rightValue);
502
- break;
503
- case 'contains':
504
- passed = String(leftValue).includes(String(rightValue));
505
- break;
506
- case 'startsWith':
507
- passed = String(leftValue).startsWith(String(rightValue));
508
- break;
509
- case 'endsWith':
510
- passed = String(leftValue).endsWith(String(rightValue));
511
- break;
512
- case 'matches':
513
- try {
514
- // Remove regex delimiters if present
515
- let pattern = String(rightValue);
516
- if (pattern.startsWith('/') && pattern.lastIndexOf('/') > 0) {
517
- const lastSlash = pattern.lastIndexOf('/');
518
- const flags = pattern.substring(lastSlash + 1);
519
- pattern = pattern.substring(1, lastSlash);
520
- passed = new RegExp(pattern, flags).test(String(leftValue));
521
- }
522
- else {
523
- passed = new RegExp(pattern).test(String(leftValue));
524
- }
525
- }
526
- catch (e) {
527
- return {
528
- passed: false,
529
- expression,
530
- message,
531
- operator: assertion.operator,
532
- leftValue,
533
- rightValue,
534
- leftExpression: assertion.leftExpr,
535
- rightExpression: assertion.rightExpr,
536
- error: `Invalid regex pattern: ${rightValue}`,
537
- ...buildFailureContext()
538
- };
539
- }
540
- break;
541
- // Note: isType is handled earlier before resolveValue is called
542
- }
543
- return {
544
- passed,
545
- expression,
546
- message,
547
- operator: assertion.operator,
548
- leftValue,
549
- rightValue,
550
- leftExpression: assertion.leftExpr,
551
- rightExpression: assertion.rightExpr,
552
- ...(!passed ? buildFailureContext() : {})
553
- };
554
- }
555
- /**
556
- * Format the assertion expression for display
557
- */
558
- function formatExpression(assertion, variables) {
559
- if (assertion.rightExpr) {
560
- return substituteAssertionDisplayTemplates(`${assertion.leftExpr} ${assertion.operator} ${assertion.rightExpr}`, variables);
561
- }
562
- return substituteAssertionDisplayTemplates(`${assertion.leftExpr} ${assertion.operator}`, variables);
563
- }
564
- function substituteAssertionDisplayTemplates(expression, variables) {
565
- return variables ? (0, parser_1.substituteVariables)(expression, variables) : expression;
566
- }
567
- //# sourceMappingURL=assertionRunner.js.map
package/out/cacheDir.js DELETED
@@ -1,136 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.NORN_CACHE_DIR = void 0;
37
- exports.getSearchStartDirectory = getSearchStartDirectory;
38
- exports.findProjectRoot = findProjectRoot;
39
- exports.ensureNornCacheGitignore = ensureNornCacheGitignore;
40
- exports.ensureNornCacheDir = ensureNornCacheDir;
41
- exports.getNornCacheFilePath = getNornCacheFilePath;
42
- exports.loadVersionedJsonCache = loadVersionedJsonCache;
43
- exports.saveVersionedJsonCache = saveVersionedJsonCache;
44
- const fs = __importStar(require("fs"));
45
- const path = __importStar(require("path"));
46
- exports.NORN_CACHE_DIR = '.norn-cache';
47
- const CACHE_GITIGNORE_FILE = '.gitignore';
48
- const CACHE_GITIGNORE_CONTENT = '*\n';
49
- const PROJECT_ROOT_MARKERS = ['.git', 'package.json', 'pnpm-workspace.yaml', 'package-lock.json', 'yarn.lock', '.nornenv'];
50
- function getSearchStartDirectory(targetPath) {
51
- const resolvedPath = path.resolve(targetPath);
52
- try {
53
- const stats = fs.statSync(resolvedPath);
54
- return stats.isDirectory() ? resolvedPath : path.dirname(resolvedPath);
55
- }
56
- catch {
57
- return path.dirname(resolvedPath);
58
- }
59
- }
60
- function findProjectRoot(targetPath) {
61
- let dir = getSearchStartDirectory(targetPath);
62
- let projectRoot;
63
- while (true) {
64
- if (PROJECT_ROOT_MARKERS.some(marker => fs.existsSync(path.join(dir, marker)))) {
65
- projectRoot = dir;
66
- }
67
- const parent = path.dirname(dir);
68
- if (parent === dir) {
69
- break;
70
- }
71
- dir = parent;
72
- }
73
- return projectRoot ?? getSearchStartDirectory(targetPath);
74
- }
75
- function ensureNornCacheGitignore(cacheDir) {
76
- const gitignorePath = path.join(cacheDir, CACHE_GITIGNORE_FILE);
77
- try {
78
- const hasDesiredContent = fs.existsSync(gitignorePath)
79
- && fs.readFileSync(gitignorePath, 'utf8') === CACHE_GITIGNORE_CONTENT;
80
- if (hasDesiredContent) {
81
- return;
82
- }
83
- fs.writeFileSync(gitignorePath, CACHE_GITIGNORE_CONTENT, 'utf8');
84
- }
85
- catch {
86
- // Best effort only; failure here should not block cache writes.
87
- }
88
- }
89
- function ensureNornCacheDir(rootPath) {
90
- const cacheDir = path.join(rootPath, exports.NORN_CACHE_DIR);
91
- try {
92
- if (!fs.existsSync(cacheDir)) {
93
- fs.mkdirSync(cacheDir, { recursive: true });
94
- }
95
- ensureNornCacheGitignore(cacheDir);
96
- return cacheDir;
97
- }
98
- catch {
99
- return undefined;
100
- }
101
- }
102
- function getNornCacheFilePath(rootPath, fileName) {
103
- return path.join(rootPath, exports.NORN_CACHE_DIR, fileName);
104
- }
105
- function loadVersionedJsonCache(options) {
106
- if (!options.cachePath || !fs.existsSync(options.cachePath)) {
107
- return options.createDefault();
108
- }
109
- try {
110
- const content = fs.readFileSync(options.cachePath, 'utf-8');
111
- const parsed = JSON.parse(content);
112
- if (parsed.version !== options.version) {
113
- return options.createDefault();
114
- }
115
- if (options.isValid && !options.isValid(parsed)) {
116
- return options.createDefault();
117
- }
118
- return parsed;
119
- }
120
- catch {
121
- return options.createDefault();
122
- }
123
- }
124
- function saveVersionedJsonCache(cachePath, cache, ensureDirectory) {
125
- if (!cachePath || !ensureDirectory()) {
126
- return false;
127
- }
128
- try {
129
- fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2), 'utf-8');
130
- return true;
131
- }
132
- catch {
133
- return false;
134
- }
135
- }
136
- //# sourceMappingURL=cacheDir.js.map