norn-cli 2.2.2 → 2.4.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 (113) hide show
  1. package/.claude/settings.local.json +18 -0
  2. package/.claude/skills/norn-social-campaign/SKILL.md +70 -0
  3. package/CHANGELOG.md +22 -1
  4. package/LICENSE +20 -29
  5. package/README.md +32 -1
  6. package/demos/nornenv-region-refactor/README.md +64 -0
  7. package/demos/nornenv-showcase/README.md +62 -0
  8. package/demos/nornenv-showcase/norn.config.json +16 -0
  9. package/demos/nornenv-showcase/showcase.norn +70 -0
  10. package/demos/nornenv-showcase/showcase.nornapi +26 -0
  11. package/demos/nornenv-showcase/showcase.nornsql +20 -0
  12. package/dist/cli.js +564 -54
  13. package/out/apiResponseIntellisenseCache.js +394 -0
  14. package/out/assertionRunner.js +567 -0
  15. package/out/cacheDir.js +136 -0
  16. package/out/chatParticipant.js +763 -0
  17. package/out/cli/colors.js +127 -0
  18. package/out/cli/formatters/assertion.js +102 -0
  19. package/out/cli/formatters/index.js +23 -0
  20. package/out/cli/formatters/response.js +106 -0
  21. package/out/cli/formatters/summary.js +246 -0
  22. package/out/cli/redaction.js +237 -0
  23. package/out/cli/reporters/html.js +689 -0
  24. package/out/cli/reporters/index.js +22 -0
  25. package/out/cli/reporters/junit.js +226 -0
  26. package/out/codeLensProvider.js +351 -0
  27. package/out/compareContentProvider.js +85 -0
  28. package/out/completionProvider.js +3739 -0
  29. package/out/contractAssertionSummary.js +225 -0
  30. package/out/contractDecorationProvider.js +243 -0
  31. package/out/coverageCalculator.js +879 -0
  32. package/out/coveragePanel.js +597 -0
  33. package/out/debug/breakpointResolver.js +84 -0
  34. package/out/debug/breakpoints.js +52 -0
  35. package/out/debug/nornDebugAdapter.js +166 -0
  36. package/out/debug/nornDebugSession.js +613 -0
  37. package/out/debug/sequenceLocationIndex.js +77 -0
  38. package/out/debug/types.js +3 -0
  39. package/out/deepClone.js +21 -0
  40. package/out/diagnosticProvider.js +2554 -0
  41. package/out/environmentParser.js +736 -0
  42. package/out/environmentProvider.js +544 -0
  43. package/out/environmentTemplates.js +146 -0
  44. package/out/errors/formatError.js +113 -0
  45. package/out/errors/nornError.js +29 -0
  46. package/out/formUrlEncoded.js +89 -0
  47. package/out/httpClient.js +348 -0
  48. package/out/httpRuntimeOptions.js +16 -0
  49. package/out/importErrors.js +31 -0
  50. package/out/inlayHintResolver.js +70 -0
  51. package/out/jsonFileReader.js +323 -0
  52. package/out/mcpClient.js +193 -0
  53. package/out/mcpConfig.js +184 -0
  54. package/out/mcpToolIntellisenseCache.js +96 -0
  55. package/out/mcpToolSchema.js +50 -0
  56. package/out/nornConfig.js +132 -0
  57. package/out/nornHoverProvider.js +124 -0
  58. package/out/nornInlayHintsProvider.js +191 -0
  59. package/out/nornPrompt.js +755 -0
  60. package/out/nornSqlParser.js +286 -0
  61. package/out/nornapiHoverProvider.js +135 -0
  62. package/out/nornapiInlayHintsProvider.js +94 -0
  63. package/out/nornapiParser.js +324 -0
  64. package/out/nornenvCodeActionProvider.js +101 -0
  65. package/out/nornenvDecorationProvider.js +239 -0
  66. package/out/nornenvFoldingProvider.js +63 -0
  67. package/out/nornenvHoverProvider.js +114 -0
  68. package/out/nornenvInlayHintsProvider.js +99 -0
  69. package/out/nornenvLanguageModel.js +187 -0
  70. package/out/nornenvRegionRefactor.js +267 -0
  71. package/out/nornsqlHoverProvider.js +95 -0
  72. package/out/nornsqlInlayHintsProvider.js +114 -0
  73. package/out/parser.js +839 -0
  74. package/out/pathAccess.js +28 -0
  75. package/out/postmanImportPanel.js +732 -0
  76. package/out/postmanImportPlanner.js +1155 -0
  77. package/out/postmanImportSidebarView.js +532 -0
  78. package/out/quotedString.js +35 -0
  79. package/out/requestPreparation.js +179 -0
  80. package/out/requestValidation.js +146 -0
  81. package/out/responsePanel.js +7754 -0
  82. package/out/schemaGenerator.js +562 -0
  83. package/out/scriptRunner.js +419 -0
  84. package/out/secrets/cliSecrets.js +415 -0
  85. package/out/secrets/crypto.js +105 -0
  86. package/out/secrets/envFileSecrets.js +177 -0
  87. package/out/secrets/keyStore.js +259 -0
  88. package/out/sequenceDeclaration.js +15 -0
  89. package/out/sequenceRunner.js +3590 -0
  90. package/out/sqlAdapterRunner.js +122 -0
  91. package/out/sqlBuiltInAdapters.js +604 -0
  92. package/out/sqlConfig.js +184 -0
  93. package/out/starterCatalog.js +554 -0
  94. package/out/stringUtils.js +25 -0
  95. package/out/swaggerBodyIntellisenseCache.js +114 -0
  96. package/out/swaggerParser.js +464 -0
  97. package/out/testProvider.js +767 -0
  98. package/out/theoryCaseLoader.js +113 -0
  99. package/out/validationCache.js +211 -0
  100. package/package.json +38 -11
  101. package/.kanbn/index.md +0 -31
  102. package/.kanbn/tasks/book-first-mentor-session.md +0 -13
  103. package/.kanbn/tasks/decide-what-success-in-a-pilot-looks-like.md +0 -9
  104. package/.kanbn/tasks/do-5-customer-conversations.md +0 -9
  105. package/.kanbn/tasks/finalise-the-one-line-pitch.md +0 -11
  106. package/.kanbn/tasks/interview-script.md +0 -49
  107. package/.kanbn/tasks/make-a-list-of-10-people-to-speak-to.md +0 -11
  108. package/.kanbn/tasks/prepare-your-customer-interview-questions.md +0 -11
  109. package/.kanbn/tasks/recruit-2/342/200/2233-pilot-users.md +0 -9
  110. package/.kanbn/tasks/refine-your-pitch.md +0 -9
  111. package/.kanbn/tasks/use-the-shiplight-website-as-a-template-to-improve-norn-website.md +0 -9
  112. package/.kanbn/tasks/write-down-repeated-wording.md +0 -9
  113. package/.kanbn/tasks/write-the-one-pager.md +0 -27
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatUserFacingError = formatUserFacingError;
4
+ const nornError_1 = require("./nornError");
5
+ function mergeContext(base, overrides) {
6
+ if (!base && !overrides) {
7
+ return undefined;
8
+ }
9
+ return {
10
+ ...(base || {}),
11
+ ...(overrides || {}),
12
+ environment: {
13
+ ...(base?.environment || {}),
14
+ ...(overrides?.environment || {})
15
+ }
16
+ };
17
+ }
18
+ function normalizeKnownError(error, context) {
19
+ if ((0, nornError_1.isNornError)(error)) {
20
+ if (!context) {
21
+ return error;
22
+ }
23
+ return new nornError_1.NornError({
24
+ category: error.category,
25
+ code: error.code,
26
+ message: error.message,
27
+ hint: error.hint,
28
+ details: error.details,
29
+ context: mergeContext(error.context, context),
30
+ cause: error.cause
31
+ });
32
+ }
33
+ if (error instanceof Error) {
34
+ const msg = error.message || String(error);
35
+ if (msg === 'No valid HTTP method found') {
36
+ return new nornError_1.NornError({
37
+ category: 'syntax',
38
+ code: 'request-missing-method',
39
+ message: 'Could not parse request: no valid HTTP method found.',
40
+ hint: 'Start the request with a valid HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS).',
41
+ context: mergeContext(undefined, context)
42
+ });
43
+ }
44
+ if (msg.startsWith('Unknown endpoint: ')) {
45
+ const endpointName = msg.slice('Unknown endpoint: '.length).trim();
46
+ return new nornError_1.NornError({
47
+ category: 'validation',
48
+ code: 'unknown-endpoint',
49
+ message: `Unknown endpoint '${endpointName}'.`,
50
+ hint: 'Import a .nornapi file that defines this endpoint, or fix the endpoint name.',
51
+ context: mergeContext(undefined, context)
52
+ });
53
+ }
54
+ if (/invalid url/i.test(msg) || /unsupported protocol/i.test(msg)) {
55
+ return new nornError_1.NornError({
56
+ category: 'url',
57
+ code: 'invalid-url',
58
+ message: `Invalid request URL.`,
59
+ details: msg,
60
+ hint: 'Check the request URL and any substituted variables (for example {{baseUrl}}).',
61
+ context: mergeContext(undefined, context)
62
+ });
63
+ }
64
+ return error;
65
+ }
66
+ return new Error(String(error));
67
+ }
68
+ function formatUserFacingError(error, context) {
69
+ const normalized = normalizeKnownError(error, context);
70
+ if (!(0, nornError_1.isNornError)(normalized)) {
71
+ if (normalized instanceof Error) {
72
+ return normalized.message;
73
+ }
74
+ return String(normalized);
75
+ }
76
+ const lines = [normalized.message];
77
+ const ctx = normalized.context;
78
+ if (ctx?.stepIndex !== undefined) {
79
+ lines.push(`Step: ${ctx.stepIndex}`);
80
+ }
81
+ if (ctx?.requestName) {
82
+ lines.push(`Request: ${ctx.requestName}`);
83
+ }
84
+ else if (ctx?.method || ctx?.url) {
85
+ const requestLine = [ctx.method, ctx.url].filter(Boolean).join(' ');
86
+ if (requestLine) {
87
+ lines.push(`Request: ${requestLine}`);
88
+ }
89
+ }
90
+ if (ctx?.filePath) {
91
+ lines.push(`File: ${ctx.filePath}`);
92
+ }
93
+ if (ctx?.environment) {
94
+ const env = ctx.environment;
95
+ if (env.hasEnvFile) {
96
+ const active = env.activeEnvironment ?? 'none';
97
+ const available = env.availableEnvironments?.length
98
+ ? env.availableEnvironments.join(', ')
99
+ : 'none';
100
+ lines.push(`Environment: ${active} (available: ${available})`);
101
+ }
102
+ }
103
+ if (normalized.details) {
104
+ for (const detail of normalized.details) {
105
+ lines.push(detail);
106
+ }
107
+ }
108
+ if (normalized.hint) {
109
+ lines.push(`Hint: ${normalized.hint}`);
110
+ }
111
+ return lines.join('\n');
112
+ }
113
+ //# sourceMappingURL=formatError.js.map
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NornError = void 0;
4
+ exports.isNornError = isNornError;
5
+ class NornError extends Error {
6
+ category;
7
+ code;
8
+ hint;
9
+ details;
10
+ context;
11
+ cause;
12
+ constructor(options) {
13
+ super(options.message);
14
+ this.name = 'NornError';
15
+ this.category = options.category;
16
+ this.code = options.code;
17
+ this.hint = options.hint;
18
+ this.details = Array.isArray(options.details)
19
+ ? options.details
20
+ : options.details ? [options.details] : undefined;
21
+ this.context = options.context;
22
+ this.cause = options.cause;
23
+ }
24
+ }
25
+ exports.NornError = NornError;
26
+ function isNornError(error) {
27
+ return error instanceof NornError;
28
+ }
29
+ //# sourceMappingURL=nornError.js.map
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFormUrlEncodedContentType = isFormUrlEncodedContentType;
4
+ exports.parseFormUrlEncodedLines = parseFormUrlEncodedLines;
5
+ exports.parseFormUrlEncodedBody = parseFormUrlEncodedBody;
6
+ exports.encodeFormUrlEncodedBody = encodeFormUrlEncodedBody;
7
+ function parseEqualsField(segment) {
8
+ const eqIndex = segment.indexOf('=');
9
+ if (eqIndex <= 0) {
10
+ return null;
11
+ }
12
+ return {
13
+ key: segment.substring(0, eqIndex).trim(),
14
+ value: segment.substring(eqIndex + 1).trim()
15
+ };
16
+ }
17
+ function isFormUrlEncodedContentType(contentType) {
18
+ return Boolean(contentType && contentType.toLowerCase().includes('application/x-www-form-urlencoded'));
19
+ }
20
+ function parseFormUrlEncodedLines(lines) {
21
+ const fields = [];
22
+ const errors = [];
23
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
24
+ const line = lines[lineIndex].trim();
25
+ if (!line) {
26
+ continue;
27
+ }
28
+ const firstEqIndex = line.indexOf('=');
29
+ const firstColonIndex = line.indexOf(':');
30
+ if (firstEqIndex > 0 && (firstColonIndex === -1 || firstEqIndex < firstColonIndex)) {
31
+ if (line.includes('&')) {
32
+ const segments = line.split('&').map(segment => segment.trim());
33
+ const hasInvalidSegment = segments.some(segment => !parseEqualsField(segment));
34
+ if (hasInvalidSegment) {
35
+ errors.push({
36
+ lineIndex,
37
+ line,
38
+ message: 'Ampersand-delimited form body lines must use key=value for every segment.'
39
+ });
40
+ continue;
41
+ }
42
+ for (const segment of segments) {
43
+ const field = parseEqualsField(segment);
44
+ if (field) {
45
+ fields.push(field);
46
+ }
47
+ }
48
+ continue;
49
+ }
50
+ const field = parseEqualsField(line);
51
+ if (field) {
52
+ fields.push(field);
53
+ continue;
54
+ }
55
+ }
56
+ if (firstColonIndex > 0) {
57
+ fields.push({
58
+ key: line.substring(0, firstColonIndex).trim(),
59
+ value: line.substring(firstColonIndex + 1).trim()
60
+ });
61
+ continue;
62
+ }
63
+ errors.push({
64
+ lineIndex,
65
+ line,
66
+ message: 'Expected key=value, key: value, or ampersand-delimited key=value pairs.'
67
+ });
68
+ }
69
+ return { fields, errors };
70
+ }
71
+ function parseFormUrlEncodedBody(body) {
72
+ if (!body) {
73
+ return { fields: [], errors: [] };
74
+ }
75
+ return parseFormUrlEncodedLines(body.split('\n'));
76
+ }
77
+ function encodeFormUrlEncodedBody(body) {
78
+ const { fields, errors } = parseFormUrlEncodedBody(body);
79
+ if (errors.length > 0) {
80
+ return { encodedBody: '', errors };
81
+ }
82
+ return {
83
+ encodedBody: fields
84
+ .map(field => `${encodeURIComponent(field.key)}=${encodeURIComponent(field.value)}`)
85
+ .join('&'),
86
+ errors: []
87
+ };
88
+ }
89
+ //# sourceMappingURL=formUrlEncoded.js.map
@@ -0,0 +1,348 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.createCookieJar = createCookieJar;
40
+ exports.getSharedCookieJar = getSharedCookieJar;
41
+ exports.clearCookies = clearCookies;
42
+ exports.getCookiesForUrl = getCookiesForUrl;
43
+ exports.getAllCookies = getAllCookies;
44
+ exports.sendRequest = sendRequest;
45
+ exports.sendRequestWithJar = sendRequestWithJar;
46
+ const axios_1 = __importDefault(require("axios"));
47
+ const https = __importStar(require("https"));
48
+ const tough_cookie_1 = require("tough-cookie");
49
+ const nornError_1 = require("./errors/nornError");
50
+ const httpRuntimeOptions_1 = require("./httpRuntimeOptions");
51
+ const formUrlEncoded_1 = require("./formUrlEncoded");
52
+ // Shared cookie jar for VS Code extension (persists across requests)
53
+ let sharedCookieJar = new tough_cookie_1.CookieJar();
54
+ const insecureHttpsAgent = new https.Agent({ rejectUnauthorized: false });
55
+ function getHttpsAgent() {
56
+ return (0, httpRuntimeOptions_1.getVerifyTlsCertificates)() ? undefined : insecureHttpsAgent;
57
+ }
58
+ /**
59
+ * Creates a fresh cookie jar for isolated execution (CLI, sequences)
60
+ */
61
+ function createCookieJar() {
62
+ return new tough_cookie_1.CookieJar();
63
+ }
64
+ /**
65
+ * Gets the shared cookie jar
66
+ */
67
+ function getSharedCookieJar() {
68
+ return sharedCookieJar;
69
+ }
70
+ /**
71
+ * Clears all cookies from the shared jar
72
+ */
73
+ function clearCookies() {
74
+ sharedCookieJar = new tough_cookie_1.CookieJar();
75
+ }
76
+ /**
77
+ * Gets all cookies from a jar for a specific URL
78
+ */
79
+ async function getCookiesForUrl(url, jar) {
80
+ const targetJar = jar || sharedCookieJar;
81
+ try {
82
+ const cookies = await targetJar.getCookies(url);
83
+ return cookies.map(cookieToInfo);
84
+ }
85
+ catch {
86
+ return [];
87
+ }
88
+ }
89
+ /**
90
+ * Gets all cookies from a jar
91
+ */
92
+ async function getAllCookies(jar) {
93
+ const targetJar = jar || sharedCookieJar;
94
+ const serialized = await targetJar.serialize();
95
+ return (serialized.cookies || []).map((c) => ({
96
+ name: String(c.key || ''),
97
+ value: String(c.value || ''),
98
+ domain: String(c.domain || ''),
99
+ path: String(c.path || '/'),
100
+ expires: c.expires ? String(c.expires) : undefined,
101
+ httpOnly: Boolean(c.httpOnly),
102
+ secure: Boolean(c.secure),
103
+ }));
104
+ }
105
+ function cookieToInfo(cookie) {
106
+ return {
107
+ name: cookie.key,
108
+ value: cookie.value,
109
+ domain: cookie.domain || '',
110
+ path: cookie.path || '/',
111
+ expires: cookie.expires instanceof Date ? cookie.expires.toISOString() : undefined,
112
+ httpOnly: cookie.httpOnly || false,
113
+ secure: cookie.secure || false,
114
+ };
115
+ }
116
+ /**
117
+ * Sends a request using the shared cookie jar (for VS Code extension)
118
+ */
119
+ async function sendRequest(request, retryOptions) {
120
+ return sendRequestWithJar(request, sharedCookieJar, retryOptions);
121
+ }
122
+ /**
123
+ * Helper function to wait
124
+ */
125
+ function sleep(ms) {
126
+ return new Promise(resolve => setTimeout(resolve, ms));
127
+ }
128
+ /**
129
+ * Check if a response indicates failure (should retry)
130
+ * Retries on: network errors, 5xx server errors, 429 rate limit
131
+ */
132
+ function shouldRetry(response, error) {
133
+ // Always retry on network errors
134
+ if (error) {
135
+ return true;
136
+ }
137
+ // Retry on server errors (5xx) or rate limiting (429)
138
+ if (response && (response.status >= 500 || response.status === 429)) {
139
+ return true;
140
+ }
141
+ return false;
142
+ }
143
+ /**
144
+ * Sends a request using a specific cookie jar
145
+ * Manually follows redirects to properly capture Set-Cookie headers at each step
146
+ * Supports automatic retry with linear backoff
147
+ */
148
+ async function sendRequestWithJar(request, jar, retryOptions) {
149
+ const totalAttempts = (retryOptions?.retryCount ?? 0) + 1; // +1 for initial attempt
150
+ const backoffMs = retryOptions?.backoffMs ?? 1000;
151
+ const retriedErrors = [];
152
+ let lastError = null;
153
+ let attemptsMade = 0;
154
+ for (let attempt = 1; attempt <= totalAttempts; attempt++) {
155
+ attemptsMade = attempt;
156
+ const startTime = Date.now();
157
+ const maxRedirects = 10;
158
+ let redirectCount = 0;
159
+ let currentUrl = request.url;
160
+ let currentMethod = request.method;
161
+ let currentBody = request.body;
162
+ let finalResponse = null;
163
+ let sentRequestBody = undefined;
164
+ let sentRequestHeaders = {};
165
+ try {
166
+ while (redirectCount <= maxRedirects) {
167
+ // Get cookies for current URL
168
+ const existingCookies = await jar.getCookies(currentUrl);
169
+ const cookieHeader = existingCookies.map(c => `${c.key}=${c.value}`).join('; ');
170
+ const headers = { ...request.headers };
171
+ if (cookieHeader) {
172
+ headers['Cookie'] = cookieHeader;
173
+ }
174
+ // Determine how to send the body based on Content-Type
175
+ let data = undefined;
176
+ if (currentBody) {
177
+ const contentType = Object.keys(headers).find((key) => key.toLowerCase() === 'content-type');
178
+ const contentTypeValue = contentType ? headers[contentType] : '';
179
+ if ((0, formUrlEncoded_1.isFormUrlEncodedContentType)(contentTypeValue)) {
180
+ const encoded = (0, formUrlEncoded_1.encodeFormUrlEncodedBody)(currentBody);
181
+ if (encoded.errors.length > 0) {
182
+ throw new nornError_1.NornError({
183
+ category: 'syntax',
184
+ code: 'invalid-form-urlencoded-body',
185
+ message: 'Request body is not valid application/x-www-form-urlencoded syntax.',
186
+ details: encoded.errors.map(error => `Body line ${error.lineIndex + 1}: ${error.message} (${error.line})`),
187
+ hint: 'Use key=value, key: value, or a single line like key1=value1&key2=value2.',
188
+ context: {
189
+ source: 'httpClient',
190
+ method: currentMethod,
191
+ url: currentUrl
192
+ }
193
+ });
194
+ }
195
+ data = encoded.encodedBody;
196
+ }
197
+ else if (contentTypeValue.includes('application/json')) {
198
+ try {
199
+ data = JSON.parse(currentBody);
200
+ }
201
+ catch {
202
+ data = currentBody;
203
+ }
204
+ }
205
+ else {
206
+ try {
207
+ data = JSON.parse(currentBody);
208
+ }
209
+ catch {
210
+ data = currentBody;
211
+ }
212
+ }
213
+ }
214
+ // Capture what we're actually sending (for debugging)
215
+ sentRequestBody = data;
216
+ sentRequestHeaders = { ...headers };
217
+ const response = await (0, axios_1.default)({
218
+ method: currentMethod,
219
+ url: currentUrl,
220
+ adapter: 'http',
221
+ headers,
222
+ data,
223
+ timeout: 30000,
224
+ maxRedirects: 0,
225
+ validateStatus: () => true,
226
+ httpsAgent: getHttpsAgent(),
227
+ });
228
+ // Store any Set-Cookie headers in the jar
229
+ const setCookieHeader = response.headers['set-cookie'];
230
+ if (setCookieHeader) {
231
+ const cookies = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
232
+ for (const cookieStr of cookies) {
233
+ try {
234
+ await jar.setCookie(cookieStr, currentUrl);
235
+ }
236
+ catch {
237
+ // Ignore invalid cookies
238
+ }
239
+ }
240
+ }
241
+ // Check if this is a redirect
242
+ if (response.status >= 300 && response.status < 400 && response.headers['location']) {
243
+ redirectCount++;
244
+ const location = response.headers['location'];
245
+ currentUrl = new URL(location, currentUrl).toString();
246
+ if (currentMethod !== 'GET' && currentMethod !== 'HEAD') {
247
+ currentMethod = 'GET';
248
+ currentBody = undefined;
249
+ }
250
+ continue;
251
+ }
252
+ finalResponse = response;
253
+ break;
254
+ }
255
+ if (!finalResponse) {
256
+ throw new Error('Too many redirects');
257
+ }
258
+ // Check if we should retry on this response status
259
+ if (shouldRetry(finalResponse, null) && attempt < totalAttempts) {
260
+ const waitMs = backoffMs * attempt; // Linear backoff
261
+ const errorMessage = `HTTP ${finalResponse.status} ${finalResponse.statusText}`;
262
+ retriedErrors.push(`Attempt ${attempt}: ${errorMessage}`);
263
+ // Call retry callback if provided
264
+ if (retryOptions?.onRetry) {
265
+ retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
266
+ }
267
+ await sleep(waitMs);
268
+ continue;
269
+ }
270
+ // Get ALL cookies in the jar
271
+ const cookies = await getAllCookies(jar);
272
+ const result = {
273
+ status: finalResponse.status,
274
+ statusText: finalResponse.statusText,
275
+ headers: finalResponse.headers,
276
+ body: finalResponse.data,
277
+ duration: Date.now() - startTime,
278
+ cookies,
279
+ requestBody: sentRequestBody,
280
+ requestHeaders: sentRequestHeaders,
281
+ };
282
+ // Add retry info if we made retries
283
+ if (attemptsMade > 1 || totalAttempts > 1) {
284
+ result.retryInfo = {
285
+ attemptsMade,
286
+ totalAttempts,
287
+ retriedErrors,
288
+ };
289
+ }
290
+ return result;
291
+ }
292
+ catch (error) {
293
+ const errorMessage = error.message || String(error);
294
+ const effectiveUrl = currentUrl || request.url;
295
+ const isUrlError = /invalid url/i.test(errorMessage) || /unsupported protocol/i.test(errorMessage);
296
+ const isAxiosNetworkError = Boolean(error?.isAxiosError) && !error?.response;
297
+ if (isUrlError) {
298
+ lastError = new nornError_1.NornError({
299
+ category: 'url',
300
+ code: 'http-invalid-url',
301
+ message: `Invalid request URL: ${effectiveUrl}`,
302
+ details: errorMessage,
303
+ hint: 'Check the URL and any variables used to build it before sending the request.',
304
+ context: {
305
+ source: 'httpClient',
306
+ method: currentMethod,
307
+ url: effectiveUrl,
308
+ },
309
+ cause: error,
310
+ });
311
+ }
312
+ else if (isAxiosNetworkError) {
313
+ lastError = new nornError_1.NornError({
314
+ category: 'network',
315
+ code: 'http-network-error',
316
+ message: `Network request failed.`,
317
+ details: [`${errorMessage}`, `URL used: ${effectiveUrl}`],
318
+ hint: 'Check connectivity, DNS/host availability, TLS settings, and the request URL.',
319
+ context: {
320
+ source: 'httpClient',
321
+ method: currentMethod,
322
+ url: effectiveUrl,
323
+ },
324
+ cause: error,
325
+ });
326
+ }
327
+ else {
328
+ lastError = new Error(`${errorMessage}\nURL used: ${effectiveUrl}`);
329
+ }
330
+ // If we have more attempts, wait and retry
331
+ if (attempt < totalAttempts) {
332
+ const waitMs = backoffMs * attempt; // Linear backoff
333
+ retriedErrors.push(`Attempt ${attempt}: ${errorMessage}`);
334
+ // Call retry callback if provided
335
+ if (retryOptions?.onRetry) {
336
+ retryOptions.onRetry(attempt, totalAttempts, errorMessage, waitMs);
337
+ }
338
+ await sleep(waitMs);
339
+ continue;
340
+ }
341
+ // No more retries, throw the error
342
+ throw lastError;
343
+ }
344
+ }
345
+ // Should never reach here, but TypeScript needs this
346
+ throw lastError || new Error('Request failed');
347
+ }
348
+ //# sourceMappingURL=httpClient.js.map
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * Shared runtime HTTP options for extension + CLI execution paths.
4
+ * Keep this module Node-only (no VS Code imports).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.setVerifyTlsCertificates = setVerifyTlsCertificates;
8
+ exports.getVerifyTlsCertificates = getVerifyTlsCertificates;
9
+ let verifyTlsCertificates = true;
10
+ function setVerifyTlsCertificates(enabled) {
11
+ verifyTlsCertificates = enabled;
12
+ }
13
+ function getVerifyTlsCertificates() {
14
+ return verifyTlsCertificates;
15
+ }
16
+ //# sourceMappingURL=httpRuntimeOptions.js.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isBlockingImportError = isBlockingImportError;
4
+ exports.getBlockingImportErrors = getBlockingImportErrors;
5
+ exports.splitImportResolutionErrors = splitImportResolutionErrors;
6
+ const BLOCKING_IMPORT_ERROR_MARKERS = [
7
+ 'Duplicate header group',
8
+ 'Duplicate endpoint',
9
+ 'Duplicate named request',
10
+ 'Duplicate sequence',
11
+ ];
12
+ function isBlockingImportError(error) {
13
+ return Boolean(error.blocking) || BLOCKING_IMPORT_ERROR_MARKERS.some(marker => error.error.includes(marker));
14
+ }
15
+ function getBlockingImportErrors(errors) {
16
+ return errors.filter(isBlockingImportError);
17
+ }
18
+ function splitImportResolutionErrors(errors) {
19
+ const blockingErrors = [];
20
+ const warningErrors = [];
21
+ for (const error of errors) {
22
+ if (isBlockingImportError(error)) {
23
+ blockingErrors.push(error);
24
+ }
25
+ else {
26
+ warningErrors.push(error);
27
+ }
28
+ }
29
+ return { blockingErrors, warningErrors };
30
+ }
31
+ //# sourceMappingURL=importErrors.js.map