/** Payload / timer bounds shared by the engine and default sinks. */ export const DEFAULT_MAX_STEPS = 50; export const MAX_STEPS_CAP = 200; export const MAX_TAG_KEYS = 32; export const MAX_ATTRIBUTE_KEYS = 64; export const MAX_ATTRIBUTED_REQUEST_KEYS = 50; export const MAX_TAG_VALUE_LEN = 200; export const DEFAULT_JOURNEY_IDLE_MS = 15 * 60 * 1_000; const DENIED_KEYS = new Set([ '__proto__', 'constructor', 'prototype', 'authorization', 'cookie', 'set-cookie', 'password', 'token', 'secret', 'ssn', ]); /** True for prototype-polluting keys and a small secret-name deny-list (case-insensitive). */ export function isUnsafeKey(key: string): boolean { return DENIED_KEYS.has(key.toLowerCase()); } /** * Finite integer ≥ 1, else `fallback`. Used for maxSteps-like caps. * Non-finite / ≤ 0 / non-numbers return `fallback`. */ export function finitePositiveInt(n: unknown, fallback: number): number { if (typeof n !== 'number' || !Number.isFinite(n) || n <= 0) { return fallback; } return Math.floor(n); } /** * Journey/step timeout: finite ≥ 0 keeps the value (floored); anything else is 0 (disabled). */ export function finiteTimeoutMs(n: unknown): number { if (typeof n !== 'number' || !Number.isFinite(n) || n < 0) { return 0; } return Math.floor(n); } /** Clamp recorded-step cap to 1..MAX_STEPS_CAP; invalid → DEFAULT_MAX_STEPS. */ export function clampMaxSteps(n: unknown): number { const value = finitePositiveInt(n, DEFAULT_MAX_STEPS); return Math.min(value, MAX_STEPS_CAP); } /** Finite ≥ 0 slow-request threshold, or undefined to fall through. */ export function finiteSlowRequestMs(n: unknown): number | undefined { if (typeof n !== 'number' || !Number.isFinite(n) || n < 0) { return undefined; } return Math.floor(n); } /** Explicit 0 disables; omitted uses idle default; finite N keeps caller timeout. */ export function resolveJourneyTimeoutMs( timeoutMs: number | undefined, idleDefaultMs: number = DEFAULT_JOURNEY_IDLE_MS ): { ms: number; explicit: boolean } { if (timeoutMs === undefined) { return { ms: finiteTimeoutMs(idleDefaultMs), explicit: false }; } return { ms: finiteTimeoutMs(timeoutMs), explicit: true }; }