File size: 2,382 Bytes
9e4583c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
 * Request ID — Correlation ID Middleware (T-23)
 *
 * Generates and propagates `x-request-id` headers for
 * request tracing across the proxy pipeline.
 *
 * Uses AsyncLocalStorage to make request ID available
 * anywhere in the call stack without explicit passing.
 *
 * @module shared/utils/requestId
 */

import { AsyncLocalStorage } from "node:async_hooks";
import { randomUUID } from "node:crypto";

const requestIdStore = new AsyncLocalStorage();

/**
 * Get the current request ID from the async context.
 * Returns null if not inside a request context.
 *
 * @returns {string | null}
 */
export function getRequestId() {
  return requestIdStore.getStore() || null;
}

/**
 * Run a handler with a request ID in the async context.
 * If the incoming request has an `x-request-id` header, it is reused;
 * otherwise a new UUID v4 is generated.
 *
 * @template T
 * @param {Request} request - Incoming request
 * @param {() => T | Promise<T>} handler - Handler to execute
 * @returns {Promise<T>}
 */
export async function withRequestId(request, handler) {
  const existingId = request?.headers?.get?.("x-request-id");
  const requestId = existingId || randomUUID();
  return requestIdStore.run(requestId, handler);
}

/**
 * Create headers object with the current request ID.
 * Useful for outgoing provider requests.
 *
 * @param {Record<string, string>} [headers={}] - Existing headers
 * @returns {Record<string, string>} Headers with x-request-id added
 */
export function addRequestIdHeader(headers = {}) {
  const requestId = getRequestId();
  if (requestId) {
    return { ...headers, "x-request-id": requestId };
  }
  return headers;
}

/**
 * Next.js middleware-compatible wrapper.
 * Attaches `x-request-id` to response headers.
 *
 * @param {Request} request
 * @param {Response} response
 * @returns {Response} Response with request ID header
 */
export function attachRequestIdToResponse(request, response) {
  const requestId = getRequestId() || request?.headers?.get?.("x-request-id") || randomUUID();

  const headers = new Headers(response.headers);
  headers.set("x-request-id", requestId);

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers,
  });
}

/**
 * Generate a new request ID (UUID v4).
 * @returns {string}
 */
export function generateRequestId() {
  return randomUUID();
}