@hypequery/serve 0.13.6 → 0.14.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.
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
package/dist/pipeline.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { warnTenantMisconfiguration } from './tenant.js';
3
3
  import { applySemanticTenantRuntime } from './semantic/utils/tenant-runtime.js';
4
- import { generateRequestId } from './utils.js';
4
+ import { generateRequestId, validateCorrelationId } from './utils.js';
5
5
  import { buildOpenApiDocument } from './openapi.js';
6
6
  import { buildDocsHtml } from './docs-ui.js';
7
7
  import { ServeHttpError } from './errors.js';
@@ -134,12 +134,37 @@ const resolveContext = async (factory, request, auth) => {
134
134
  }
135
135
  return cloneContext(factory);
136
136
  };
137
- const resolveRequestId = (request, provided) => provided ?? request.headers['x-request-id'] ?? request.headers['x-trace-id'] ?? generateRequestId();
137
+ /**
138
+ * Resolve the authoritative request id and the optional external correlation id.
139
+ *
140
+ * The authoritative id is always server-generated unless a trusted in-process caller passes
141
+ * one explicitly; a network-supplied `x-request-id`/`x-trace-id` is NEVER treated as
142
+ * authoritative. Any caller-supplied value is validated and length-bounded and surfaced only
143
+ * as a separate correlation id, so it cannot inject control characters into logs/headers or
144
+ * spoof cross-request correlation.
145
+ */
146
+ const resolveRequestContext = (request, provided) => ({
147
+ requestId: validateCorrelationId(provided) ?? generateRequestId(),
148
+ correlationId: validateCorrelationId(request.headers['x-request-id']) ??
149
+ validateCorrelationId(request.headers['x-trace-id']),
150
+ });
151
+ const withManagedTraceHeaders = (headers, requestId, correlationId) => ({
152
+ ...Object.fromEntries(Object.entries(headers ?? {}).filter(([key]) => {
153
+ const normalized = key.toLowerCase();
154
+ return normalized !== 'x-request-id' && normalized !== 'x-correlation-id';
155
+ })),
156
+ 'x-request-id': requestId,
157
+ ...(correlationId ? { 'x-correlation-id': correlationId } : {}),
158
+ });
138
159
  export const executeEndpoint = async (options) => {
139
160
  const { endpoint, request, requestId: explicitRequestId, authStrategies, contextFactory, globalMiddlewares, tenantConfig, hooks = {}, queryLogger, additionalContext, verboseAuthErrors = false, // Default to secure mode for production safety
140
161
  sanitizeErrors = true, // Default to secure mode for HTTP requests
141
162
  } = options;
142
- const requestId = resolveRequestId(request, explicitRequestId);
163
+ const { requestId, correlationId } = resolveRequestContext(request, explicitRequestId);
164
+ // Echoed alongside the authoritative x-request-id; only ever a validated caller value.
165
+ const traceHeaders = correlationId
166
+ ? { 'x-correlation-id': correlationId }
167
+ : {};
143
168
  const locals = {};
144
169
  let cacheTtlMs = endpoint.cacheTtlMs ?? null;
145
170
  const setCacheTtl = (ttl) => {
@@ -206,7 +231,7 @@ export const executeEndpoint = async (options) => {
206
231
  ...(verboseAuthErrors && { strategies_attempted: strategies.length }),
207
232
  ...(verboseAuthErrors && authErrorInfo?.details ? { auth_error: authErrorInfo.details } : {}),
208
233
  endpoint: endpoint.metadata.path,
209
- }, { 'x-request-id': requestId, 'cache-control': 'no-store' });
234
+ }, { 'x-request-id': requestId, ...traceHeaders, 'cache-control': 'no-store' });
210
235
  }
211
236
  context.auth = authContext;
212
237
  // Check role/scope authorization after successful authentication
@@ -232,7 +257,7 @@ export const executeEndpoint = async (options) => {
232
257
  actual: authzResult.actual,
233
258
  }),
234
259
  endpoint: endpoint.metadata.path,
235
- }, { 'x-request-id': requestId, 'cache-control': 'no-store' });
260
+ }, { 'x-request-id': requestId, ...traceHeaders, 'cache-control': 'no-store' });
236
261
  }
237
262
  const resolvedContext = await resolveContext(contextFactory, request, authContext);
238
263
  Object.assign(context, resolvedContext, additionalContext);
@@ -255,7 +280,7 @@ export const executeEndpoint = async (options) => {
255
280
  return createErrorResponse(403, 'FORBIDDEN', errorMessage, {
256
281
  reason: 'missing_tenant_context',
257
282
  tenant_required: true,
258
- }, { 'x-request-id': requestId, 'cache-control': 'no-store' });
283
+ }, { 'x-request-id': requestId, ...traceHeaders, 'cache-control': 'no-store' });
259
284
  }
260
285
  if (tenantId) {
261
286
  context.tenantId = tenantId;
@@ -291,7 +316,7 @@ export const executeEndpoint = async (options) => {
291
316
  });
292
317
  return createErrorResponse(400, 'VALIDATION_ERROR', 'Request validation failed', {
293
318
  issues: validationResult.error.issues,
294
- }, { 'x-request-id': requestId });
319
+ }, { 'x-request-id': requestId, ...traceHeaders });
295
320
  }
296
321
  context.input = validationResult.data;
297
322
  const pipeline = [
@@ -299,10 +324,7 @@ export const executeEndpoint = async (options) => {
299
324
  ...endpoint.middlewares,
300
325
  ];
301
326
  const result = await runMiddlewares(pipeline, context, () => endpoint.handler(context));
302
- const headers = {
303
- ...(endpoint.defaultHeaders ?? {}),
304
- 'x-request-id': requestId,
305
- };
327
+ const headers = withManagedTraceHeaders(endpoint.defaultHeaders, requestId, correlationId);
306
328
  // Authenticated and tenant-aware responses must never be shared by an
307
329
  // intermediary cache. Endpoint TTLs only opt public, tenant-independent
308
330
  // responses into shared caching.
@@ -377,7 +399,7 @@ export const executeEndpoint = async (options) => {
377
399
  'status' in error &&
378
400
  'payload' in error) {
379
401
  const structured = error;
380
- const response = createErrorResponse(structured.status, structured.payload.type, structured.payload.message, undefined, { 'x-request-id': requestId, ...(structured.headers ?? {}) });
402
+ const response = createErrorResponse(structured.status, structured.payload.type, structured.payload.message, undefined, withManagedTraceHeaders(structured.headers, requestId, correlationId));
381
403
  return response;
382
404
  }
383
405
  // When sanitizeErrors is enabled (default for HTTP), hide internal error
@@ -386,20 +408,24 @@ export const executeEndpoint = async (options) => {
386
408
  const errorMessage = sanitizeErrors
387
409
  ? 'An unexpected error occurred'
388
410
  : (error instanceof Error ? error.message : String(error));
389
- return createErrorResponse(500, 'INTERNAL_SERVER_ERROR', errorMessage, undefined, { 'x-request-id': requestId });
411
+ return createErrorResponse(500, 'INTERNAL_SERVER_ERROR', errorMessage, undefined, { 'x-request-id': requestId, ...traceHeaders });
390
412
  }
391
413
  };
392
414
  export const createServeHandler = ({ router, globalMiddlewares, authStrategies, tenantConfig, contextFactory, hooks, queryLogger, verboseAuthErrors = false, corsConfig, }) => {
393
415
  return async (request) => {
416
+ const { requestId, correlationId } = resolveRequestContext(request);
417
+ const traceHeaders = correlationId
418
+ ? { 'x-correlation-id': correlationId }
419
+ : {};
394
420
  // Handle CORS preflight and compute headers for actual requests
395
421
  const { preflightResponse, corsHeaders } = handleCorsRequest(corsConfig ?? null, request);
396
422
  if (preflightResponse) {
423
+ preflightResponse.headers = withManagedTraceHeaders(preflightResponse.headers, requestId, correlationId);
397
424
  return preflightResponse;
398
425
  }
399
- const requestId = resolveRequestId(request);
400
426
  const endpoint = router.match(request.method, request.path);
401
427
  if (!endpoint) {
402
- const response = createErrorResponse(404, 'NOT_FOUND', `No endpoint registered for ${request.method} ${request.path}`, undefined, { 'x-request-id': requestId });
428
+ const response = createErrorResponse(404, 'NOT_FOUND', `No endpoint registered for ${request.method} ${request.path}`, undefined, { 'x-request-id': requestId, ...traceHeaders });
403
429
  response.headers = { ...response.headers, ...corsHeaders };
404
430
  return response;
405
431
  }
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  export declare const ensureArray: <T>(value: T | T[] | undefined | null) => T[];
2
2
  export declare const mergeTags: (existing: string[], next?: string[]) => string[];
3
3
  export declare const generateRequestId: () => string;
4
+ /** Upper bound on an accepted external correlation id, in UTF-8 bytes. */
5
+ export declare const MAX_CORRELATION_ID_BYTES = 200;
6
+ /**
7
+ * Validate a caller-supplied external correlation id (e.g. `x-request-id`). This value is
8
+ * never authoritative — the authoritative request id is generated server-side. Returns the
9
+ * trimmed value when it is a safe, bounded string, or `undefined` when absent or rejected
10
+ * (empty, control characters, or over {@link MAX_CORRELATION_ID_BYTES}).
11
+ */
12
+ export declare const validateCorrelationId: (value?: string) => string | undefined;
4
13
  export declare const normalizeHeaderMap: (headers?: Record<string, string | undefined>) => Record<string, string | undefined>;
5
14
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,IAAI,KAAG,CAAC,EAKnE,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,UAAU,MAAM,EAAE,EAAE,OAAO,MAAM,EAAE,aAG5D,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAO,MAKpC,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,UAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,KAC/C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAInC,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,GAAG,IAAI,KAAG,CAAC,EAKnE,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,UAAU,MAAM,EAAE,EAAE,OAAO,MAAM,EAAE,aAG5D,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAO,MAKpC,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAS5C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,SAU/D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,UAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,KAC/C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAInC,CAAC"}
package/dist/utils.js CHANGED
@@ -14,6 +14,36 @@ export const generateRequestId = () => {
14
14
  }
15
15
  return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
16
16
  };
17
+ /** Upper bound on an accepted external correlation id, in UTF-8 bytes. */
18
+ export const MAX_CORRELATION_ID_BYTES = 200;
19
+ // C0 controls, DEL, and C1 controls — rejected so a caller-supplied correlation id can
20
+ // never inject newlines/control sequences into logs or response headers.
21
+ // eslint-disable-next-line no-control-regex -- control chars are exactly what we reject
22
+ const CORRELATION_CONTROL_CHARS = /[\u0000-\u001F\u007F-\u009F]/;
23
+ const CORRELATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
24
+ const correlationIdEncoder = new TextEncoder();
25
+ /**
26
+ * Validate a caller-supplied external correlation id (e.g. `x-request-id`). This value is
27
+ * never authoritative — the authoritative request id is generated server-side. Returns the
28
+ * trimmed value when it is a safe, bounded string, or `undefined` when absent or rejected
29
+ * (empty, control characters, or over {@link MAX_CORRELATION_ID_BYTES}).
30
+ */
31
+ export const validateCorrelationId = (value) => {
32
+ if (typeof value !== 'string')
33
+ return undefined;
34
+ const trimmed = value.trim();
35
+ if (trimmed.length === 0)
36
+ return undefined;
37
+ if (CORRELATION_CONTROL_CHARS.test(trimmed))
38
+ return undefined;
39
+ // Keep external identifiers in a narrow ASCII grammar so Unicode
40
+ // confusables and whitespace cannot produce visually ambiguous values.
41
+ if (!CORRELATION_ID_PATTERN.test(trimmed))
42
+ return undefined;
43
+ if (correlationIdEncoder.encode(trimmed).length > MAX_CORRELATION_ID_BYTES)
44
+ return undefined;
45
+ return trimmed;
46
+ };
17
47
  export const normalizeHeaderMap = (headers = {}) => {
18
48
  return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));
19
49
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypequery/serve",
3
- "version": "0.13.6",
3
+ "version": "0.14.0",
4
4
  "description": "Declarative HTTP server for exposing hypequery analytics endpoints",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "openapi-typescript": "^7.13.0",
28
28
  "zod": "^3.23.8",
29
29
  "zod-to-json-schema": "^3.23.5",
30
- "@hypequery/datasets": "^0.12.7"
30
+ "@hypequery/datasets": "^0.13.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "tsx": "^4.0.0"