@elisra-devops/docgen-data-provider 1.141.0 → 1.142.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 (63) hide show
  1. package/bin/helpers/requestContext.d.ts +51 -0
  2. package/bin/helpers/requestContext.js +218 -0
  3. package/bin/helpers/requestContext.js.map +1 -0
  4. package/bin/helpers/tfs.d.ts +5 -0
  5. package/bin/helpers/tfs.js +22 -29
  6. package/bin/helpers/tfs.js.map +1 -1
  7. package/bin/modules/GitDataProvider.js +3 -2
  8. package/bin/modules/GitDataProvider.js.map +1 -1
  9. package/bin/modules/JfrogDataProvider.js +2 -1
  10. package/bin/modules/JfrogDataProvider.js.map +1 -1
  11. package/bin/modules/PipelinesDataProvider.js +4 -3
  12. package/bin/modules/PipelinesDataProvider.js.map +1 -1
  13. package/bin/modules/ResultDataProvider.js +12 -11
  14. package/bin/modules/ResultDataProvider.js.map +1 -1
  15. package/bin/modules/TestDataProvider.js +4 -3
  16. package/bin/modules/TestDataProvider.js.map +1 -1
  17. package/bin/modules/TicketsDataProvider.js +13 -12
  18. package/bin/modules/TicketsDataProvider.js.map +1 -1
  19. package/bin/tests/helpers/requestContext.test.d.ts +1 -0
  20. package/bin/tests/helpers/requestContext.test.js +165 -0
  21. package/bin/tests/helpers/requestContext.test.js.map +1 -0
  22. package/bin/tests/helpers/tfs.test.js +54 -0
  23. package/bin/tests/helpers/tfs.test.js.map +1 -1
  24. package/bin/tests/modules/JfrogDataProvider.test.js +1 -1
  25. package/bin/tests/modules/JfrogDataProvider.test.js.map +1 -1
  26. package/bin/tests/modules/ResultDataProvider.test.js +4 -2
  27. package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
  28. package/bin/tests/modules/gitDataProvider.test.js +1 -1
  29. package/bin/tests/modules/gitDataProvider.test.js.map +1 -1
  30. package/bin/tests/modules/pipelineDataProvider.test.js +1 -1
  31. package/bin/tests/modules/pipelineDataProvider.test.js.map +1 -1
  32. package/bin/tests/modules/testDataProvider.test.js +1 -1
  33. package/bin/tests/modules/testDataProvider.test.js.map +1 -1
  34. package/bin/tests/modules/ticketsDataProvider.test.js +2 -2
  35. package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -1
  36. package/bin/tests/utils/logger.test.js +94 -0
  37. package/bin/tests/utils/logger.test.js.map +1 -1
  38. package/bin/utils/logSink.d.ts +14 -0
  39. package/bin/utils/logSink.js +12 -0
  40. package/bin/utils/logSink.js.map +1 -1
  41. package/bin/utils/logger.d.ts +1 -0
  42. package/bin/utils/logger.js +48 -1
  43. package/bin/utils/logger.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/helpers/requestContext.ts +222 -0
  46. package/src/helpers/tfs.ts +28 -26
  47. package/src/modules/GitDataProvider.ts +3 -2
  48. package/src/modules/JfrogDataProvider.ts +2 -1
  49. package/src/modules/PipelinesDataProvider.ts +4 -3
  50. package/src/modules/ResultDataProvider.ts +12 -11
  51. package/src/modules/TestDataProvider.ts +4 -3
  52. package/src/modules/TicketsDataProvider.ts +13 -12
  53. package/src/tests/helpers/requestContext.test.ts +202 -0
  54. package/src/tests/helpers/tfs.test.ts +86 -0
  55. package/src/tests/modules/JfrogDataProvider.test.ts +2 -1
  56. package/src/tests/modules/ResultDataProvider.test.ts +5 -2
  57. package/src/tests/modules/gitDataProvider.test.ts +2 -1
  58. package/src/tests/modules/pipelineDataProvider.test.ts +2 -1
  59. package/src/tests/modules/testDataProvider.test.ts +4 -1
  60. package/src/tests/modules/ticketsDataProvider.test.ts +2 -2
  61. package/src/tests/utils/logger.test.ts +111 -0
  62. package/src/utils/logSink.ts +18 -3
  63. package/src/utils/logger.ts +43 -3
@@ -1,3 +1,9 @@
1
+ export declare const CONTEXT_LIMITS: {
2
+ readonly method: 10;
3
+ readonly url: 1000;
4
+ readonly requestBody: 2000;
5
+ readonly responseExcerpt: 300;
6
+ };
1
7
  export interface DiagnosticEvent {
2
8
  ts: string;
3
9
  level: string;
@@ -16,6 +22,14 @@ export interface DiagnosticEvent {
16
22
  code?: string;
17
23
  stack?: string;
18
24
  };
25
+ context?: {
26
+ method?: string;
27
+ url?: string;
28
+ status?: number;
29
+ attempt?: number;
30
+ requestBody?: string;
31
+ responseExcerpt?: string;
32
+ };
19
33
  retainPending?: boolean;
20
34
  }
21
35
  export interface LogSink {
@@ -1,7 +1,19 @@
1
1
  'use strict';
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONTEXT_LIMITS = void 0;
3
4
  exports.getLogSink = getLogSink;
4
5
  exports.installLogSink = installLogSink;
6
+ // The one queryable-error-store event shape, emitted by DiagnosticsTransport (logger.ts).
7
+ // This package never installs a sink itself — it only reads whatever the host process
8
+ // (docgen-content-control's index.ts) installed. err stays narrow — {message, code, stack}.
9
+ // `context` is the one deliberate exception to "no extra-fields bucket": a sanitized
10
+ // description of the failed outbound request (method, url, status, attempt, request-body
11
+ // summary, response excerpt) — without it a 404 in the dashboard can't be traced to the call
12
+ // that produced it. A fixed, allowlisted shape, not a free-form bag.
13
+ // Bounds for DiagnosticEvent.context. Single source for this package (requestContext.ts and the
14
+ // logger transport); docgen-api-gate's DiagnosticsController.sanitizeContext re-validates at
15
+ // ingest with its own copy — keep the two in step.
16
+ exports.CONTEXT_LIMITS = { method: 10, url: 1000, requestBody: 2000, responseExcerpt: 300 };
5
17
  // Symbol.for so this package's copy converges on the same installed sink as
6
18
  // docgen-content-control's (the host process this package runs inside) and
7
19
  // docgen-dg-skins-package's, the same reasoning as runContext.ts's AsyncLocalStorage. A
@@ -1 +1 @@
1
- {"version":3,"file":"logSink.js","sourceRoot":"","sources":["../../src/utils/logSink.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAqCb,gCAEC;AAED,wCAEC;AAZD,4EAA4E;AAC5E,2EAA2E;AAC3E,wFAAwF;AACxF,6FAA6F;AAC7F,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAEtD,SAAgB,UAAU;IACxB,OAAQ,UAAsC,CAAC,GAAG,CAAwB,CAAC;AAC7E,CAAC;AAED,SAAgB,cAAc,CAAC,IAAa;IACzC,UAAsC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"logSink.js","sourceRoot":"","sources":["../../src/utils/logSink.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;AAoDb,gCAEC;AAED,wCAEC;AAxDD,0FAA0F;AAC1F,sFAAsF;AACtF,4FAA4F;AAC5F,qFAAqF;AACrF,yFAAyF;AACzF,6FAA6F;AAC7F,qEAAqE;AACrE,gGAAgG;AAChG,6FAA6F;AAC7F,mDAAmD;AACtC,QAAA,cAAc,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAW,CAAC;AAkC1G,4EAA4E;AAC5E,2EAA2E;AAC3E,wFAAwF;AACxF,6FAA6F;AAC7F,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAEtD,SAAgB,UAAU;IACxB,OAAQ,UAAsC,CAAC,GAAG,CAAwB,CAAC;AAC7E,CAAC;AAED,SAAgB,cAAc,CAAC,IAAa;IACzC,UAAsC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;AACtD,CAAC"}
@@ -2,6 +2,7 @@ import * as winston from 'winston';
2
2
  import * as Transport from 'winston-transport';
3
3
  declare let logger: winston.Logger;
4
4
  export declare const withRunContext: winston.Logform.FormatWrap;
5
+ export declare function redactValue(value: unknown, depth?: number, seen?: WeakSet<object>): unknown;
5
6
  export declare const redact: winston.Logform.FormatWrap;
6
7
  export declare class DiagnosticsTransport extends Transport {
7
8
  log(info: Record<string, unknown>, callback: () => void): void;
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DiagnosticsTransport = exports.redact = exports.withRunContext = void 0;
4
+ exports.redactValue = redactValue;
4
5
  const winston = require("winston");
5
6
  const fs = require("fs");
6
7
  const path = require("path");
@@ -122,7 +123,26 @@ function readOwnVersion() {
122
123
  // crash inside this formatter itself, the one place in the pipeline with no try/catch around
123
124
  // it. Symbol.toString() itself never throws, so this needs no further guarding.
124
125
  const safeMessageString = (value) => typeof value === 'symbol' ? value.toString() : String(value);
125
- const textFormat = winston.format.printf((info) => `${info.timestamp} - ${info.level}: ${safeMessageString(info.message)}`);
126
+ // A failed ADO request's description (helpers/requestContext.ts), attached to the thrown error
127
+ // and merged onto the record by winston. In text mode — the default, which prints only the
128
+ // message — this is the only way the URL reaches stdout for a record whose own message doesn't
129
+ // carry it. Never throws: this runs inside the formatter, which has no try/catch around it.
130
+ const requestSuffix = (ctx) => {
131
+ try {
132
+ if (!ctx || typeof ctx !== 'object')
133
+ return '';
134
+ const c = ctx;
135
+ if (typeof c.url !== 'string')
136
+ return '';
137
+ const method = typeof c.method === 'string' ? `${c.method} ` : '';
138
+ const status = typeof c.status === 'number' ? ` -> ${c.status}` : '';
139
+ return ` [${method}${c.url}${status}]`;
140
+ }
141
+ catch (_a) {
142
+ return '';
143
+ }
144
+ };
145
+ const textFormat = winston.format.printf((info) => `${info.timestamp} - ${info.level}: ${safeMessageString(info.message)}${requestSuffix(info.adoRequest)}`);
126
146
  // Bounded so one oversized message/stack can't produce an unbounded LogEvent document.
127
147
  const MAX_MESSAGE_LEN = 2000;
128
148
  const MAX_STACK_LEN = 4000;
@@ -131,6 +151,32 @@ function clamp(value, max) {
131
151
  return undefined;
132
152
  return value.length > max ? value.slice(0, max) : value;
133
153
  }
154
+ // Re-validates the request description at the transport instead of trusting its shape — it
155
+ // arrives via an Error's own properties, which anything upstream could have set. Allowlisted
156
+ // keys only, each type-checked and bounded.
157
+ function pickContext(raw) {
158
+ if (!raw || typeof raw !== 'object')
159
+ return undefined;
160
+ const c = raw;
161
+ const context = {};
162
+ const method = clamp(c.method, logSink_1.CONTEXT_LIMITS.method);
163
+ const url = clamp(c.url, logSink_1.CONTEXT_LIMITS.url);
164
+ const requestBody = clamp(c.requestBody, logSink_1.CONTEXT_LIMITS.requestBody);
165
+ const responseExcerpt = clamp(c.responseExcerpt, logSink_1.CONTEXT_LIMITS.responseExcerpt);
166
+ if (method)
167
+ context.method = method;
168
+ if (url)
169
+ context.url = url;
170
+ if (requestBody)
171
+ context.requestBody = requestBody;
172
+ if (responseExcerpt)
173
+ context.responseExcerpt = responseExcerpt;
174
+ if (typeof c.status === 'number' && Number.isFinite(c.status))
175
+ context.status = c.status;
176
+ if (typeof c.attempt === 'number' && Number.isFinite(c.attempt))
177
+ context.attempt = c.attempt;
178
+ return Object.keys(context).length ? context : undefined;
179
+ }
134
180
  // Ships warn/error records to whatever LogSink the host process (docgen-content-control's
135
181
  // index.ts) has installed — this package never installs one itself, only reads it. This is
136
182
  // the dominant source of real ADO error records (tfs.ts's executeWithRetry is the single
@@ -187,6 +233,7 @@ class DiagnosticsTransport extends Transport {
187
233
  stack: clamp(info.stack, MAX_STACK_LEN),
188
234
  }
189
235
  : undefined,
236
+ context: pickContext(info.adoRequest),
190
237
  retainPending: !isWarnOrError && captureMode === 'retain-on-failure' ? true : undefined,
191
238
  };
192
239
  (_e = (0, logSink_1.getLogSink)()) === null || _e === void 0 ? void 0 : _e.push(event);
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elisra-devops/docgen-data-provider",
3
- "version": "1.141.0",
3
+ "version": "1.142.0",
4
4
  "description": "A document generator data provider, aimed to retrive data from azure devops",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,222 @@
1
+ import { redactValue } from '../utils/logger';
2
+ import { CONTEXT_LIMITS } from '../utils/logSink';
3
+
4
+ // What a failed Azure DevOps request looks like to whoever is triaging it. Built once, at the
5
+ // TFSServices choke point, and attached to the thrown error — so every caller that logs that
6
+ // error (however it words its own message) carries the same sanitized request description
7
+ // into the log record and the diagnostics store, instead of only the call sites that happened
8
+ // to print the URL themselves.
9
+ export interface AdoRequestContext {
10
+ method?: string;
11
+ url?: string;
12
+ status?: number;
13
+ attempt?: number;
14
+ requestBody?: string;
15
+ responseExcerpt?: string;
16
+ }
17
+
18
+ const MAX_URL_LEN = CONTEXT_LIMITS.url;
19
+ const MAX_BODY_LEN = CONTEXT_LIMITS.requestBody;
20
+ const MAX_STRING_VALUE_LEN = 500;
21
+ const MAX_ARRAY_ITEMS = 50;
22
+ const MAX_PATCH_OPS = 20;
23
+ const MAX_RESPONSE_LEN = CONTEXT_LIMITS.responseExcerpt;
24
+ const MAX_TEXT_RESPONSE_LEN = 200;
25
+
26
+ // Exact names, not a substring match — "key" must not mask a legitimate "keywords" parameter.
27
+ const SENSITIVE_PARAM = /^(access_?token|token|pat|password|pwd|secret|sig|signature|api_?key|key|authorization|auth)$/i;
28
+
29
+ const truncate = (text: string, max: number): string => (text.length > max ? `${text.slice(0, max)}…` : text);
30
+
31
+ /** Strips credentials embedded in the URL and masks sensitive query parameter values. */
32
+ export function sanitizeUrl(raw: unknown): string | undefined {
33
+ if (typeof raw !== 'string' || !raw) return undefined;
34
+ try {
35
+ const parsed = new URL(raw);
36
+ parsed.username = '';
37
+ parsed.password = '';
38
+ // Rewrite only the sensitive values in the raw query string — re-serializing through
39
+ // URLSearchParams would re-encode every other parameter ($depth, %20, +).
40
+ if (parsed.search) {
41
+ parsed.search = parsed.search
42
+ .slice(1)
43
+ .split('&')
44
+ .map((pair) => {
45
+ const eq = pair.indexOf('=');
46
+ if (eq < 0) return pair;
47
+ let name = pair.slice(0, eq);
48
+ try {
49
+ name = decodeURIComponent(name);
50
+ } catch {
51
+ // keep the raw name
52
+ }
53
+ return SENSITIVE_PARAM.test(name) ? `${pair.slice(0, eq)}=[REDACTED]` : pair;
54
+ })
55
+ .join('&');
56
+ }
57
+ return truncate(parsed.toString(), MAX_URL_LEN);
58
+ } catch {
59
+ return truncate(raw, MAX_URL_LEN);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Replaces each single-quoted WIQL literal with its length — the query structure (fields,
65
+ * operators, numbers, @macros) stays readable, free-text values never reach the logs.
66
+ * `''` is WIQL's escaped quote.
67
+ */
68
+ export function maskWiqlLiterals(text: string): string {
69
+ return text.replace(/'(?:[^']|'')*'/g, (m) => `'[${m.length - 2} chars]'`);
70
+ }
71
+
72
+ function isBinaryLike(data: unknown): boolean {
73
+ return (
74
+ (typeof Buffer !== 'undefined' && Buffer.isBuffer(data)) ||
75
+ data instanceof ArrayBuffer ||
76
+ ArrayBuffer.isView(data as ArrayBufferView)
77
+ );
78
+ }
79
+
80
+ function isJsonPatch(data: unknown): data is Array<{ op: string; path: string }> {
81
+ return (
82
+ Array.isArray(data) &&
83
+ data.length > 0 &&
84
+ data.every(
85
+ (item) =>
86
+ item &&
87
+ typeof item === 'object' &&
88
+ typeof (item as Record<string, unknown>).op === 'string' &&
89
+ typeof (item as Record<string, unknown>).path === 'string'
90
+ )
91
+ );
92
+ }
93
+
94
+ function clampValues(value: unknown, depth = 0): unknown {
95
+ if (typeof value === 'string') return truncate(value, MAX_STRING_VALUE_LEN);
96
+ if (depth > 6 || value === null || typeof value !== 'object') return value;
97
+ if (Array.isArray(value)) {
98
+ const items = value.slice(0, MAX_ARRAY_ITEMS).map((v) => clampValues(v, depth + 1));
99
+ return value.length > MAX_ARRAY_ITEMS ? [...items, `…(+${value.length - MAX_ARRAY_ITEMS} more)`] : items;
100
+ }
101
+ const out: Record<string, unknown> = {};
102
+ for (const key of Object.keys(value as Record<string, unknown>)) {
103
+ out[key] = clampValues((value as Record<string, unknown>)[key], depth + 1);
104
+ }
105
+ return out;
106
+ }
107
+
108
+ /**
109
+ * A short, safe description of a request body — enough to see *what was asked* (the WIQL
110
+ * text, the id list) without ever carrying credentials or work-item content:
111
+ * - GET/HEAD never have one.
112
+ * - JSON-Patch arrays (work item / test case updates) keep only `op` + `path`; the `value`s
113
+ * are real field content and are dropped.
114
+ * - a WIQL `query` keeps its structure but its quoted literals are masked.
115
+ * - other JSON goes through redactValue (credential-looking keys scrubbed at any depth),
116
+ * with every string and the total length bounded.
117
+ * - anything that isn't plain JSON is described by type and size only.
118
+ */
119
+ export function summarizeBody(data: unknown, method?: string): string | undefined {
120
+ const verb = String(method || '').toUpperCase();
121
+ if (verb === 'GET' || verb === 'HEAD') return undefined;
122
+ if (data === undefined || data === null || data === '') return undefined;
123
+
124
+ if (typeof data === 'string') {
125
+ try {
126
+ const parsed = JSON.parse(data);
127
+ return typeof parsed === 'string' ? `[text, ${data.length} chars]` : summarizeBody(parsed, method);
128
+ } catch {
129
+ return `[text, ${data.length} chars]`;
130
+ }
131
+ }
132
+ if (typeof data !== 'object') return `[${typeof data}]`;
133
+ if (isBinaryLike(data)) {
134
+ const size = (data as { byteLength?: number }).byteLength;
135
+ return `[binary${typeof size === 'number' ? `, ${size} bytes` : ''}]`;
136
+ }
137
+ if (typeof (data as { pipe?: unknown }).pipe === 'function') return '[stream]';
138
+ if (typeof FormData !== 'undefined' && data instanceof FormData) return '[form-data]';
139
+
140
+ if (isJsonPatch(data)) {
141
+ const ops = data.slice(0, MAX_PATCH_OPS).map((o) => `${o.op} ${o.path}`);
142
+ const more = data.length > MAX_PATCH_OPS ? ` …(+${data.length - MAX_PATCH_OPS} more)` : '';
143
+ return truncate(`json-patch (${data.length} ops, values omitted): ${ops.join('; ')}${more}`, MAX_BODY_LEN);
144
+ }
145
+ if (Array.isArray(data) ? data.length === 0 : Object.keys(data).length === 0) return undefined;
146
+
147
+ try {
148
+ const body =
149
+ !Array.isArray(data) && typeof (data as { query?: unknown }).query === 'string'
150
+ ? { ...(data as Record<string, unknown>), query: maskWiqlLiterals((data as { query: string }).query) }
151
+ : data;
152
+ return truncate(JSON.stringify(clampValues(redactValue(body))), MAX_BODY_LEN);
153
+ } catch {
154
+ return '[unserializable body]';
155
+ }
156
+ }
157
+
158
+ /** The server's own explanation of the failure (ADO puts it in `message`), bounded. */
159
+ export function summarizeResponse(data: unknown): string | undefined {
160
+ if (data === undefined || data === null) return undefined;
161
+ if (typeof data === 'string') return truncate(data, MAX_TEXT_RESPONSE_LEN);
162
+ if (isBinaryLike(data)) return undefined;
163
+ if (typeof (data as { pipe?: unknown }).pipe === 'function') return '[stream]';
164
+ try {
165
+ const message = (data as { message?: unknown }).message;
166
+ if (typeof message === 'string') return truncate(message, MAX_RESPONSE_LEN);
167
+ return truncate(JSON.stringify(redactValue(data)), MAX_RESPONSE_LEN);
168
+ } catch {
169
+ return '[unserializable response data]';
170
+ }
171
+ }
172
+
173
+ export function buildAdoRequestContext(
174
+ error: any,
175
+ request: { method?: string; url?: string; data?: unknown },
176
+ attempt?: number
177
+ ): AdoRequestContext {
178
+ const method = String(request.method || error?.config?.method || 'get').toUpperCase();
179
+ const context: AdoRequestContext = { method, url: sanitizeUrl(request.url ?? error?.config?.url) };
180
+ const status = Number(error?.response?.status);
181
+ if (Number.isFinite(status) && status > 0) context.status = status;
182
+ if (attempt !== undefined) context.attempt = attempt;
183
+ const requestBody = summarizeBody(request.data, method);
184
+ if (requestBody) context.requestBody = requestBody;
185
+ const responseExcerpt = summarizeResponse(error?.response?.data);
186
+ if (responseExcerpt) context.responseExcerpt = responseExcerpt;
187
+ return context;
188
+ }
189
+
190
+ /**
191
+ * Attaches the context as an enumerable own property: winston's errors() / log() merge an
192
+ * Error's own enumerable properties onto the log record, which is how the transport and the
193
+ * text formatter find it without every call site having to pass it along.
194
+ */
195
+ export function annotateAdoError<T>(error: T, context: AdoRequestContext): T {
196
+ if (error && typeof error === 'object') {
197
+ try {
198
+ Object.defineProperty(error, 'adoRequest', { value: context, enumerable: true, configurable: true, writable: true });
199
+ } catch {
200
+ // A frozen/exotic error object just goes unannotated — never worth failing the request path.
201
+ }
202
+ }
203
+ return error;
204
+ }
205
+
206
+ /**
207
+ * The log metadata for a caught error: message, stack, code, and the request description when
208
+ * the error came through TFSServices. Deliberately NOT the error object itself — passing a
209
+ * whole AxiosError would merge its entire config/request/response graph onto the record.
210
+ * winston concatenates meta.message onto the call's own message, so
211
+ * `logger.error('Could not fetch X:', describeError(err))` reads the same as the old
212
+ * `logger.error(\`Could not fetch X: ${err.message}\`)` — plus the request, which a message
213
+ * string can't carry.
214
+ */
215
+ export function describeError(error: unknown): Record<string, unknown> {
216
+ if (!error || typeof error !== 'object') return { message: String(error) };
217
+ const e = error as Record<string, unknown>;
218
+ const meta: Record<string, unknown> = { message: e.message, stack: e.stack };
219
+ if (typeof e.code === 'string') meta.code = e.code;
220
+ if (e.adoRequest) meta.adoRequest = e.adoRequest;
221
+ return meta;
222
+ }
@@ -1,5 +1,6 @@
1
1
  import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
2
2
  import logger from '../utils/logger';
3
+ import { AdoRequestContext, annotateAdoError, buildAdoRequestContext } from './requestContext';
3
4
 
4
5
  // Environment detection
5
6
  const isNode = typeof window === 'undefined' && typeof process !== 'undefined' && process.versions?.node;
@@ -83,7 +84,7 @@ export class TFSServices {
83
84
  return res;
84
85
  } catch (e) {
85
86
  logger.error(`error download zip file , url : ${url}`);
86
- throw new Error(String(e));
87
+ throw annotateAdoError(new Error(String(e)), buildAdoRequestContext(e, { method: 'GET', url }));
87
88
  }
88
89
  }
89
90
 
@@ -178,7 +179,7 @@ export class TFSServices {
178
179
  const result = await this.axiosInstance.request(config);
179
180
  return result.data;
180
181
  } catch (e: any) {
181
- this.logDetailedError(e, url);
182
+ this.reportFailure(e, { method: 'GET', url });
182
183
  throw e;
183
184
  }
184
185
  }
@@ -203,7 +204,7 @@ export class TFSServices {
203
204
  const result = await this.axiosInstance.request(config);
204
205
  return result;
205
206
  } catch (e: any) {
206
- this.logDetailedError(e, url);
207
+ this.reportFailure(e, { method: requestMethod, url, data });
207
208
  throw e;
208
209
  }
209
210
  }
@@ -246,10 +247,9 @@ export class TFSServices {
246
247
  continue;
247
248
  }
248
249
 
249
- // Log error if needed
250
- if (printError) {
251
- this.logDetailedError(e, url, attempts);
252
- }
250
+ // Always annotate (so any caller that logs this error — however it words its own
251
+ // message — carries the request), log the dedicated record only when asked to.
252
+ this.reportFailure(e, { method: config.method, url, data: config.data }, attempts, printError);
253
253
 
254
254
  throw e;
255
255
  }
@@ -284,28 +284,30 @@ export class TFSServices {
284
284
  }
285
285
 
286
286
  /**
287
- * Log detailed error information
287
+ * Describes the failed request once, attaches it to the error, and (optionally) emits the one
288
+ * dedicated "ADO request failed" record.
288
289
  */
289
- private static logDetailedError(error: any, url: string, attempt?: number): void {
290
- let responseExcerpt: string | undefined;
291
- if (error.response?.data) {
292
- if (typeof error.response.data === 'string') {
293
- responseExcerpt = error.response.data.substring(0, 200);
294
- } else {
295
- try {
296
- responseExcerpt = error.response.data.message || JSON.stringify(error.response.data).substring(0, 200);
297
- } catch {
298
- responseExcerpt = '[unserializable response data]';
299
- }
300
- }
290
+ private static reportFailure(
291
+ error: any,
292
+ request: { method?: string; url: string; data?: unknown },
293
+ attempt?: number,
294
+ print: boolean = true
295
+ ): void {
296
+ const context = buildAdoRequestContext(error, request, attempt);
297
+ annotateAdoError(error, context);
298
+ if (print) {
299
+ this.logDetailedError(error, context);
301
300
  }
301
+ }
302
+
303
+ /**
304
+ * Log detailed error information
305
+ */
306
+ private static logDetailedError(error: any, context: AdoRequestContext): void {
302
307
  logger.error('ADO request failed', {
303
- message: error.message,
304
- stack: error.stack,
305
- url,
306
- status: error.response?.status,
307
- responseExcerpt,
308
- attempt,
308
+ message: error?.message,
309
+ stack: error?.stack,
310
+ adoRequest: context,
309
311
  });
310
312
  }
311
313
 
@@ -1,6 +1,7 @@
1
1
  import { TFSServices } from '../helpers/tfs';
2
2
  import TicketsDataProvider from './TicketsDataProvider';
3
3
  import logger from '../utils/logger';
4
+ import { describeError } from '../helpers/requestContext';
4
5
  import {
5
6
  createLinkedRelation,
6
7
  createRequirementRelation,
@@ -124,7 +125,7 @@ export default class GitDataProvider {
124
125
  }
125
126
  return undefined;
126
127
  } catch (err: any) {
127
- logger.warn(`File ${fileName} could not be read: ${err.message}`);
128
+ logger.warn(`File ${fileName} could not be read:`, describeError(err));
128
129
  return undefined;
129
130
  }
130
131
  }
@@ -1119,7 +1120,7 @@ export default class GitDataProvider {
1119
1120
  submodules.push(subModule);
1120
1121
  }
1121
1122
  } catch (error: any) {
1122
- logger.error(`Error in getSubmodulesData: ${error.message}`);
1123
+ logger.error('Error in getSubmodulesData:', describeError(error));
1123
1124
  } finally {
1124
1125
  return submodules;
1125
1126
  }
@@ -1,6 +1,7 @@
1
1
  import { TFSServices } from '../helpers/tfs';
2
2
 
3
3
  import logger from '../utils/logger';
4
+ import { describeError } from '../helpers/requestContext';
4
5
 
5
6
  export default class JfrogDataProvider {
6
7
  orgUrl: string = '';
@@ -56,7 +57,7 @@ export default class JfrogDataProvider {
56
57
  logger.debug(`CI Url from JFROG: ${getCiResponse.buildInfo.url}`);
57
58
  return getCiResponse.buildInfo.url;
58
59
  } catch (err: any) {
59
- logger.error(`Error occurred during querying JFrog using: ${err.message}`);
60
+ logger.error('Error occurred during querying JFrog using:', describeError(err));
60
61
  throw err;
61
62
  }
62
63
  }
@@ -2,6 +2,7 @@ import { PipelineRun, Repository, ResourceRepository } from '../models/tfs-data'
2
2
  import { TFSServices } from '../helpers/tfs';
3
3
 
4
4
  import logger from '../utils/logger';
5
+ import { describeError } from '../helpers/requestContext';
5
6
  import GitDataProvider from './GitDataProvider';
6
7
  const pLimit = require('p-limit');
7
8
  const MAX_DISCOVERY_PAGES = 50;
@@ -1331,7 +1332,7 @@ export default class PipelinesDataProvider {
1331
1332
  }
1332
1333
  return res;
1333
1334
  } catch (err: any) {
1334
- logger.error(`Could not fetch Pipeline Run History: ${err.message}`);
1335
+ logger.error('Could not fetch Pipeline Run History:', describeError(err));
1335
1336
  }
1336
1337
  }
1337
1338
 
@@ -1401,7 +1402,7 @@ export default class PipelinesDataProvider {
1401
1402
  // Azure DevOps returns continuation token header for next page
1402
1403
  continuationToken = this.getContinuationToken(headers);
1403
1404
  } catch (err: any) {
1404
- logger.error(`GetAllReleaseHistory failed: ${err.message}`);
1405
+ logger.error('GetAllReleaseHistory failed:', describeError(err));
1405
1406
  throw err;
1406
1407
  }
1407
1408
  } while (continuationToken);
@@ -1463,7 +1464,7 @@ export default class PipelinesDataProvider {
1463
1464
  }
1464
1465
  }
1465
1466
  } catch (err: any) {
1466
- logger.error(`Error fetching pipeline ${pipelineRunId} with url ${url} : ${err.message}`);
1467
+ logger.error(`Error fetching pipeline ${pipelineRunId} with url ${url} :`, describeError(err));
1467
1468
  return undefined;
1468
1469
  }
1469
1470
  }