autotel
Version:
Write Once, Observe Anywhere
1 lines • 23.5 kB
Source Map (JSON)
{"version":3,"file":"business-baggage.cjs","names":["propagation","context"],"sources":["../src/business-baggage.ts"],"sourcesContent":["/**\n * Safe baggage propagation with guardrails\n *\n * Provides type-safe baggage schemas with built-in protection against\n * common pitfalls: high-cardinality values, PII leakage, and oversized payloads.\n *\n * @example Define a custom schema\n * ```typescript\n * import { createSafeBaggageSchema } from 'autotel/business-baggage';\n *\n * const OrderBaggage = createSafeBaggageSchema({\n * orderId: { type: 'string' },\n * customerId: { type: 'string', hash: true }, // Auto-hash for privacy\n * priority: { type: 'enum', values: ['low', 'normal', 'high'] },\n * });\n *\n * // Usage in traced function\n * OrderBaggage.set(ctx, { orderId: 'ord-123', customerId: 'cust-456', priority: 'high' });\n * const { orderId, priority } = OrderBaggage.get(ctx);\n * ```\n *\n * @example Use pre-built BusinessBaggage\n * ```typescript\n * import { BusinessBaggage } from 'autotel/business-baggage';\n *\n * BusinessBaggage.set(ctx, { tenantId: 'acme', userId: 'user-123' });\n * const { tenantId } = BusinessBaggage.get(ctx);\n * ```\n *\n * @module\n */\n\nimport { context, propagation } from '@opentelemetry/api';\nimport type { TraceContext } from './trace-context';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Supported field types in baggage schema\n */\nexport type BaggageFieldType = 'string' | 'number' | 'boolean' | 'enum';\n\n/**\n * Field definition in a baggage schema\n */\nexport interface BaggageFieldDefinition {\n /** Field type */\n type: BaggageFieldType;\n\n /** Maximum length for string values (default: 256) */\n maxLength?: number;\n\n /** Hash value before storing (for privacy) */\n hash?: boolean;\n\n /** Allowed values for enum type */\n values?: readonly string[];\n\n /** Default value if not provided */\n defaultValue?: string | number | boolean;\n\n /** Whether field is required */\n required?: boolean;\n\n /** Custom validation function */\n validate?: (value: unknown) => boolean;\n}\n\n/**\n * Options for creating a safe baggage schema\n */\nexport interface SafeBaggageOptions {\n /** Maximum key length (default: 64) */\n maxKeyLength?: number;\n\n /** Maximum value length (default: 256) */\n maxValueLength?: number;\n\n /** Maximum total baggage size in bytes (default: 8192) */\n maxTotalSize?: number;\n\n /** Prefix for all keys (default: none) */\n prefix?: string;\n\n /** Hash high-cardinality values automatically */\n hashHighCardinality?: boolean;\n\n /** Detect and redact PII patterns */\n redactPII?: boolean;\n\n /** Allowed keys whitelist (others rejected) */\n allowedKeys?: string[];\n\n /** Custom error handler */\n onError?: (error: BaggageError) => void;\n}\n\n/**\n * Schema definition type - maps field names to definitions\n */\nexport type BaggageSchemaDefinition = Record<string, BaggageFieldDefinition>;\n\n/**\n * Inferred type from schema definition\n */\nexport type InferBaggageType<T extends BaggageSchemaDefinition> = {\n [K in keyof T]?: T[K]['type'] extends 'string'\n ? string\n : T[K]['type'] extends 'number'\n ? number\n : T[K]['type'] extends 'boolean'\n ? boolean\n : T[K]['type'] extends 'enum'\n ? T[K]['values'] extends readonly string[]\n ? T[K]['values'][number]\n : string\n : unknown;\n};\n\n/**\n * Baggage error details\n */\nexport interface BaggageError {\n type: 'validation' | 'size' | 'pii' | 'key_length' | 'value_length';\n key: string;\n message: string;\n value?: unknown;\n}\n\n/**\n * Safe baggage schema interface\n */\nexport interface SafeBaggageSchema<T extends BaggageSchemaDefinition> {\n /**\n * Get baggage values from context\n */\n get(ctx?: TraceContext): Partial<InferBaggageType<T>>;\n\n /**\n * Set baggage values in context\n * Returns new context with baggage (for context propagation)\n */\n set(\n ctx: TraceContext | undefined,\n values: Partial<InferBaggageType<T>>,\n ): void;\n\n /**\n * Get a single baggage value\n */\n getValue<K extends keyof T>(\n key: K,\n ctx?: TraceContext,\n ): InferBaggageType<T>[K] | undefined;\n\n /**\n * Set a single baggage value\n */\n setValue<K extends keyof T>(\n key: K,\n value: InferBaggageType<T>[K],\n ctx?: TraceContext,\n ): void;\n\n /**\n * Clear all schema baggage values\n */\n clear(ctx?: TraceContext): void;\n\n /**\n * Get all baggage as headers for propagation\n */\n toHeaders(ctx?: TraceContext): Record<string, string>;\n\n /**\n * Restore baggage from headers\n */\n fromHeaders(headers: Record<string, string>, ctx?: TraceContext): void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_MAX_KEY_LENGTH = 64;\nconst DEFAULT_MAX_VALUE_LENGTH = 256;\nconst DEFAULT_MAX_TOTAL_SIZE = 8192;\n\n// PII patterns to detect and redact\nconst PII_PATTERNS = [\n /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/, // Email\n /\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b/, // Phone (US)\n /\\b\\d{3}[-]?\\d{2}[-]?\\d{4}\\b/, // SSN\n /\\b\\d{16}\\b/, // Credit card (basic)\n];\n\n// High-cardinality value patterns\nconst HIGH_CARDINALITY_PATTERNS = [\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, // UUID\n /^\\d{13,}$/, // Timestamps\n /^[A-Za-z0-9+/]{20,}={0,2}$/, // Base64\n];\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create a safe baggage schema with validation and guardrails\n *\n * @param schema - Field definitions\n * @param options - Safety options\n * @returns Type-safe baggage schema\n *\n * @example\n * ```typescript\n * const MyBaggage = createSafeBaggageSchema({\n * userId: { type: 'string', hash: true },\n * region: { type: 'enum', values: ['us', 'eu', 'ap'] },\n * debug: { type: 'boolean', defaultValue: false },\n * });\n * ```\n */\nexport function createSafeBaggageSchema<T extends BaggageSchemaDefinition>(\n schema: T,\n options: SafeBaggageOptions = {},\n): SafeBaggageSchema<T> {\n const {\n maxKeyLength = DEFAULT_MAX_KEY_LENGTH,\n maxValueLength = DEFAULT_MAX_VALUE_LENGTH,\n maxTotalSize = DEFAULT_MAX_TOTAL_SIZE,\n prefix = '',\n hashHighCardinality = false,\n redactPII = false,\n allowedKeys,\n onError,\n } = options;\n\n // Validate schema keys\n const schemaKeys = new Set(Object.keys(schema));\n if (allowedKeys) {\n for (const key of schemaKeys) {\n if (!allowedKeys.includes(key)) {\n throw new Error(`Key \"${key}\" not in allowedKeys whitelist`);\n }\n }\n }\n\n // Prefix a key\n const prefixKey = (key: string): string =>\n prefix ? `${prefix}.${key}` : key;\n\n // Hash a value using simple FNV-1a (synchronous, no crypto dependency)\n const hashValue = (value: string): string => {\n let hash = 2_166_136_261;\n for (let i = 0; i < value.length; i++) {\n hash ^= value.codePointAt(i) ?? 0;\n hash = (hash * 16_777_619) >>> 0;\n }\n return `h_${hash.toString(16)}`;\n };\n\n // Check for PII\n const containsPII = (value: string): boolean => {\n return PII_PATTERNS.some((pattern) => pattern.test(value));\n };\n\n // Check for high-cardinality\n const isHighCardinality = (value: string): boolean => {\n return HIGH_CARDINALITY_PATTERNS.some((pattern) => pattern.test(value));\n };\n\n // Validate and transform a single value\n const validateAndTransform = (\n key: string,\n value: unknown,\n fieldDef: BaggageFieldDefinition,\n ): string | null => {\n const fullKey = prefixKey(key);\n\n // Check key length\n if (fullKey.length > maxKeyLength) {\n onError?.({\n type: 'key_length',\n key,\n message: `Key \"${key}\" exceeds max length ${maxKeyLength}`,\n });\n return null;\n }\n\n // Handle undefined/null with default\n if (value === undefined || value === null) {\n if (fieldDef.required) {\n onError?.({\n type: 'validation',\n key,\n message: `Required field \"${key}\" is missing`,\n });\n return null;\n }\n if (fieldDef.defaultValue === undefined) {\n return null;\n } else {\n value = fieldDef.defaultValue;\n }\n }\n\n // Type validation\n let stringValue: string;\n\n switch (fieldDef.type) {\n case 'string': {\n if (typeof value !== 'string') {\n onError?.({\n type: 'validation',\n key,\n message: `Field \"${key}\" expected string, got ${typeof value}`,\n value,\n });\n return null;\n }\n stringValue = value;\n break;\n }\n\n case 'number': {\n if (typeof value !== 'number' || Number.isNaN(value)) {\n onError?.({\n type: 'validation',\n key,\n message: `Field \"${key}\" expected number, got ${typeof value}`,\n value,\n });\n return null;\n }\n stringValue = String(value);\n break;\n }\n\n case 'boolean': {\n if (typeof value !== 'boolean') {\n onError?.({\n type: 'validation',\n key,\n message: `Field \"${key}\" expected boolean, got ${typeof value}`,\n value,\n });\n return null;\n }\n stringValue = String(value);\n break;\n }\n\n case 'enum': {\n if (!fieldDef.values?.includes(String(value))) {\n onError?.({\n type: 'validation',\n key,\n message: `Field \"${key}\" value \"${value}\" not in allowed values: ${fieldDef.values?.join(', ')}`,\n value,\n });\n return null;\n }\n stringValue = String(value);\n break;\n }\n\n default: {\n stringValue = String(value);\n }\n }\n\n // Custom validation\n if (fieldDef.validate && !fieldDef.validate(value)) {\n onError?.({\n type: 'validation',\n key,\n message: `Field \"${key}\" failed custom validation`,\n value,\n });\n return null;\n }\n\n // PII check\n if (redactPII && containsPII(stringValue)) {\n onError?.({\n type: 'pii',\n key,\n message: `Field \"${key}\" contains PII pattern`,\n value: '[REDACTED]',\n });\n stringValue = hashValue(stringValue);\n }\n\n // Hash if requested or high-cardinality\n if (\n fieldDef.hash ||\n (hashHighCardinality && isHighCardinality(stringValue))\n ) {\n stringValue = hashValue(stringValue);\n }\n\n // Length validation\n const maxLen = fieldDef.maxLength ?? maxValueLength;\n if (stringValue.length > maxLen) {\n onError?.({\n type: 'value_length',\n key,\n message: `Field \"${key}\" value exceeds max length ${maxLen}`,\n value: stringValue,\n });\n stringValue = stringValue.slice(0, maxLen);\n }\n\n return stringValue;\n };\n\n // Parse value back from baggage string\n const parseValue = (\n key: string,\n stringValue: string,\n fieldDef: BaggageFieldDefinition,\n ): unknown => {\n switch (fieldDef.type) {\n case 'number': {\n return Number.parseFloat(stringValue);\n }\n case 'boolean': {\n return stringValue === 'true';\n }\n default: {\n return stringValue;\n }\n }\n };\n\n return {\n get(): Partial<InferBaggageType<T>> {\n const baggage = propagation.getBaggage(context.active());\n if (!baggage) {\n return {};\n }\n\n const result: Record<string, unknown> = {};\n\n for (const [key, fieldDef] of Object.entries(schema)) {\n const fullKey = prefixKey(key);\n const entry = baggage.getEntry(fullKey);\n\n if (entry) {\n result[key] = parseValue(key, entry.value, fieldDef);\n } else if (fieldDef.defaultValue !== undefined) {\n result[key] = fieldDef.defaultValue;\n }\n }\n\n return result as Partial<InferBaggageType<T>>;\n },\n\n set(\n ctx: TraceContext | undefined,\n values: Partial<InferBaggageType<T>>,\n ): void {\n let baggage =\n propagation.getBaggage(context.active()) ?? propagation.createBaggage();\n let totalSize = 0;\n\n // Calculate existing size\n for (const [key, entry] of baggage.getAllEntries()) {\n totalSize += key.length + entry.value.length;\n }\n\n for (const [key, value] of Object.entries(values)) {\n const fieldDef = schema[key];\n if (!fieldDef) continue;\n\n const fullKey = prefixKey(key);\n const stringValue = validateAndTransform(key, value, fieldDef);\n\n if (stringValue !== null) {\n // Check total size\n const entrySize = fullKey.length + stringValue.length;\n if (totalSize + entrySize > maxTotalSize) {\n onError?.({\n type: 'size',\n key,\n message: `Adding \"${key}\" would exceed max baggage size ${maxTotalSize}`,\n value,\n });\n continue;\n }\n\n baggage = baggage.setEntry(fullKey, { value: stringValue });\n totalSize += entrySize;\n }\n }\n\n // Update context with new baggage\n const newContext = propagation.setBaggage(context.active(), baggage);\n // Note: This only works if the caller propagates the context\n // In OTel, baggage propagation happens via context.with()\n // For now we set on active context\n propagation.setBaggage(newContext, baggage);\n },\n\n getValue<K extends keyof T>(key: K): InferBaggageType<T>[K] | undefined {\n const baggage = propagation.getBaggage(context.active());\n if (!baggage) return undefined;\n\n const fullKey = prefixKey(String(key));\n const entry = baggage.getEntry(fullKey);\n const fieldDef = schema[String(key)];\n\n if (!entry) {\n return fieldDef?.defaultValue as InferBaggageType<T>[K] | undefined;\n }\n\n if (!fieldDef) {\n return undefined;\n }\n\n return parseValue(\n String(key),\n entry.value,\n fieldDef,\n ) as InferBaggageType<T>[K];\n },\n\n setValue<K extends keyof T>(\n key: K,\n value: InferBaggageType<T>[K],\n ctx?: TraceContext,\n ): void {\n this.set(ctx, { [key]: value } as Partial<InferBaggageType<T>>);\n },\n\n clear(): void {\n let baggage = propagation.getBaggage(context.active());\n if (!baggage) return;\n\n for (const key of Object.keys(schema)) {\n const fullKey = prefixKey(key);\n baggage = baggage.removeEntry(fullKey);\n }\n\n propagation.setBaggage(context.active(), baggage);\n },\n\n toHeaders(): Record<string, string> {\n const headers: Record<string, string> = {};\n propagation.inject(context.active(), headers);\n return headers;\n },\n\n fromHeaders(headers: Record<string, string>, ctx?: TraceContext): void {\n const extractedContext = propagation.extract(context.active(), headers);\n const baggage = propagation.getBaggage(extractedContext);\n\n if (baggage) {\n const values: Record<string, unknown> = {};\n\n for (const [key, fieldDef] of Object.entries(schema)) {\n const fullKey = prefixKey(key);\n const entry = baggage.getEntry(fullKey);\n\n if (entry) {\n values[key] = parseValue(key, entry.value, fieldDef);\n }\n }\n\n this.set(ctx, values as Partial<InferBaggageType<T>>);\n }\n },\n };\n}\n\n// ============================================================================\n// Pre-built Business Context Schema\n// ============================================================================\n\n/**\n * Pre-built baggage schema for common business context fields\n *\n * Fields:\n * - `tenantId`: Multi-tenant identifier (string, max 64 chars)\n * - `userId`: User identifier (hashed for privacy)\n * - `correlationId`: Request correlation ID (string)\n * - `workflowId`: Workflow/saga instance ID (string)\n * - `priority`: Request priority (low, normal, high, critical)\n * - `region`: Geographic region (string)\n * - `channel`: Request channel (web, mobile, api, internal)\n *\n * @example\n * ```typescript\n * import { BusinessBaggage } from 'autotel/business-baggage';\n *\n * // Set business context at entry point\n * BusinessBaggage.set(ctx, {\n * tenantId: 'acme-corp',\n * userId: 'user-123',\n * priority: 'high',\n * channel: 'api',\n * });\n *\n * // Access anywhere in the trace\n * const { tenantId, priority } = BusinessBaggage.get(ctx);\n * ```\n */\nexport const BusinessBaggage = createSafeBaggageSchema(\n {\n tenantId: {\n type: 'string',\n maxLength: 64,\n },\n userId: {\n type: 'string',\n hash: true, // Auto-hash for privacy\n maxLength: 64,\n },\n correlationId: {\n type: 'string',\n maxLength: 128,\n },\n workflowId: {\n type: 'string',\n maxLength: 128,\n },\n priority: {\n type: 'enum',\n values: ['low', 'normal', 'high', 'critical'] as const,\n defaultValue: 'normal',\n },\n region: {\n type: 'string',\n maxLength: 32,\n },\n channel: {\n type: 'enum',\n values: [\n 'web',\n 'mobile',\n 'api',\n 'internal',\n 'webhook',\n 'scheduled',\n ] as const,\n },\n },\n {\n prefix: 'biz',\n redactPII: true,\n hashHighCardinality: true,\n },\n);\n\n/**\n * Type alias for BusinessBaggage values\n */\nexport type BusinessBaggageValues = {\n tenantId?: string;\n userId?: string;\n correlationId?: string;\n workflowId?: string;\n priority?: 'low' | 'normal' | 'high' | 'critical';\n region?: string;\n channel?: 'web' | 'mobile' | 'api' | 'internal' | 'webhook' | 'scheduled';\n};\n"],"mappings":"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"}