@adobe/spacecat-shared-utils 1.34.0 → 1.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [@adobe/spacecat-shared-utils-v1.35.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.34.0...@adobe/spacecat-shared-utils-v1.35.0) (2025-03-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * tracing fetch timeout ([#657](https://github.com/adobe/spacecat-shared/issues/657)) ([67335e3](https://github.com/adobe/spacecat-shared/commit/67335e32b7cceeb500ca58dc05518876589a108a))
7
+
1
8
  # [@adobe/spacecat-shared-utils-v1.34.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.33.3...@adobe/spacecat-shared-utils-v1.34.0) (2025-03-05)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-utils",
3
- "version": "1.34.0",
3
+ "version": "1.35.0",
4
4
  "description": "Shared modules of the Spacecat Services - utils",
5
5
  "type": "module",
6
6
  "engines": {
@@ -9,7 +9,7 @@
9
9
  * OF ANY KIND, either express or implied. See the License for the specific language
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
- import { Request } from '@adobe/fetch';
12
+ import { Request, timeoutSignal, AbortError } from '@adobe/fetch';
13
13
  import AWSXRay from 'aws-xray-sdk';
14
14
 
15
15
  import { fetch as adobeFetch } from './adobe-fetch.js';
@@ -107,17 +107,60 @@ const handleSubSegmentError = (subSegment, request, error) => {
107
107
  subSegment.close(error);
108
108
  };
109
109
 
110
+ /**
111
+ * Creates a timeout error with a consistent format
112
+ * @param {number} timeout - The timeout value in milliseconds
113
+ * @returns {Error} A formatted timeout error
114
+ */
115
+ const createTimeoutError = (timeout) => {
116
+ const timeoutError = new Error(`Request timeout after ${timeout}ms`);
117
+ timeoutError.code = 'ETIMEOUT';
118
+ return timeoutError;
119
+ };
120
+
121
+ /**
122
+ * Performs a fetch request with timeout handling
123
+ * @param {string|Request} resource - The resource to fetch
124
+ * @param {Object} options - Options for the fetch call
125
+ * @param {Object} signal - The timeout signal
126
+ * @returns {Promise<Response>} The fetch response
127
+ */
128
+ const fetchWithTimeout = async (resource, options, signal) => {
129
+ try {
130
+ const fetchOptions = { ...options, signal };
131
+ return await adobeFetch(resource, fetchOptions);
132
+ } catch (error) {
133
+ if (error instanceof AbortError) {
134
+ // Extract timeout from signal (implementation detail, but necessary)
135
+ // eslint-disable-next-line no-underscore-dangle
136
+ const timeout = signal._ms || 10000;
137
+ throw createTimeoutError(timeout);
138
+ }
139
+ throw error;
140
+ } finally {
141
+ signal.clear(); // Clean up the signal
142
+ }
143
+ };
144
+
110
145
  /**
111
146
  * Performs a fetch request and adds AWS X-Ray tracing, including request/response tracking.
112
147
  * @param {string} url - The URL for the request.
113
- * @param {Object} options - Options to be passed to the fetch call.
148
+ * @param {Object} [options] - Optional options to be passed to the fetch call.
149
+ * @param {number} [options.timeout=10000] - Timeout in milliseconds (defaults to 10 seconds).
114
150
  * @returns {Promise<Response>} The response from the fetch request.
115
151
  */
116
152
  export async function tracingFetch(url, options) {
117
153
  const parentSegment = AWSXRay.getSegment();
118
154
 
119
- options = isObject(options) ? options : {};
120
- options.headers = isObject(options.headers) ? options.headers : new Headers();
155
+ options = isObject(options) ? { ...options } : {};
156
+ options.headers = isObject(options.headers) ? options.headers : { };
157
+
158
+ // Set default timeout of 10 seconds if not specified
159
+ const timeout = options.timeout || 10000;
160
+ delete options.timeout; // Remove from options as we'll handle it separately
161
+
162
+ // Create a timeout signal
163
+ const signal = timeoutSignal(timeout);
121
164
 
122
165
  // find user-agent header in headers case insensitively
123
166
  let hasUserAgent = false;
@@ -131,10 +174,12 @@ export async function tracingFetch(url, options) {
131
174
  options.headers['User-Agent'] = SPACECAT_USER_AGENT;
132
175
  }
133
176
 
177
+ // If no parent segment, perform fetch without tracing
134
178
  if (!parentSegment) {
135
- return adobeFetch(url, options);
179
+ return fetchWithTimeout(url, options, signal);
136
180
  }
137
181
 
182
+ // With parent segment, create subsegment and add tracing
138
183
  const request = new Request(url, options);
139
184
  const { hostname } = new URL(request.url);
140
185
  const subSegment = createSubsegment(parentSegment, hostname);
@@ -145,21 +190,22 @@ export async function tracingFetch(url, options) {
145
190
  setTraceHeaders(request, parentSegment, subSegment);
146
191
  }
147
192
 
148
- const capturedAdobeFetch = async () => {
149
- let response = null;
150
- try {
151
- response = await adobeFetch(request);
152
- } catch (e) {
153
- handleSubSegmentError(subSegment, request, e);
154
- throw e;
155
- }
193
+ subSegment.addAnnotation('timeout_ms', timeout);
156
194
 
157
- setSubSegmentFlagsByStatusCode(subSegment, response.status);
195
+ try {
196
+ // Create a new request with the signal
197
+ const requestWithSignal = new Request(request, { signal });
198
+
199
+ // Use the same fetchWithTimeout function but catch errors to handle subsegment
200
+ const response = await fetchWithTimeout(requestWithSignal, { }, signal);
158
201
 
202
+ setSubSegmentFlagsByStatusCode(subSegment, response.status);
159
203
  addFetchRequestDataToSegment(subSegment, request, response);
160
204
  subSegment.close();
161
- return response;
162
- };
163
205
 
164
- return capturedAdobeFetch();
206
+ return response;
207
+ } catch (error) {
208
+ handleSubSegmentError(subSegment, request, error);
209
+ throw error;
210
+ }
165
211
  }