@ciq-dev/neoiq-foundation-node 1.1.2-beta.11 → 1.1.2-beta.13
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/dist/bootstrap.js +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{tracing-BYBASOyy.js → tracing-CDX5DHsr.js} +3 -3
- package/dist/{tracing-CVXLySRJ.mjs.map → tracing-CDX5DHsr.js.map} +1 -1
- package/dist/{tracing-CVXLySRJ.mjs → tracing-C_cQs3bg.mjs} +4 -4
- package/dist/tracing-C_cQs3bg.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/tracing-BYBASOyy.js.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["correlationId: string","context: RequestContext","fn: () => T","key: K","updates: Partial<RequestContext>","key: string","value: unknown","data: Record<string, unknown>","REDACT_PATHS: ReadonlyArray<string>","VALUE_PATTERNS: ReadonlyArray<{ label: string; pattern: RegExp }>","SENSITIVE_KEYS: ReadonlySet<string>","value: unknown","value: string","depth: number","obj: Record<string, unknown>","result: Record<string, unknown>","body: T","additionalPaths: string[]","options: LoggerOptions","result: Record<string, unknown>","pinoLogger: PinoLogger","serviceName: string","baseBindings: object","level: string","obj: object","msg?: string","fallback: Logger","globalLogger: Logger | null","logger: Logger","meterProvider: MeterProvider | null","options: Pick<MetricsOptions, 'endpoint' | 'intervalMs'>","options: MetricsOptions","name: string","version: string","headers: Record<string, unknown>","redactList: string[]","redacted: Record<string, unknown>","body: unknown","maxSize: number","options: FastifyPluginOptions","plugin: FastifyPluginAsync<Partial<FastifyPluginOptions>>","ctx: PluginRequestContext","fn: () => T","_ctx: PluginRequestContext","requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']>","requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']>","requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']>","request: FastifyRequest","reply: FastifyReply","requestContext: PluginRequestContext","logData: Record<string, unknown>","_reply: FastifyReply","error: Error","options: HttpClientOptions","logger: Logger","requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null","requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']> | null","requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null","config: InternalAxiosRequestConfig","carrier: Record<string, string>","response: AxiosResponse","retryConfig: IAxiosRetryConfig","config: AxiosRequestConfig","url: string","config?: AxiosRequestConfig","data?: unknown","value: string","err: unknown","options: VaultClientOptions","jwt: string","path: string","key: string","oldPath: string","newPath: string","logger?: Logger","input: FoundationConfigInput","logger: Logger","loggingError: string | undefined","metricReader: ReturnType<typeof createMetricReader> | undefined","metricsError: string | undefined","tracerInstance: Tracer | null","tracingError: string | undefined","meterInstance: Meter | null","origin: string","err: unknown","flushPromises: Promise<void>[]","name: string","fn: () => T | Promise<T>","observability: ObservabilityModule","name?: string","version?: string","httpModule: HttpModule","secrets: SecretsModule | null","status: HealthStatus['status']","promises: Promise<void>[]","fallback?: T","lifecycle: LifecycleModule","foundation: FoundationInstance","err: unknown","clientMod: Record<string, unknown>","presignerMod: Record<string, unknown>","options: AwsS3ObjectStoreOptions","ref: ObjectRef","body: ObjectBody","options: PutObjectOptions","options: ListObjectsOptions","options: PresignGetObjectOptions","options: PresignPutObjectOptions","body: ObjectBody","chunks: Buffer[]","buf: Buffer","options: InMemoryObjectStoreOptions","bucket: string","ref: ObjectRef","options: PutObjectOptions","obj: StoredObject","options: ListObjectsOptions","_ref: ObjectRef","_options: PresignGetObjectOptions","_options: PresignPutObjectOptions"],"sources":["../src/features/context.ts","../src/features/redaction.ts","../src/features/logging.ts","../src/features/metrics.ts","../src/integrations/fastify-plugin.ts","../src/integrations/http-client.ts","../src/features/vault.ts","../src/foundation.ts","../src/integrations/object-store/aws-s3.ts","../src/integrations/object-store/in-memory.ts"],"sourcesContent":["/**\n * Context Manager\n *\n * Uses OTEL Baggage as the primary store for correlationId when tracing\n * is active. Falls back to AsyncLocalStorage when tracing is disabled,\n * ensuring correlation IDs work regardless of OTEL state.\n */\n\nimport { AsyncLocalStorage } from 'async_hooks';\nimport {\n propagation,\n context as otelContext,\n type Context as OtelContext,\n} from '@opentelemetry/api';\n\nexport interface RequestContext {\n correlationId?: string;\n traceId?: string;\n spanId?: string;\n startTime?: number;\n contextData?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface ContextManager {\n getContext(): RequestContext | undefined;\n run<T>(context: RequestContext, fn: () => T): T;\n get<K extends keyof RequestContext>(key: K): RequestContext[K] | undefined;\n update(updates: Partial<RequestContext>): RequestContext | undefined;\n\n /** Set an arbitrary context value (e.g. tenant_id, user_id) — auto-injected into every log line */\n setContextValue(key: string, value: unknown): void;\n /** Get a single context value by key */\n getContextValue(key: string): unknown;\n /** Get all arbitrary context data */\n getContextData(): Record<string, unknown>;\n /** Bulk-set multiple context values */\n setContextData(data: Record<string, unknown>): void;\n /** Clear all arbitrary context data */\n clearContextData(): void;\n}\n\nconst BAGGAGE_CORRELATION_KEY = 'correlation.id';\n\nfunction setBaggageCorrelationId(correlationId: string): OtelContext {\n const currentBaggage = propagation.getBaggage(otelContext.active());\n const baggage = (currentBaggage ?? propagation.createBaggage()).setEntry(\n BAGGAGE_CORRELATION_KEY,\n { value: correlationId }\n );\n return propagation.setBaggage(otelContext.active(), baggage);\n}\n\nfunction getBaggageCorrelationId(): string | undefined {\n const baggage = propagation.getBaggage(otelContext.active());\n return baggage?.getEntry(BAGGAGE_CORRELATION_KEY)?.value;\n}\n\n/** Create a new context manager instance */\nexport function createContextManager(): ContextManager {\n const als = new AsyncLocalStorage<RequestContext>();\n\n return {\n getContext(): RequestContext | undefined {\n const alsCtx = als.getStore();\n if (!alsCtx) return undefined;\n\n // Prefer OTEL Baggage for correlationId when available\n const baggageCorrelationId = getBaggageCorrelationId();\n if (baggageCorrelationId && baggageCorrelationId !== alsCtx.correlationId) {\n return { ...alsCtx, correlationId: baggageCorrelationId };\n }\n return alsCtx;\n },\n\n run<T>(context: RequestContext, fn: () => T): T {\n // Store correlationId in OTEL Baggage for cross-service propagation\n if (context.correlationId) {\n const otelCtx = setBaggageCorrelationId(context.correlationId);\n return otelContext.with(otelCtx, () => als.run(context, fn));\n }\n return als.run(context, fn);\n },\n\n get<K extends keyof RequestContext>(key: K): RequestContext[K] | undefined {\n if (key === 'correlationId') {\n return (getBaggageCorrelationId() ?? als.getStore()?.correlationId) as RequestContext[K];\n }\n return als.getStore()?.[key];\n },\n\n update(updates: Partial<RequestContext>): RequestContext | undefined {\n const current = als.getStore();\n if (!current) return undefined;\n if (updates.correlationId) {\n setBaggageCorrelationId(updates.correlationId);\n }\n Object.assign(current, updates);\n return current;\n },\n\n setContextValue(key: string, value: unknown): void {\n const current = als.getStore();\n if (!current) return;\n if (!current.contextData) current.contextData = {};\n current.contextData[key] = value;\n },\n\n getContextValue(key: string): unknown {\n return als.getStore()?.contextData?.[key];\n },\n\n getContextData(): Record<string, unknown> {\n return als.getStore()?.contextData ?? {};\n },\n\n setContextData(data: Record<string, unknown>): void {\n const current = als.getStore();\n if (!current) return;\n current.contextData = { ...(current.contextData ?? {}), ...data };\n },\n\n clearContextData(): void {\n const current = als.getStore();\n if (!current) return;\n current.contextData = {};\n },\n };\n}\n","/**\n * Log Redaction & PII Sanitization\n *\n * Two-layer approach:\n * - Layer A: Pino native `redact` (fast-redact) for key-based redaction on every log call.\n * Compiled at init, near-zero runtime cost.\n * - Layer B: Deep-traverse sanitizer for value-pattern detection (JWTs, AWS keys, etc.).\n * Only used for request/response body logging — NOT on every log call.\n */\n\nconst PLACEHOLDER = '[REDACTED]';\n\n/**\n * Key names that Pino's fast-redact will censor automatically.\n * Supports wildcards: '*.password' matches nested keys one level deep.\n */\nexport const REDACT_PATHS: ReadonlyArray<string> = [\n 'password',\n 'passwd',\n 'pass',\n 'pwd',\n 'secret',\n 'secretKey',\n 'secret_key',\n 'token',\n 'accessToken',\n 'access_token',\n 'refreshToken',\n 'refresh_token',\n 'idToken',\n 'id_token',\n 'apiKey',\n 'api_key',\n 'apiSecret',\n 'api_secret',\n 'authorization',\n 'auth',\n 'credentials',\n 'privateKey',\n 'private_key',\n 'cookie',\n 'setCookie',\n 'set_cookie',\n 'creditCard',\n 'credit_card',\n 'cardNumber',\n 'card_number',\n 'ccNumber',\n 'cc_number',\n 'cvv',\n 'cvc',\n 'securityCode',\n 'security_code',\n 'accountNumber',\n 'account_number',\n 'ssn',\n 'socialSecurity',\n 'social_security',\n 'dateOfBirth',\n 'date_of_birth',\n 'dob',\n // Wildcard variants for one-level nesting\n '*.password',\n '*.secret',\n '*.token',\n '*.apiKey',\n '*.api_key',\n '*.authorization',\n '*.cookie',\n '*.credentials',\n '*.creditCard',\n '*.cardNumber',\n '*.cvv',\n '*.ssn',\n '*.privateKey',\n '*.private_key',\n];\n\n/**\n * Value patterns that indicate a secret regardless of the key name.\n * Used only for body sanitization (Layer B).\n */\nconst VALUE_PATTERNS: ReadonlyArray<{ label: string; pattern: RegExp }> = [\n { label: 'jwt', pattern: /^eyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]+$/ },\n { label: 'aws_access_key', pattern: /(?:^|[^A-Za-z0-9])AKIA[0-9A-Z]{16}(?:$|[^A-Za-z0-9])/ },\n { label: 'stripe_key', pattern: /^[sr]k_(live|test)_[A-Za-z0-9]{10,}$/ },\n { label: 'openai_key', pattern: /^sk-[A-Za-z0-9_-]{20,}$/ },\n { label: 'github_token', pattern: /^gh[ps]_[A-Za-z0-9]{36,}$/ },\n { label: 'pem_private_key', pattern: /-----BEGIN\\s+(RSA\\s+|EC\\s+)?PRIVATE\\s+KEY-----/ },\n { label: 'connection_string', pattern: /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/[^:]+:[^@]+@/ },\n { label: 'bearer_token', pattern: /^Bearer\\s+\\S{10,}$/i },\n];\n\nconst SENSITIVE_KEYS: ReadonlySet<string> = new Set(\n REDACT_PATHS.filter((p) => !p.includes('*')).map((k) => k.toLowerCase())\n);\n\nconst MAX_DEPTH = 10;\nconst MAX_KEYS = 200;\n\nfunction mask(value: unknown): string {\n if (typeof value !== 'string' || value.length <= 8) return PLACEHOLDER;\n return `${value.slice(0, 4)}${'*'.repeat(Math.min(value.length - 8, 20))}${value.slice(-4)}`;\n}\n\nfunction isSensitiveValue(value: string): boolean {\n return VALUE_PATTERNS.some((p) => p.pattern.test(value));\n}\n\nfunction sanitizeValue(value: unknown, depth: number): unknown {\n if (depth > MAX_DEPTH) return value;\n if (typeof value === 'string' && isSensitiveValue(value)) return mask(value);\n if (Array.isArray(value)) return value.map((item) => sanitizeValue(item, depth + 1));\n if (value !== null && typeof value === 'object') {\n return sanitizeObject(value as Record<string, unknown>, depth + 1);\n }\n return value;\n}\n\nfunction sanitizeObject(obj: Record<string, unknown>, depth: number): Record<string, unknown> {\n if (depth > MAX_DEPTH) return obj;\n const keys = Object.keys(obj);\n if (keys.length > MAX_KEYS) return obj;\n\n const result: Record<string, unknown> = {};\n for (const key of keys) {\n const value = obj[key];\n if (SENSITIVE_KEYS.has(key.toLowerCase())) {\n result[key] = typeof value === 'string' ? mask(value) : PLACEHOLDER;\n } else {\n result[key] = sanitizeValue(value, depth);\n }\n }\n return result;\n}\n\n/**\n * Deep-traverse sanitizer for request/response bodies.\n * Checks both key names (deny-list) and value patterns (JWT, AWS keys, etc.).\n * NOT intended for every log call — use Pino native `redact` for that.\n */\nexport function sanitizeBody<T>(body: T): T {\n if (body === null || body === undefined) return body;\n if (typeof body === 'string') {\n return (isSensitiveValue(body) ? mask(body) : body) as T;\n }\n if (typeof body !== 'object') return body;\n if (Array.isArray(body)) return body.map((item) => sanitizeValue(item, 0)) as T;\n return sanitizeObject(body as Record<string, unknown>, 0) as T;\n}\n\n/** Build the Pino `redact` config object for fast-redact integration */\nexport function buildPinoRedactConfig(additionalPaths: string[] = []): {\n paths: string[];\n censor: string;\n} {\n return {\n paths: [...REDACT_PATHS, ...additionalPaths],\n censor: PLACEHOLDER,\n };\n}\n","/**\n * Structured Logging with Pino\n */\n\nimport pino, { Logger as PinoLogger } from 'pino';\nimport { trace } from '@opentelemetry/api';\nimport { type ContextManager } from './context';\nimport { buildPinoRedactConfig } from './redaction';\n\nexport interface LoggerOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n level: 'debug' | 'info' | 'warn' | 'error';\n prettyPrint: boolean;\n contextManager?: ContextManager;\n additionalRedactPaths?: string[];\n}\n\nexport interface Logger {\n debug: (obj: object, msg?: string) => void;\n info: (obj: object, msg?: string) => void;\n warn: (obj: object, msg?: string) => void;\n error: (obj: object, msg?: string) => void;\n child: (bindings: object) => Logger;\n readonly pino: PinoLogger;\n}\n\n/** Create a structured logger with automatic trace context injection */\nexport function createLogger(options: LoggerOptions): Logger {\n const {\n serviceName,\n serviceVersion,\n environment,\n level,\n prettyPrint,\n contextManager,\n additionalRedactPaths = [],\n } = options;\n\n const pinoLogger = pino({\n level,\n redact: buildPinoRedactConfig(additionalRedactPaths),\n base: { service: serviceName, version: serviceVersion, env: environment },\n mixin: () => {\n const span = trace.getActiveSpan();\n const spanContext = span?.spanContext();\n const ctx = contextManager?.getContext();\n\n const traceId = spanContext?.traceId || ctx?.traceId;\n const spanId = spanContext?.spanId || ctx?.spanId;\n const correlationId = ctx?.correlationId;\n const contextData = ctx?.contextData;\n\n const result: Record<string, unknown> = {\n trace_id: traceId,\n span_id: spanId,\n correlation_id: correlationId,\n };\n\n if (contextData && Object.keys(contextData).length > 0) {\n result.context = contextData;\n }\n\n return result;\n },\n formatters: { level: (label) => ({ level: label }) },\n transport: prettyPrint\n ? {\n target: 'pino-pretty',\n options: { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname' },\n }\n : undefined,\n });\n\n return wrapPinoLogger(pinoLogger);\n}\n\nfunction wrapPinoLogger(pinoLogger: PinoLogger): Logger {\n return {\n debug: (obj, msg) => pinoLogger.debug(obj, msg),\n info: (obj, msg) => pinoLogger.info(obj, msg),\n warn: (obj, msg) => pinoLogger.warn(obj, msg),\n error: (obj, msg) => pinoLogger.error(obj, msg),\n child: (bindings) => wrapPinoLogger(pinoLogger.child(bindings)),\n pino: pinoLogger,\n };\n}\n\n/** Fallback logger when Pino is not available */\nexport function createFallbackLogger(\n serviceName: string = 'unknown',\n baseBindings: object = {}\n): Logger {\n const log = (level: string, obj: object, msg?: string): void => {\n const logObj = {\n timestamp: new Date().toISOString(),\n level,\n service: serviceName,\n ...baseBindings,\n ...obj,\n msg,\n };\n const payload = JSON.stringify(logObj);\n if (level === 'warn') console.warn(payload);\n else if (level === 'error') console.error(payload);\n else console.log(payload);\n };\n\n const fallback: Logger = {\n debug: (obj, msg) => log('debug', obj, msg),\n info: (obj, msg) => log('info', obj, msg),\n warn: (obj, msg) => log('warn', obj, msg),\n error: (obj, msg) => log('error', obj, msg),\n child: (bindings) => createFallbackLogger(serviceName, { ...baseBindings, ...bindings }),\n pino: null as unknown as PinoLogger,\n };\n\n return fallback;\n}\n\n// Global logger for standalone usage\nlet globalLogger: Logger | null = null;\n\nexport function setGlobalLogger(logger: Logger): void {\n globalLogger = logger;\n}\n\nexport function getGlobalLogger(): Logger {\n return globalLogger || createFallbackLogger();\n}\n","/**\n * OpenTelemetry Metrics Setup\n */\n\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n type IMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { metrics, type Meter } from '@opentelemetry/api';\nimport { getDefaultOtelEndpoint } from '../config';\n\nexport interface MetricsOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n intervalMs?: number;\n}\n\nlet meterProvider: MeterProvider | null = null;\nlet isInitialized = false;\n\n/**\n * Build a gRPC OTLP metric reader for the tracing NodeSDK to own.\n *\n * Preferred path: hand this reader to `setupTracing({ metricReader })` so a single\n * NodeSDK owns both traces and metrics — one global MeterProvider, exporting to the\n * same gRPC collector. This avoids the duplicate global-MeterProvider registration\n * that occurs when NodeSDK and a standalone `setupMetrics` both try to register.\n *\n * The MeterProvider, resource, and global registration are owned by NodeSDK; this\n * function only constructs the reader/exporter.\n */\nexport function createMetricReader(\n options: Pick<MetricsOptions, 'endpoint' | 'intervalMs'> = {}\n): IMetricReader {\n const { endpoint = getDefaultOtelEndpoint(), intervalMs = 5000 } = options;\n const exporter = new OTLPMetricExporter({ url: endpoint });\n const reader = new PeriodicExportingMetricReader({\n exporter,\n exportIntervalMillis: intervalMs,\n });\n isInitialized = true;\n return reader;\n}\n\n/**\n * Initialize a standalone metrics pipeline with its own MeterProvider.\n *\n * Fallback for when tracing (and thus the NodeSDK) is disabled but metrics are\n * enabled. When tracing is on, prefer {@link createMetricReader} + NodeSDK so a\n * single SDK owns both signals.\n */\nexport function setupMetrics(options: MetricsOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Metrics already initialized');\n return;\n }\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n intervalMs = 5000,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const metricExporter = new OTLPMetricExporter({ url: endpoint });\n const metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: intervalMs,\n });\n\n meterProvider = new MeterProvider({ resource, readers: [metricReader] });\n metrics.setGlobalMeterProvider(meterProvider);\n isInitialized = true;\n}\n\n/** Shutdown metrics gracefully */\nexport async function shutdownMetrics(): Promise<void> {\n if (!meterProvider) return;\n try {\n await meterProvider.shutdown();\n isInitialized = false;\n meterProvider = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down metrics:', error);\n throw error;\n }\n}\n\nexport function getMeter(name: string, version: string = '1.0.0'): Meter {\n return metrics.getMeter(name, version);\n}\n\nexport function isMetricsEnabled(): boolean {\n return isInitialized;\n}\n\nexport { metrics };\nexport type { Meter };\n","/**\n * Fastify Observability Plugin\n */\n\nimport { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { randomUUID } from 'crypto';\nimport { trace, SpanStatusCode } from '@opentelemetry/api';\nimport { type ContextManager } from '../features/context';\nimport { type Logger, getGlobalLogger } from '../features/logging';\nimport { getMeter } from '../features/metrics';\nimport { sanitizeBody } from '../features/redaction';\n\ninterface PluginRequestContext {\n correlationId: string;\n traceId: string;\n spanId: string;\n startTime: number;\n [key: string]: unknown;\n}\n\nexport interface RequestLoggingOptions {\n logHeaders?: boolean;\n logBody?: boolean;\n logResponseBody?: boolean;\n maxBodySize?: number;\n redactHeaders?: string[];\n}\n\nexport interface FastifyPluginOptions {\n serviceName: string;\n logger?: Logger;\n contextManager?: ContextManager;\n tracingEnabled?: boolean;\n metricsEnabled?: boolean;\n excludeRoutes?: string[];\n /** Request/response logging options */\n requestLogging?: RequestLoggingOptions;\n}\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n __requestContext?: PluginRequestContext;\n }\n}\n\n// Default headers to redact for security\nconst DEFAULT_REDACT_HEADERS = [\n 'authorization',\n 'cookie',\n 'x-api-key',\n 'x-auth-token',\n 'set-cookie',\n];\n\n/** Redact sensitive headers */\nfunction redactHeaders(\n headers: Record<string, unknown>,\n redactList: string[]\n): Record<string, unknown> {\n const redacted: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (redactList.includes(key.toLowerCase())) {\n redacted[key] = '[REDACTED]';\n } else {\n redacted[key] = value;\n }\n }\n return redacted;\n}\n\n/** Truncate body if too large */\nfunction truncateBody(body: unknown, maxSize: number): unknown {\n if (body === undefined || body === null) return undefined;\n const str = typeof body === 'string' ? body : JSON.stringify(body);\n if (str.length > maxSize) {\n return `[TRUNCATED - ${str.length} bytes]`;\n }\n return body;\n}\n\n/** Create a configured Fastify observability plugin */\nexport function createObservabilityPlugin(\n options: FastifyPluginOptions\n): FastifyPluginAsync<Partial<FastifyPluginOptions>> {\n const plugin: FastifyPluginAsync<Partial<FastifyPluginOptions>> = async (fastify, pluginOpts) => {\n const {\n serviceName,\n logger = getGlobalLogger(),\n contextManager,\n tracingEnabled = true,\n metricsEnabled = true,\n excludeRoutes = ['/health', '/health/', '/healthz', '/ready', '/live'],\n requestLogging = {},\n } = { ...options, ...pluginOpts };\n\n // Request logging config\n const logHeaders = requestLogging.logHeaders ?? true;\n const logBody = requestLogging.logBody ?? false;\n const logResponseBody = requestLogging.logResponseBody ?? false;\n const maxBodySize = requestLogging.maxBodySize ?? 10 * 1024; // 10KB\n const headersToRedact = requestLogging.redactHeaders ?? DEFAULT_REDACT_HEADERS;\n\n const runInContext = contextManager\n ? <T>(ctx: PluginRequestContext, fn: () => T) => contextManager.run(ctx, fn)\n : <T>(_ctx: PluginRequestContext, fn: () => T) => fn();\n\n let requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']>;\n let requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']>;\n let requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']>;\n\n if (metricsEnabled) {\n const meter = getMeter(serviceName);\n requestCounter = meter.createCounter('http.server.requests.total');\n requestDuration = meter.createHistogram('http.server.request.duration', { unit: 'ms' });\n requestErrors = meter.createCounter('http.server.requests.errors');\n }\n\n // onRequest - Enrich existing OTEL span, set up context, log headers\n fastify.addHook('onRequest', (request: FastifyRequest, reply: FastifyReply, done) => {\n if (excludeRoutes.some((route) => request.url.startsWith(route))) {\n done();\n return;\n }\n\n const correlationId = (request.headers['x-request-id'] as string) || randomUUID();\n reply.header('x-request-id', correlationId);\n\n // Enrich existing OTEL auto-instrumented span instead of creating a duplicate\n let traceId = '';\n let spanId = '';\n const activeSpan = tracingEnabled ? trace.getActiveSpan() : undefined;\n\n if (activeSpan) {\n activeSpan.setAttribute('http.correlation_id', correlationId);\n\n // Name the HTTP server span with the Fastify route template. The HTTP\n // instrumentation creates the span before Fastify resolves the route, so\n // we patch in low-cardinality route identity once Fastify knows it.\n const routeTemplate = request.routeOptions?.url;\n if (routeTemplate) {\n activeSpan.setAttribute('http.route', routeTemplate);\n activeSpan.updateName(`${request.method} ${routeTemplate}`);\n }\n\n const spanContext = activeSpan.spanContext();\n traceId = spanContext.traceId;\n spanId = spanContext.spanId;\n }\n\n const requestContext: PluginRequestContext = {\n correlationId,\n traceId,\n spanId,\n startTime: Date.now(),\n };\n request.__requestContext = requestContext;\n\n runInContext(requestContext, () => {\n const logData: Record<string, unknown> = {\n correlation_id: correlationId,\n trace_id: traceId || undefined,\n span_id: spanId || undefined,\n method: request.method,\n url: request.url,\n ip: request.ip,\n userAgent: request.headers['user-agent'],\n };\n\n if (logHeaders) {\n logData.headers = redactHeaders(\n request.headers as Record<string, unknown>,\n headersToRedact\n );\n }\n\n logger.info(logData, 'Request received');\n done();\n });\n });\n\n // preHandler - Log request body (if enabled)\n if (logBody) {\n fastify.addHook('preHandler', (request: FastifyRequest, _reply: FastifyReply, done) => {\n const ctx = request.__requestContext;\n if (!ctx || !request.body) {\n done();\n return;\n }\n\n runInContext(ctx, () => {\n logger.debug(\n {\n correlation_id: ctx.correlationId,\n body: sanitizeBody(truncateBody(request.body, maxBodySize)),\n },\n 'Request body'\n );\n done();\n });\n });\n }\n\n // onSend - Log response body (if enabled)\n if (logResponseBody) {\n fastify.addHook('onSend', (request: FastifyRequest, reply: FastifyReply, payload, done) => {\n const ctx = request.__requestContext;\n if (!ctx) {\n done(null, payload);\n return;\n }\n\n runInContext(ctx, () => {\n logger.debug(\n {\n correlation_id: ctx.correlationId,\n statusCode: reply.statusCode,\n body: sanitizeBody(truncateBody(payload, maxBodySize)),\n },\n 'Response body'\n );\n done(null, payload);\n });\n });\n }\n\n // onResponse - Record metrics, enrich active span\n fastify.addHook('onResponse', (request: FastifyRequest, reply: FastifyReply, done) => {\n const ctx = request.__requestContext;\n if (!ctx) {\n done();\n return;\n }\n\n const durationMs = Date.now() - ctx.startTime;\n const route = request.routeOptions?.url || request.url;\n const labels = { method: request.method, route, status_code: String(reply.statusCode) };\n\n runInContext(ctx, () => {\n logger.info(\n {\n correlation_id: ctx.correlationId,\n method: request.method,\n statusCode: reply.statusCode,\n durationMs,\n },\n 'Request completed'\n );\n\n if (metricsEnabled) {\n requestCounter.add(1, labels);\n requestDuration.record(durationMs, labels);\n if (reply.statusCode >= 400) requestErrors.add(1, labels);\n }\n\n // Enrich the OTEL auto-instrumented span with response metadata\n if (tracingEnabled) {\n const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.setAttribute('http.response_time_ms', durationMs);\n }\n }\n\n done();\n });\n });\n\n // onError - Record exception on active span\n fastify.addHook(\n 'onError',\n (request: FastifyRequest, _reply: FastifyReply, error: Error, done) => {\n const ctx = request.__requestContext;\n if (!ctx) {\n done();\n return;\n }\n\n runInContext(ctx, () => {\n logger.error(\n {\n correlation_id: ctx.correlationId,\n method: request.method,\n url: request.url,\n error: error.message,\n },\n 'Request failed'\n );\n\n if (tracingEnabled) {\n const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n activeSpan.recordException(error);\n }\n }\n\n done();\n });\n }\n );\n };\n\n return fp(plugin, {\n name: 'neoiq-observability',\n fastify: '>=4.x',\n }) as FastifyPluginAsync<Partial<FastifyPluginOptions>>;\n}\n","/**\n * HTTP Client with Observability\n */\n\nimport axios from 'axios';\nimport type {\n AxiosInstance,\n AxiosRequestConfig,\n InternalAxiosRequestConfig,\n AxiosResponse,\n} from 'axios';\nimport axiosRetry, { IAxiosRetryConfig } from 'axios-retry';\nimport CircuitBreaker from 'opossum';\nimport { context, propagation } from '@opentelemetry/api';\nimport type { HttpClientOptions, HttpClient } from '../types';\nimport { getGlobalLogger, type Logger } from '../features/logging';\nimport { getMeter } from '../features/metrics';\n\n/** Create a configured HTTP client with full observability */\nexport function createHttpClient(options: HttpClientOptions): HttpClient {\n const {\n baseURL,\n serviceName,\n timeout = 30000,\n retry = {},\n circuitBreaker: cbOptions = {},\n headers = {},\n foundation,\n } = options;\n\n const logger: Logger = foundation?.logger || getGlobalLogger();\n const getContext = () => foundation?.context.getContext();\n const metricsEnabled = foundation?.features.metrics ?? true;\n const tracingEnabled = foundation?.features.tracing ?? true;\n\n const client = axios.create({\n baseURL,\n timeout,\n headers: { 'Content-Type': 'application/json', ...headers },\n });\n\n // Metrics\n let requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null = null;\n let requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']> | null = null;\n let requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null = null;\n\n if (metricsEnabled) {\n const meter = getMeter(`http-client-${serviceName}`);\n requestCounter = meter.createCounter('http.client.requests.total');\n requestDuration = meter.createHistogram('http.client.request.duration', { unit: 'ms' });\n requestErrors = meter.createCounter('http.client.requests.errors');\n }\n\n // Request interceptor - Add trace context\n client.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n if (tracingEnabled) {\n const carrier: Record<string, string> = {};\n propagation.inject(context.active(), carrier);\n if (carrier.traceparent) config.headers.set('traceparent', carrier.traceparent);\n if (carrier.tracestate) config.headers.set('tracestate', carrier.tracestate);\n }\n\n const reqCtx = getContext();\n if (reqCtx?.correlationId) config.headers.set('x-request-id', reqCtx.correlationId);\n\n (config as InternalAxiosRequestConfig & { __startTime: number }).__startTime = Date.now();\n\n logger.debug(\n {\n method: config.method?.toUpperCase(),\n url: `${config.baseURL || ''}${config.url}`,\n targetService: serviceName,\n },\n 'Outbound HTTP request'\n );\n\n return config;\n });\n\n // Response interceptor - Log and record metrics\n client.interceptors.response.use(\n (response: AxiosResponse) => {\n const config = response.config as InternalAxiosRequestConfig & { __startTime?: number };\n const durationMs = Date.now() - (config.__startTime || Date.now());\n const labels = {\n target_service: serviceName,\n method: config.method?.toUpperCase() || 'GET',\n status_code: String(response.status),\n };\n\n logger.debug(\n {\n method: config.method?.toUpperCase(),\n statusCode: response.status,\n durationMs,\n targetService: serviceName,\n },\n 'Outbound HTTP response'\n );\n\n if (metricsEnabled) {\n requestCounter?.add(1, labels);\n requestDuration?.record(durationMs, labels);\n }\n\n return response;\n },\n (error) => {\n const config = error.config as\n | (InternalAxiosRequestConfig & { __startTime?: number })\n | undefined;\n const durationMs = config ? Date.now() - (config.__startTime || Date.now()) : 0;\n const statusCode = error.response?.status || 0;\n const labels = {\n target_service: serviceName,\n method: config?.method?.toUpperCase() || 'GET',\n status_code: String(statusCode),\n };\n\n logger.error(\n {\n method: config?.method?.toUpperCase(),\n statusCode,\n durationMs,\n error: error.message,\n targetService: serviceName,\n },\n 'Outbound HTTP error'\n );\n\n if (metricsEnabled) {\n requestCounter?.add(1, labels);\n requestDuration?.record(durationMs, labels);\n requestErrors?.add(1, labels);\n }\n\n return Promise.reject(error);\n }\n );\n\n // Retry config (opt-out via retry.enabled = false)\n if (retry.enabled !== false) {\n const retryConfig: IAxiosRetryConfig = {\n retries: retry.retries ?? 3,\n retryDelay: (retryCount) => (retry.retryDelay ?? 1000) * Math.pow(2, retryCount - 1),\n retryCondition: (error) => {\n const method = (error.config?.method ?? 'get').toUpperCase();\n const IDEMPOTENT = new Set(['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE']);\n if (!retry.retryNonIdempotent && !IDEMPOTENT.has(method)) return false;\n\n const retryStatusCodes = retry.retryStatusCodes ?? [408, 429, 500, 502, 503, 504];\n return !error.response || retryStatusCodes.includes(error.response?.status || 0);\n },\n onRetry: (retryCount, error, requestConfig) => {\n logger.warn(\n {\n retryCount,\n url: `${requestConfig.baseURL || ''}${requestConfig.url}`,\n error: error.message,\n },\n 'Retrying request'\n );\n },\n };\n axiosRetry(client, retryConfig);\n }\n\n // Circuit breaker — opt-in via circuitBreaker.enabled = true\n if (cbOptions.enabled === true) {\n const originalRequest = client.request.bind(client);\n\n const breaker = new CircuitBreaker(\n async (config: AxiosRequestConfig) => originalRequest(config),\n {\n timeout,\n resetTimeout: cbOptions.resetTimeout ?? 30000,\n errorThresholdPercentage: cbOptions.errorThresholdPercentage ?? 50,\n volumeThreshold: 10,\n }\n );\n breaker.on('open', () => logger.warn({ targetService: serviceName }, 'Circuit breaker OPEN'));\n breaker.on('halfOpen', () =>\n logger.info({ targetService: serviceName }, 'Circuit breaker HALF-OPEN')\n );\n breaker.on('close', () =>\n logger.info({ targetService: serviceName }, 'Circuit breaker CLOSED')\n );\n\n client.request = ((config: AxiosRequestConfig) =>\n breaker.fire(config)) as typeof client.request;\n client.get = ((url: string, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'GET', url })) as typeof client.get;\n client.post = ((url: string, data?: unknown, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'POST', url, data })) as typeof client.post;\n client.put = ((url: string, data?: unknown, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'PUT', url, data })) as typeof client.put;\n client.delete = ((url: string, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'DELETE', url })) as typeof client.delete;\n client.patch = ((url: string, data?: unknown, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'PATCH', url, data })) as typeof client.patch;\n\n // Preserve direct access for edge cases\n (client as AxiosInstance & { __originalRequest: typeof originalRequest }).__originalRequest =\n originalRequest;\n }\n\n return client;\n}\n\nexport type { HttpClient, HttpClientOptions };\nexport type { AxiosInstance, AxiosRequestConfig, AxiosResponse };\n","/**\n * HashiCorp Vault client.\n *\n * Supports two auth methods:\n * - kubernetes: reads a service-account JWT and exchanges it for a Vault token\n * - token: uses VAULT_TOKEN directly (for local development)\n *\n * Reads KV v2 secrets. No in-memory cache: callers are expected to read a\n * secret at most a few times per process. If a hot read path emerges, add\n * caching at that call site or here, deliberately.\n *\n * `node-vault` is loaded dynamically so it stays an optional peer dependency.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type { VaultConfig } from '../config';\nimport type { Logger } from './logging';\nimport type { SecretsModule } from '../types';\n\ninterface NodeVaultClient {\n token: string;\n kubernetesLogin(options: {\n role: string;\n jwt: string;\n mount_point?: string;\n }): Promise<{ auth: { client_token: string; lease_duration: number } }>;\n read(path: string): Promise<{ data: { data: Record<string, string> } }>;\n}\n\ntype NodeVaultFactory = (options: {\n apiVersion: string;\n endpoint: string;\n token?: string;\n namespace?: string;\n}) => NodeVaultClient;\n\nexport interface VaultClientOptions {\n config: VaultConfig;\n logger: Logger;\n /** Inject a node-vault factory (used in tests). */\n nodeVaultFactory?: NodeVaultFactory;\n}\n\nconst DEFAULTS = {\n address: 'https://vault.beta.commerceiq.ai',\n authMethod: 'kubernetes' as const,\n mountPoint: 'secret',\n tokenPath: '/var/run/secrets/kubernetes.io/serviceaccount/token',\n k8sAuthMount: 'kubernetes',\n};\n\nfunction stripSlashes(value: string): string {\n let start = 0;\n let end = value.length;\n while (start < end && value[start] === '/') start += 1;\n while (end > start && value[end - 1] === '/') end -= 1;\n return value.slice(start, end);\n}\n\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === '/') end -= 1;\n return value.slice(0, end);\n}\n\n/**\n * Heuristic: does this error look like Vault rejecting our token vs. a\n * different failure (path missing, network, malformed payload)?\n *\n * `node-vault` surfaces Vault's \"permission denied\" / 403 / \"missing client\n * token\" messages as plain Error instances; we match on the message text.\n */\nfunction isAuthLikeError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message.toLowerCase() : '';\n return (\n msg.includes('permission denied') ||\n msg.includes('forbidden') ||\n msg.includes('missing client token') ||\n msg.includes('invalid token')\n );\n}\n\nasync function loadNodeVault(): Promise<NodeVaultFactory> {\n try {\n const mod = (await import('node-vault' as string)) as Record<string, unknown>;\n return (mod.default ?? mod) as NodeVaultFactory;\n } catch {\n throw new Error(\n 'node-vault is not installed. Install it as a peer dependency: npm install node-vault'\n );\n }\n}\n\nexport class VaultClient implements SecretsModule {\n private readonly logger: Logger;\n private readonly address: string;\n private readonly authMethod: 'kubernetes' | 'token';\n private readonly role: string | undefined;\n private readonly mountPoint: string;\n private readonly tokenPath: string;\n private readonly k8sAuthMount: string;\n private readonly namespace: string | undefined;\n\n private readonly clientPromise: Promise<NodeVaultClient>;\n private authenticated = false;\n /** In-flight auth so concurrent getSecret() calls don't all trigger kubernetesLogin. */\n private authPromise: Promise<void> | null = null;\n\n constructor(options: VaultClientOptions) {\n this.logger = options.logger;\n\n const cfg = options.config;\n this.address = stripTrailingSlashes(cfg.address ?? DEFAULTS.address);\n this.authMethod = cfg.authMethod ?? DEFAULTS.authMethod;\n this.role = cfg.role;\n this.mountPoint = stripSlashes(cfg.mountPoint ?? DEFAULTS.mountPoint);\n this.tokenPath = cfg.tokenPath ?? DEFAULTS.tokenPath;\n this.k8sAuthMount = cfg.k8sAuthMount ?? DEFAULTS.k8sAuthMount;\n this.namespace = cfg.namespace;\n\n const factory = options.nodeVaultFactory\n ? Promise.resolve(options.nodeVaultFactory)\n : loadNodeVault();\n\n this.clientPromise = factory.then((create) =>\n create({\n apiVersion: 'v1',\n endpoint: this.address,\n namespace: this.namespace,\n })\n );\n }\n\n private async authenticate(): Promise<void> {\n if (this.authenticated) return;\n if (this.authPromise) return this.authPromise;\n\n this.authPromise = this.doAuthenticate().finally(() => {\n // Allow retry on failure; on success the `authenticated` short-circuit takes over.\n if (!this.authenticated) this.authPromise = null;\n });\n return this.authPromise;\n }\n\n private async doAuthenticate(): Promise<void> {\n const client = await this.clientPromise;\n\n if (this.authMethod === 'token') {\n const token = process.env.VAULT_TOKEN;\n if (!token) {\n throw new Error('VAULT_TOKEN env var is required for token auth');\n }\n client.token = token;\n this.authenticated = true;\n this.logger.info({ authMethod: 'token' }, 'Vault authenticated via token');\n return;\n }\n\n if (!this.role) {\n throw new Error(\n 'Vault role is required for Kubernetes auth (set vault.role or VAULT_AUTH_ROLE)'\n );\n }\n\n let jwt: string;\n try {\n jwt = await readFile(this.tokenPath, 'utf8');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to read service-account token at ${this.tokenPath}: ${msg}`);\n }\n\n const result = await client.kubernetesLogin({\n role: this.role,\n jwt,\n mount_point: this.k8sAuthMount,\n });\n client.token = result.auth.client_token;\n this.authenticated = true;\n\n this.logger.info(\n {\n authMethod: 'kubernetes',\n role: this.role,\n leaseDuration: result.auth.lease_duration,\n },\n 'Vault authenticated via Kubernetes service account'\n );\n }\n\n async getSecret(path: string): Promise<Record<string, string>> {\n await this.authenticate();\n const client = await this.clientPromise;\n const fullPath = `${this.mountPoint}/data/${stripSlashes(path)}`;\n this.logger.debug({ path: fullPath }, 'Reading secret from Vault');\n\n try {\n const response = await client.read(fullPath);\n return response.data.data;\n } catch (err) {\n if (!isAuthLikeError(err)) throw err;\n // Cached token is probably expired (Vault tokens have a TTL — the\n // `authenticated` latch above doesn't know about lease expiry). Clear\n // the cached auth state and try the full login flow exactly once.\n this.logger.warn(\n { path: fullPath, error: err instanceof Error ? err.message : err },\n 'Vault read denied; clearing cached auth and re-authenticating once'\n );\n this.authenticated = false;\n this.authPromise = null;\n await this.authenticate();\n const response = await client.read(fullPath);\n return response.data.data;\n }\n }\n\n async getSecretValue(path: string, key: string): Promise<string | undefined> {\n const secrets = await this.getSecret(path);\n return secrets[key];\n }\n\n isAuthenticated(): boolean {\n return this.authenticated;\n }\n\n async checkHealth(): Promise<boolean> {\n try {\n await this.authenticate();\n return true;\n } catch {\n return false;\n }\n }\n}\n\nexport function createVaultClient(options: VaultClientOptions): VaultClient {\n return new VaultClient(options);\n}\n","/**\n * Foundation - Main Entry Point\n */\n\nimport { parseConfig, type FoundationConfig, type FoundationConfigInput } from './config';\nimport { createContextManager, type ContextManager, type RequestContext } from './features/context';\nimport { createLogger, setGlobalLogger, type Logger } from './features/logging';\nimport {\n setupTracing,\n shutdownTracing,\n getTracer,\n getActiveSpan,\n getTraceContext,\n isTracingEnabled,\n SpanStatusCode,\n type Tracer,\n} from './features/tracing';\nimport {\n createMetricReader,\n setupMetrics,\n shutdownMetrics,\n getMeter,\n isMetricsEnabled,\n type Meter,\n} from './features/metrics';\nimport {\n createObservabilityPlugin,\n type FastifyPluginOptions,\n} from './integrations/fastify-plugin';\nimport { createHttpClient as createHttpClientInternal } from './integrations/http-client';\nimport { createVaultClient } from './features/vault';\nimport type {\n FoundationInstance,\n HealthStatus,\n ComponentHealth,\n ObservabilityModule,\n HttpModule,\n LifecycleModule,\n HttpClientOptions,\n HttpClient,\n SecretsModule,\n} from './types';\n\nconst deprecationWarnings = new Set<string>();\n\nfunction warnDeprecation(oldPath: string, newPath: string, logger?: Logger): void {\n if (deprecationWarnings.has(oldPath)) return;\n deprecationWarnings.add(oldPath);\n const msg =\n `foundation.${oldPath}() is deprecated. ` +\n `Use foundation.${newPath}() instead. This alias will be removed in the next major version.`;\n if (logger) {\n logger.warn({ deprecated: oldPath, replacement: newPath }, msg);\n } else {\n console.warn(`[neoiq-foundation] DEPRECATED: ${msg}`);\n }\n}\n\n/** Create a fully configured observability foundation */\nexport function createFoundation(input: FoundationConfigInput): FoundationInstance {\n const startTime = Date.now();\n const config = parseConfig(input);\n const {\n serviceName,\n serviceVersion,\n environment,\n features: featuresConfig,\n otel,\n logging: loggingConfig,\n requestLogging: requestLoggingConfig,\n redaction: redactionConfig,\n shutdown: shutdownConfig,\n } = config;\n\n const features = {\n tracing: featuresConfig.tracing ?? true,\n metrics: featuresConfig.metrics ?? true,\n logging: featuresConfig.logging ?? true,\n };\n\n const contextManager = createContextManager();\n\n let logger: Logger;\n let loggingError: string | undefined;\n try {\n logger = createLogger({\n serviceName,\n serviceVersion,\n environment,\n level: loggingConfig.level ?? 'info',\n // Pretty-print only when stdout is an interactive terminal (local dev). In a\n // pod stdout is a pipe (isTTY undefined), so logs stay newline-delimited JSON\n // for the log collector. Keying this on environment==='development' broke log\n // ingestion for services deployed to dev/beta envs (pino-pretty emits multi-line,\n // ANSI-colored output the collector can't parse). Callers can still force it via\n // logging.prettyPrint.\n prettyPrint: loggingConfig.prettyPrint ?? process.stdout.isTTY === true,\n contextManager,\n additionalRedactPaths: redactionConfig.additionalPaths,\n });\n setGlobalLogger(logger);\n } catch (err) {\n loggingError = (err as Error).message;\n logger = {\n debug: (obj, msg) => console.log(JSON.stringify({ level: 'debug', ...obj, msg })),\n info: (obj, msg) => console.log(JSON.stringify({ level: 'info', ...obj, msg })),\n warn: (obj, msg) => console.warn(JSON.stringify({ ...obj, msg })),\n error: (obj, msg) => console.error(JSON.stringify({ ...obj, msg })),\n child: () => logger,\n pino: null as never,\n };\n console.error('[neoiq-foundation] Logger setup failed, using console:', loggingError);\n }\n\n // Metrics + tracing share one OpenTelemetry NodeSDK so there is a single global\n // MeterProvider exporting to the same gRPC collector as traces. When tracing is on\n // and metrics are on, the metric reader is built here and handed to setupTracing\n // (the NodeSDK owns it). Registering a separate MeterProvider alongside NodeSDK\n // would trigger a duplicate global registration and silently drop metrics.\n let metricReader: ReturnType<typeof createMetricReader> | undefined;\n let metricsError: string | undefined;\n if (features.metrics && features.tracing) {\n try {\n metricReader = createMetricReader({\n endpoint: otel.endpoint,\n intervalMs: otel.metricsIntervalMs,\n });\n } catch (err) {\n metricsError = (err as Error).message;\n logger.error({ error: metricsError }, 'Metrics setup failed - continuing without metrics');\n }\n }\n\n let tracerInstance: Tracer | null = null;\n let tracingError: string | undefined;\n if (features.tracing) {\n try {\n setupTracing({\n serviceName,\n serviceVersion,\n environment,\n endpoint: otel.endpoint,\n autoInstrumentation: featuresConfig.autoInstrumentation,\n metricReader,\n });\n tracerInstance = getTracer(serviceName);\n logger.info({ feature: 'tracing', endpoint: otel.endpoint }, 'Tracing enabled');\n } catch (err) {\n tracingError = (err as Error).message;\n logger.error({ error: tracingError }, 'Tracing setup failed - continuing without tracing');\n }\n }\n\n // Standalone metrics path: metrics enabled but tracing disabled, so there is no\n // NodeSDK to own the MeterProvider — set one up directly.\n if (features.metrics && !features.tracing && !metricsError) {\n try {\n setupMetrics({\n serviceName,\n serviceVersion,\n environment,\n endpoint: otel.endpoint,\n intervalMs: otel.metricsIntervalMs,\n });\n } catch (err) {\n metricsError = (err as Error).message;\n logger.error({ error: metricsError }, 'Metrics setup failed - continuing without metrics');\n }\n } else if (metricReader && tracingError && !metricsError) {\n // Unified metrics ride on the NodeSDK; if it failed to start, metrics are dead.\n metricsError = `metrics SDK (tracing) failed to start: ${tracingError}`;\n }\n\n let meterInstance: Meter | null = null;\n if (features.metrics && !metricsError) {\n meterInstance = getMeter(serviceName);\n logger.info({ feature: 'metrics', interval: `${otel.metricsIntervalMs}ms` }, 'Metrics enabled');\n }\n\n logger.info({ serviceName, serviceVersion, environment, features }, 'Foundation initialized');\n\n // Crash-flush: flush telemetry on uncaughtException/unhandledRejection before exit\n if (shutdownConfig.flushOnCrash) {\n const flushTimeoutMs = shutdownConfig.flushTimeoutMs ?? 5000;\n\n const crashFlush = (origin: string, err: unknown): void => {\n const error = err instanceof Error ? err : new Error(String(err));\n logger.error(\n { error: error.message, stack: error.stack, origin },\n 'Process crash detected, flushing telemetry'\n );\n\n const flushPromises: Promise<void>[] = [];\n if (features.tracing && isTracingEnabled()) flushPromises.push(shutdownTracing());\n if (features.metrics && isMetricsEnabled()) flushPromises.push(shutdownMetrics());\n\n const timeout = new Promise<void>((resolve) => setTimeout(resolve, flushTimeoutMs));\n Promise.race([Promise.allSettled(flushPromises), timeout]).finally(() => {\n process.exit(1);\n });\n };\n\n process.on('uncaughtException', (err) => crashFlush('uncaughtException', err));\n process.on('unhandledRejection', (reason) => crashFlush('unhandledRejection', reason));\n logger.info({ flushTimeoutMs }, 'Crash-flush handlers registered');\n }\n\n // --- Sub-module: observability ---\n const traceInSpan = async <T>(name: string, fn: () => T | Promise<T>): Promise<T> => {\n const tracer = tracerInstance || getTracer(serviceName);\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(name, async (span) => {\n try {\n const result = await fn();\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n resolve(result);\n } catch (err) {\n const error = err as Error;\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n logger.error({ span: name, error: error.message }, 'Span failed');\n reject(error);\n }\n });\n });\n };\n\n const observability: ObservabilityModule = {\n logger,\n tracer: tracerInstance,\n meter: meterInstance,\n getTracer: (name?: string) => getTracer(name || serviceName),\n getMeter: (name: string, version?: string) => getMeter(name, version),\n getTraceContext,\n getActiveSpan,\n trace: traceInSpan,\n };\n\n // --- Sub-module: http ---\n const httpModule: HttpModule = {\n createClient: (options) => createHttpClientInternal({ ...options, foundation }),\n };\n\n // --- Sub-module: secrets (Vault) ---\n let secrets: SecretsModule | null = null;\n if (config.vault?.enabled) {\n try {\n secrets = createVaultClient({ config: config.vault, logger });\n logger.info(\n { address: config.vault.address, authMethod: config.vault.authMethod },\n 'Vault secrets module initialized'\n );\n } catch (err) {\n logger.error(\n { error: (err as Error).message },\n 'Vault initialization failed - continuing without secrets'\n );\n }\n }\n\n // --- Sub-module: lifecycle ---\n const buildHealthStatus = (): HealthStatus => {\n const tracingUp = !features.tracing || (!tracingError && isTracingEnabled());\n const metricsUp = !features.metrics || (!metricsError && isMetricsEnabled());\n const loggingUp = !loggingError;\n\n const allUp = tracingUp && metricsUp && loggingUp;\n const allDown =\n (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;\n\n let status: HealthStatus['status'] = 'healthy';\n if (!allUp) status = allDown ? 'unhealthy' : 'degraded';\n\n return {\n status,\n timestamp: new Date().toISOString(),\n service: serviceName,\n version: serviceVersion,\n uptime: Math.floor((Date.now() - startTime) / 1000),\n components: {\n tracing: {\n enabled: features.tracing,\n status: !features.tracing ? 'disabled' : tracingError ? 'down' : 'up',\n message: tracingError,\n },\n metrics: {\n enabled: features.metrics,\n status: !features.metrics ? 'disabled' : metricsError ? 'down' : 'up',\n message: metricsError,\n },\n logging: {\n enabled: features.logging,\n status: loggingError ? 'down' : 'up',\n message: loggingError,\n },\n },\n };\n };\n\n const shutdownFn = async (): Promise<void> => {\n logger.info({}, 'Shutting down foundation...');\n const promises: Promise<void>[] = [];\n if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());\n if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());\n await Promise.all(promises);\n logger.info({}, 'Foundation shutdown complete');\n };\n\n const isReadyFn = (): boolean => {\n if (features.tracing && !tracingError && !isTracingEnabled()) return false;\n if (features.metrics && !metricsError && !isMetricsEnabled()) return false;\n return true;\n };\n\n const safeRunFn = async <T>(fn: () => T | Promise<T>, fallback?: T): Promise<T | undefined> => {\n try {\n return await fn();\n } catch (err) {\n const error = err as Error;\n logger.error({ error: error.message, stack: error.stack }, 'safeRun caught error');\n return fallback;\n }\n };\n\n const lifecycle: LifecycleModule = {\n health: buildHealthStatus,\n isReady: isReadyFn,\n shutdown: shutdownFn,\n safeRun: safeRunFn,\n };\n\n // --- Assemble foundation instance ---\n const foundation: FoundationInstance = {\n config,\n features,\n\n // Sub-modules\n observability,\n http: httpModule,\n lifecycle,\n secrets,\n\n // Top-level ergonomic shortcuts\n logger,\n context: contextManager,\n\n fastifyPlugin: createObservabilityPlugin({\n serviceName,\n logger,\n contextManager,\n tracingEnabled: features.tracing && !tracingError,\n metricsEnabled: features.metrics && !metricsError,\n requestLogging: requestLoggingConfig,\n }),\n\n // Backward-compat aliases (deprecated)\n tracer: tracerInstance,\n meter: meterInstance,\n\n getTracer: (name?: string) => {\n warnDeprecation('getTracer', 'observability.getTracer', logger);\n return observability.getTracer(name);\n },\n getMeter: (name: string, version?: string) => {\n warnDeprecation('getMeter', 'observability.getMeter', logger);\n return observability.getMeter(name, version);\n },\n getTraceContext: () => {\n warnDeprecation('getTraceContext', 'observability.getTraceContext', logger);\n return observability.getTraceContext();\n },\n getActiveSpan: () => {\n warnDeprecation('getActiveSpan', 'observability.getActiveSpan', logger);\n return observability.getActiveSpan();\n },\n createHttpClient: (options) => {\n warnDeprecation('createHttpClient', 'http.createClient', logger);\n return httpModule.createClient(options);\n },\n shutdown: () => {\n warnDeprecation('shutdown', 'lifecycle.shutdown', logger);\n return lifecycle.shutdown();\n },\n isReady: () => {\n warnDeprecation('isReady', 'lifecycle.isReady', logger);\n return lifecycle.isReady();\n },\n health: () => {\n warnDeprecation('health', 'lifecycle.health', logger);\n return lifecycle.health();\n },\n trace: (name, fn) => {\n warnDeprecation('trace', 'observability.trace', logger);\n return observability.trace(name, fn);\n },\n safeRun: (fn, fallback) => {\n warnDeprecation('safeRun', 'lifecycle.safeRun', logger);\n return lifecycle.safeRun(fn, fallback);\n },\n };\n\n return foundation;\n}\n\n/** Alias for createFoundation */\nexport const setupObservability = createFoundation;\n\nexport { SpanStatusCode };\nexport type { FoundationConfig, FoundationConfigInput };\nexport type { Logger };\nexport type { ContextManager, RequestContext };\nexport type { Tracer, Meter };\nexport type { FastifyPluginOptions };\nexport type {\n FoundationInstance,\n HealthStatus,\n ComponentHealth,\n ObservabilityModule,\n HttpModule,\n LifecycleModule,\n HttpClientOptions,\n HttpClient,\n SecretsModule,\n};\n","import type {\n GetObjectResult,\n HeadObjectResult,\n ListObjectsOptions,\n ListObjectsResult,\n ObjectBody,\n ObjectRef,\n PresignGetObjectOptions,\n PresignPutObjectOptions,\n PutObjectOptions,\n PutObjectResult,\n} from './types';\nimport type { ObjectStore } from './object-store';\n\n/**\n * Minimal interface for the subset of AWS S3Client we use.\n * Keeps the module free of `any` while the SDK is dynamically imported.\n */\ninterface S3ClientLike {\n send(command: unknown): Promise<Record<string, unknown>>;\n}\n\ninterface AwsS3Sdk {\n S3Client: new (options: Record<string, unknown>) => S3ClientLike;\n PutObjectCommand: new (input: Record<string, unknown>) => unknown;\n GetObjectCommand: new (input: Record<string, unknown>) => unknown;\n HeadObjectCommand: new (input: Record<string, unknown>) => unknown;\n DeleteObjectCommand: new (input: Record<string, unknown>) => unknown;\n ListObjectsV2Command: new (input: Record<string, unknown>) => unknown;\n getSignedUrl: (\n client: S3ClientLike,\n command: unknown,\n options: { expiresIn: number }\n ) => Promise<string>;\n}\n\ninterface AwsErrorLike {\n name?: string;\n message?: string;\n $metadata?: { httpStatusCode?: number };\n}\n\nfunction isAwsError(err: unknown): err is AwsErrorLike {\n return typeof err === 'object' && err !== null;\n}\n\nexport interface AwsS3ObjectStoreOptions {\n /** Provide an existing S3 client instance (advanced use). */\n client?: S3ClientLike;\n /** Options forwarded to `new S3Client(...)` when `client` isn't provided. */\n clientOptions?: Record<string, unknown>;\n}\n\nasync function loadAwsS3(): Promise<AwsS3Sdk> {\n try {\n // Dynamic import — these are optional peer deps and may not be installed\n const clientMod: Record<string, unknown> = await import('@aws-sdk/client-s3' as string);\n const presignerMod: Record<string, unknown> = await import(\n '@aws-sdk/s3-request-presigner' as string\n );\n return {\n S3Client: clientMod.S3Client as AwsS3Sdk['S3Client'],\n PutObjectCommand: clientMod.PutObjectCommand as AwsS3Sdk['PutObjectCommand'],\n GetObjectCommand: clientMod.GetObjectCommand as AwsS3Sdk['GetObjectCommand'],\n HeadObjectCommand: clientMod.HeadObjectCommand as AwsS3Sdk['HeadObjectCommand'],\n DeleteObjectCommand: clientMod.DeleteObjectCommand as AwsS3Sdk['DeleteObjectCommand'],\n ListObjectsV2Command: clientMod.ListObjectsV2Command as AwsS3Sdk['ListObjectsV2Command'],\n getSignedUrl: presignerMod.getSignedUrl as AwsS3Sdk['getSignedUrl'],\n };\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n throw new Error(\n `AWS SDK not available. Install optional peer deps: @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner. Original error: ${e.message}`\n );\n }\n}\n\n/**\n * AwsS3ObjectStore - wraps AWS S3 behind the provider-agnostic `ObjectStore` interface.\n *\n * This implementation uses dynamic imports so `neoiq-foundation-node` can be used without AWS SDK.\n */\nexport class AwsS3ObjectStore implements ObjectStore {\n public readonly provider = 'aws-s3';\n\n private readonly clientPromise: Promise<S3ClientLike>;\n private readonly awsPromise = loadAwsS3();\n\n constructor(options: AwsS3ObjectStoreOptions = {}) {\n if (options.client) {\n this.clientPromise = Promise.resolve(options.client);\n return;\n }\n\n this.clientPromise = (async () => {\n const aws = await this.awsPromise;\n return new aws.S3Client(options.clientOptions ?? {});\n })();\n }\n\n private async client(): Promise<S3ClientLike> {\n return this.clientPromise;\n }\n\n async putObject(\n ref: ObjectRef,\n body: ObjectBody,\n options: PutObjectOptions = {}\n ): Promise<PutObjectResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n const res = await s3.send(\n new aws.PutObjectCommand({\n Bucket: ref.bucket,\n Key: ref.key,\n Body: body,\n ContentType: options.contentType,\n CacheControl: options.cacheControl,\n Metadata: options.metadata,\n })\n );\n\n return {\n etag: res?.ETag as string | undefined,\n versionId: res?.VersionId as string | undefined,\n };\n }\n\n async getObject(ref: ObjectRef): Promise<GetObjectResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n const res = await s3.send(new aws.GetObjectCommand({ Bucket: ref.bucket, Key: ref.key }));\n\n if (!res?.Body) {\n const err = new Error(`S3 GetObject returned empty body: ${ref.bucket}/${ref.key}`);\n (err as Error & { code?: string }).code = 'EMPTY_BODY';\n throw err;\n }\n\n return {\n body: res.Body as GetObjectResult['body'],\n contentType: res.ContentType as string | undefined,\n contentLength: res.ContentLength as number | undefined,\n etag: res.ETag as string | undefined,\n versionId: res.VersionId as string | undefined,\n metadata: res.Metadata as Record<string, string> | undefined,\n lastModified: res.LastModified as Date | undefined,\n };\n }\n\n async headObject(ref: ObjectRef): Promise<HeadObjectResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n try {\n const res = await s3.send(new aws.HeadObjectCommand({ Bucket: ref.bucket, Key: ref.key }));\n return {\n exists: true,\n contentType: res.ContentType as string | undefined,\n contentLength: res.ContentLength as number | undefined,\n etag: res.ETag as string | undefined,\n versionId: res.VersionId as string | undefined,\n metadata: res.Metadata as Record<string, string> | undefined,\n lastModified: res.LastModified as Date | undefined,\n };\n } catch (err) {\n if (isAwsError(err)) {\n const name = String(err.name ?? '');\n if (name === 'NoSuchBucket') throw err;\n const httpStatus = err.$metadata?.httpStatusCode;\n if (httpStatus === 404 || name.includes('NotFound') || name.includes('NoSuchKey')) {\n return { exists: false };\n }\n }\n throw err;\n }\n }\n\n async deleteObject(ref: ObjectRef): Promise<void> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n await s3.send(new aws.DeleteObjectCommand({ Bucket: ref.bucket, Key: ref.key }));\n }\n\n async listObjects(options: ListObjectsOptions): Promise<ListObjectsResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n const res = await s3.send(\n new aws.ListObjectsV2Command({\n Bucket: options.bucket,\n Prefix: options.prefix,\n ContinuationToken: options.continuationToken,\n MaxKeys: options.maxKeys,\n })\n );\n\n const contents = (res?.Contents ?? []) as Array<{\n Key?: string;\n Size?: number;\n ETag?: string;\n LastModified?: Date;\n }>;\n\n return {\n objects: contents\n .filter((o): o is typeof o & { Key: string } => typeof o.Key === 'string')\n .map((o) => ({\n key: o.Key,\n size: o.Size,\n etag: o.ETag,\n lastModified: o.LastModified,\n })),\n nextContinuationToken: res?.NextContinuationToken as string | undefined,\n };\n }\n\n async presignGetObject(ref: ObjectRef, options: PresignGetObjectOptions): Promise<string> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n return aws.getSignedUrl(\n s3,\n new aws.GetObjectCommand({\n Bucket: ref.bucket,\n Key: ref.key,\n ResponseContentType: options.responseContentType,\n }),\n { expiresIn: options.expiresInSeconds }\n );\n }\n\n async presignPutObject(ref: ObjectRef, options: PresignPutObjectOptions): Promise<string> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n return aws.getSignedUrl(\n s3,\n new aws.PutObjectCommand({\n Bucket: ref.bucket,\n Key: ref.key,\n ContentType: options.contentType,\n }),\n { expiresIn: options.expiresInSeconds }\n );\n }\n}\n","import { createHash } from 'node:crypto';\nimport { Readable } from 'node:stream';\nimport type {\n GetObjectResult,\n HeadObjectResult,\n ListObjectsOptions,\n ListObjectsResult,\n ObjectBody,\n ObjectRef,\n PresignGetObjectOptions,\n PresignPutObjectOptions,\n PutObjectOptions,\n PutObjectResult,\n} from './types';\nimport type { ObjectStore } from './object-store';\n\nconst DEFAULT_MAX_OBJECTS = 10_000;\nconst STREAM_TIMEOUT_MS = 30_000;\n\ntype StoredObject = {\n body: Buffer;\n contentType?: string;\n cacheControl?: string;\n metadata?: Record<string, string>;\n etag: string;\n lastModified: Date;\n};\n\nexport interface InMemoryObjectStoreOptions {\n /** Maximum number of objects across all buckets. Throws when exceeded. */\n maxObjects?: number;\n}\n\nfunction toBuffer(body: ObjectBody): Promise<Buffer> | Buffer {\n if (typeof body === 'string') return Buffer.from(body);\n if (Buffer.isBuffer(body)) return body;\n if (body instanceof Uint8Array) return Buffer.from(body);\n\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n const timer = setTimeout(() => {\n body.destroy(new Error('InMemoryObjectStore: stream read timed out'));\n reject(new Error('InMemoryObjectStore: stream read timed out'));\n }, STREAM_TIMEOUT_MS);\n\n body.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));\n body.on('end', () => {\n clearTimeout(timer);\n resolve(Buffer.concat(chunks));\n });\n body.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\nfunction computeEtag(buf: Buffer): string {\n return `\"${createHash('md5').update(buf).digest('hex')}\"`;\n}\n\n/**\n * InMemoryObjectStore - useful for local dev, unit tests, and as a safe default.\n * NOTE: presign* methods are not meaningful here and will throw.\n */\nexport class InMemoryObjectStore implements ObjectStore {\n public readonly provider = 'in-memory';\n\n private readonly buckets = new Map<string, Map<string, StoredObject>>();\n private readonly maxObjects: number;\n private objectCount = 0;\n\n constructor(options: InMemoryObjectStoreOptions = {}) {\n this.maxObjects = options.maxObjects ?? DEFAULT_MAX_OBJECTS;\n }\n\n private bucketMap(bucket: string): Map<string, StoredObject> {\n let m = this.buckets.get(bucket);\n if (!m) {\n m = new Map();\n this.buckets.set(bucket, m);\n }\n return m;\n }\n\n async putObject(\n ref: ObjectRef,\n body: ObjectBody,\n options: PutObjectOptions = {}\n ): Promise<PutObjectResult> {\n const buf = await toBuffer(body);\n const map = this.bucketMap(ref.bucket);\n const isNew = !map.has(ref.key);\n\n if (isNew && this.objectCount >= this.maxObjects) {\n throw new Error(`InMemoryObjectStore: max object limit reached (${this.maxObjects})`);\n }\n\n const obj: StoredObject = {\n body: buf,\n contentType: options.contentType,\n cacheControl: options.cacheControl,\n metadata: options.metadata,\n etag: computeEtag(buf),\n lastModified: new Date(),\n };\n map.set(ref.key, obj);\n if (isNew) this.objectCount++;\n return { etag: obj.etag };\n }\n\n async getObject(ref: ObjectRef): Promise<GetObjectResult> {\n const obj = this.bucketMap(ref.bucket).get(ref.key);\n if (!obj) {\n const err = new Error(`Object not found: ${ref.bucket}/${ref.key}`);\n (err as Error & { code?: string }).code = 'OBJECT_NOT_FOUND';\n throw err;\n }\n return {\n body: Readable.from(obj.body),\n contentType: obj.contentType,\n contentLength: obj.body.length,\n etag: obj.etag,\n metadata: obj.metadata,\n lastModified: obj.lastModified,\n };\n }\n\n async headObject(ref: ObjectRef): Promise<HeadObjectResult> {\n const obj = this.bucketMap(ref.bucket).get(ref.key);\n if (!obj) return { exists: false };\n return {\n exists: true,\n contentType: obj.contentType,\n contentLength: obj.body.length,\n etag: obj.etag,\n metadata: obj.metadata,\n lastModified: obj.lastModified,\n };\n }\n\n async deleteObject(ref: ObjectRef): Promise<void> {\n const map = this.bucketMap(ref.bucket);\n if (map.delete(ref.key)) {\n this.objectCount--;\n }\n }\n\n async listObjects(options: ListObjectsOptions): Promise<ListObjectsResult> {\n const { bucket, prefix = '', continuationToken } = options;\n const maxKeys = Math.max(1, Math.floor(options.maxKeys ?? 1000));\n const map = this.bucketMap(bucket);\n const all = [...map.entries()]\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, obj]) => ({\n key,\n size: obj.body.length,\n etag: obj.etag,\n lastModified: obj.lastModified,\n }))\n .sort((a, b) => a.key.localeCompare(b.key));\n\n let startIndex = 0;\n if (continuationToken) {\n const idx = all.findIndex((o) => o.key === continuationToken);\n if (idx === -1) {\n throw new Error(`InMemoryObjectStore: invalid continuationToken \"${continuationToken}\"`);\n }\n startIndex = idx + 1;\n }\n\n const page = all.slice(startIndex, startIndex + maxKeys);\n const hasMore = startIndex + maxKeys < all.length;\n\n return {\n objects: page,\n nextContinuationToken: hasMore ? page[page.length - 1]?.key : undefined,\n };\n }\n\n async presignGetObject(_ref: ObjectRef, _options: PresignGetObjectOptions): Promise<string> {\n throw new Error('InMemoryObjectStore does not support presigned URLs');\n }\n\n async presignPutObject(_ref: ObjectRef, _options: PresignPutObjectOptions): Promise<string> {\n throw new Error('InMemoryObjectStore does not support presigned URLs');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA0CA,MAAM,0BAA0B;AAEhC,SAAS,wBAAwBA,eAAoC;CACnE,MAAM,iBAAiB,cAAY,WAAW,UAAY,QAAQ,CAAC;CACnE,MAAM,UAAU,CAAC,kBAAkB,cAAY,eAAe,EAAE,SAC9D,yBACA,EAAE,OAAO,cAAe,EACzB;AACD,QAAO,cAAY,WAAW,UAAY,QAAQ,EAAE,QAAQ;AAC7D;AAED,SAAS,0BAA8C;CACrD,MAAM,UAAU,cAAY,WAAW,UAAY,QAAQ,CAAC;AAC5D,QAAO,SAAS,SAAS,wBAAwB,EAAE;AACpD;;AAGD,SAAgB,uBAAuC;CACrD,MAAM,MAAM,IAAI;AAEhB,QAAO;EACL,aAAyC;GACvC,MAAM,SAAS,IAAI,UAAU;AAC7B,QAAK,OAAQ;GAGb,MAAM,uBAAuB,yBAAyB;AACtD,OAAI,wBAAwB,yBAAyB,OAAO,cAC1D,QAAO;IAAE,GAAG;IAAQ,eAAe;GAAsB;AAE3D,UAAO;EACR;EAED,IAAOC,WAAyBC,IAAgB;AAE9C,OAAI,UAAQ,eAAe;IACzB,MAAM,UAAU,wBAAwB,UAAQ,cAAc;AAC9D,WAAO,UAAY,KAAK,SAAS,MAAM,IAAI,IAAI,WAAS,GAAG,CAAC;GAC7D;AACD,UAAO,IAAI,IAAI,WAAS,GAAG;EAC5B;EAED,IAAoCC,KAAuC;AACzE,OAAI,QAAQ,gBACV,QAAQ,yBAAyB,IAAI,IAAI,UAAU,EAAE;AAEvD,UAAO,IAAI,UAAU,GAAG;EACzB;EAED,OAAOC,SAA8D;GACnE,MAAM,UAAU,IAAI,UAAU;AAC9B,QAAK,QAAS;AACd,OAAI,QAAQ,cACV,yBAAwB,QAAQ,cAAc;AAEhD,UAAO,OAAO,SAAS,QAAQ;AAC/B,UAAO;EACR;EAED,gBAAgBC,KAAaC,OAAsB;GACjD,MAAM,UAAU,IAAI,UAAU;AAC9B,QAAK,QAAS;AACd,QAAK,QAAQ,YAAa,SAAQ,cAAc,CAAE;AAClD,WAAQ,YAAY,OAAO;EAC5B;EAED,gBAAgBD,KAAsB;AACpC,UAAO,IAAI,UAAU,EAAE,cAAc;EACtC;EAED,iBAA0C;AACxC,UAAO,IAAI,UAAU,EAAE,eAAe,CAAE;EACzC;EAED,eAAeE,MAAqC;GAClD,MAAM,UAAU,IAAI,UAAU;AAC9B,QAAK,QAAS;AACd,WAAQ,cAAc;IAAE,GAAI,QAAQ,eAAe,CAAE;IAAG,GAAG;GAAM;EAClE;EAED,mBAAyB;GACvB,MAAM,UAAU,IAAI,UAAU;AAC9B,QAAK,QAAS;AACd,WAAQ,cAAc,CAAE;EACzB;CACF;AACF;;;;;;;;;;;;;ACtHD,MAAM,cAAc;;;;;AAMpB,MAAaC,eAAsC;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;AAMD,MAAMC,iBAAoE;CACxE;EAAE,OAAO;EAAO,SAAS;CAAkE;CAC3F;EAAE,OAAO;EAAkB,SAAS;CAAwD;CAC5F;EAAE,OAAO;EAAc,SAAS;CAAwC;CACxE;EAAE,OAAO;EAAc,SAAS;CAA2B;CAC3D;EAAE,OAAO;EAAgB,SAAS;CAA6B;CAC/D;EAAE,OAAO;EAAmB,SAAS;CAAkD;CACvF;EAAE,OAAO;EAAqB,SAAS;CAA6C;CACpF;EAAE,OAAO;EAAgB,SAAS;CAAuB;AAC1D;AAED,MAAMC,iBAAsC,IAAI,IAC9C,aAAa,OAAO,CAAC,OAAO,EAAE,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC;AAG1E,MAAM,YAAY;AAClB,MAAM,WAAW;AAEjB,SAAS,KAAKC,OAAwB;AACpC,YAAW,UAAU,YAAY,MAAM,UAAU,EAAG,QAAO;AAC3D,SAAQ,EAAE,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,IAAI,OAAO,KAAK,IAAI,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,EAAE,MAAM,MAAA,GAAS,CAAC;AAC5F;AAED,SAAS,iBAAiBC,OAAwB;AAChD,QAAO,eAAe,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,MAAM,CAAC;AACzD;AAED,SAAS,cAAcD,OAAgBE,OAAwB;AAC7D,KAAI,QAAQ,UAAW,QAAO;AAC9B,YAAW,UAAU,YAAY,iBAAiB,MAAM,CAAE,QAAO,KAAK,MAAM;AAC5E,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,IAAI,CAAC,SAAS,cAAc,MAAM,QAAQ,EAAE,CAAC;AACpF,KAAI,UAAU,eAAe,UAAU,SACrC,QAAO,eAAe,OAAkC,QAAQ,EAAE;AAEpE,QAAO;AACR;AAED,SAAS,eAAeC,KAA8BD,OAAwC;AAC5F,KAAI,QAAQ,UAAW,QAAO;CAC9B,MAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,KAAI,KAAK,SAAS,SAAU,QAAO;CAEnC,MAAME,SAAkC,CAAE;AAC1C,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,IAAI;AAClB,MAAI,eAAe,IAAI,IAAI,aAAa,CAAC,CACvC,QAAO,cAAc,UAAU,WAAW,KAAK,MAAM,GAAG;MAExD,QAAO,OAAO,cAAc,OAAO,MAAM;CAE5C;AACD,QAAO;AACR;;;;;;AAOD,SAAgB,aAAgBC,MAAY;AAC1C,KAAI,SAAS,QAAQ,gBAAoB,QAAO;AAChD,YAAW,SAAS,SAClB,QAAQ,iBAAiB,KAAK,GAAG,KAAK,KAAK,GAAG;AAEhD,YAAW,SAAS,SAAU,QAAO;AACrC,KAAI,MAAM,QAAQ,KAAK,CAAE,QAAO,KAAK,IAAI,CAAC,SAAS,cAAc,MAAM,EAAE,CAAC;AAC1E,QAAO,eAAe,MAAiC,EAAE;AAC1D;;AAGD,SAAgB,sBAAsBC,kBAA4B,CAAE,GAGlE;AACA,QAAO;EACL,OAAO,CAAC,GAAG,cAAc,GAAG,eAAgB;EAC5C,QAAQ;CACT;AACF;;;;;ACnID,SAAgB,aAAaC,SAAgC;CAC3D,MAAM,EACJ,aACA,gBACA,aACA,OACA,aACA,gBACA,wBAAwB,CAAE,GAC3B,GAAG;CAEJ,MAAM,aAAa,KAAK;EACtB;EACA,QAAQ,sBAAsB,sBAAsB;EACpD,MAAM;GAAE,SAAS;GAAa,SAAS;GAAgB,KAAK;EAAa;EACzE,OAAO,MAAM;GACX,MAAM,OAAO,QAAM,eAAe;GAClC,MAAM,cAAc,MAAM,aAAa;GACvC,MAAM,MAAM,gBAAgB,YAAY;GAExC,MAAM,UAAU,aAAa,WAAW,KAAK;GAC7C,MAAM,SAAS,aAAa,UAAU,KAAK;GAC3C,MAAM,gBAAgB,KAAK;GAC3B,MAAM,cAAc,KAAK;GAEzB,MAAMC,SAAkC;IACtC,UAAU;IACV,SAAS;IACT,gBAAgB;GACjB;AAED,OAAI,eAAe,OAAO,KAAK,YAAY,CAAC,SAAS,EACnD,QAAO,UAAU;AAGnB,UAAO;EACR;EACD,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,MAAO,GAAG;EACpD,WAAW,cACP;GACE,QAAQ;GACR,SAAS;IAAE,UAAU;IAAM,eAAe;IAAgB,QAAQ;GAAgB;EACnF;CAEN,EAAC;AAEF,QAAO,eAAe,WAAW;AAClC;AAED,SAAS,eAAeC,YAAgC;AACtD,QAAO;EACL,OAAO,CAAC,KAAK,QAAQ,WAAW,MAAM,KAAK,IAAI;EAC/C,MAAM,CAAC,KAAK,QAAQ,WAAW,KAAK,KAAK,IAAI;EAC7C,MAAM,CAAC,KAAK,QAAQ,WAAW,KAAK,KAAK,IAAI;EAC7C,OAAO,CAAC,KAAK,QAAQ,WAAW,MAAM,KAAK,IAAI;EAC/C,OAAO,CAAC,aAAa,eAAe,WAAW,MAAM,SAAS,CAAC;EAC/D,MAAM;CACP;AACF;;AAGD,SAAgB,qBACdC,cAAsB,WACtBC,eAAuB,CAAE,GACjB;CACR,MAAM,MAAM,CAACC,OAAeC,KAAaC,QAAuB;EAC9D,MAAM,SAAS;GACb,WAAW,IAAI,OAAO,aAAa;GACnC;GACA,SAAS;GACT,GAAG;GACH,GAAG;GACH;EACD;EACD,MAAM,UAAU,KAAK,UAAU,OAAO;AACtC,MAAI,UAAU,OAAQ,SAAQ,KAAK,QAAQ;WAClC,UAAU,QAAS,SAAQ,MAAM,QAAQ;MAC7C,SAAQ,IAAI,QAAQ;CAC1B;CAED,MAAMC,WAAmB;EACvB,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI;EAC3C,MAAM,CAAC,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;EACzC,MAAM,CAAC,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;EACzC,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI;EAC3C,OAAO,CAAC,aAAa,qBAAqB,aAAa;GAAE,GAAG;GAAc,GAAG;EAAU,EAAC;EACxF,MAAM;CACP;AAED,QAAO;AACR;AAGD,IAAIC,eAA8B;AAElC,SAAgB,gBAAgBC,QAAsB;AACpD,gBAAe;AAChB;AAED,SAAgB,kBAA0B;AACxC,QAAO,gBAAgB,sBAAsB;AAC9C;;;;AC3GD,IAAIC,gBAAsC;AAC1C,IAAI,gBAAgB;;;;;;;;;;;;AAapB,SAAgB,mBACdC,UAA2D,CAAE,GAC9C;CACf,MAAM,EAAE,WAAW,wBAAwB,EAAE,aAAa,KAAM,GAAG;CACnE,MAAM,WAAW,IAAI,mBAAmB,EAAE,KAAK,SAAU;CACzD,MAAM,SAAS,IAAI,8BAA8B;EAC/C;EACA,sBAAsB;CACvB;AACD,iBAAgB;AAChB,QAAO;AACR;;;;;;;;AASD,SAAgB,aAAaC,SAA+B;AAC1D,KAAI,eAAe;AACjB,UAAQ,KAAK,iDAAiD;AAC9D;CACD;CAED,MAAM,EACJ,aACA,gBACA,aACA,WAAW,wBAAwB,EACnC,aAAa,KACd,GAAG;CAEJ,MAAM,WAAW,uBAAuB;GACrC,oBAAoB;GACpB,uBAAuB;EACxB,0BAA0B;CAC3B,EAAC;CAEF,MAAM,iBAAiB,IAAI,mBAAmB,EAAE,KAAK,SAAU;CAC/D,MAAM,eAAe,IAAI,8BAA8B;EACrD,UAAU;EACV,sBAAsB;CACvB;AAED,iBAAgB,IAAI,cAAc;EAAE;EAAU,SAAS,CAAC,YAAa;CAAE;AACvE,SAAQ,uBAAuB,cAAc;AAC7C,iBAAgB;AACjB;;AAGD,eAAsB,kBAAiC;AACrD,MAAK,cAAe;AACpB,KAAI;AACF,QAAM,cAAc,UAAU;AAC9B,kBAAgB;AAChB,kBAAgB;CACjB,SAAQ,OAAO;AACd,UAAQ,MAAM,mDAAmD,MAAM;AACvE,QAAM;CACP;AACF;AAED,SAAgB,SAASC,MAAcC,UAAkB,SAAgB;AACvE,QAAO,QAAQ,SAAS,MAAM,QAAQ;AACvC;AAED,SAAgB,mBAA4B;AAC1C,QAAO;AACR;;;;AC5DD,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;AACD;;AAGD,SAAS,cACPC,SACAC,YACyB;CACzB,MAAMC,WAAoC,CAAE;AAC5C,MAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,CAChD,KAAI,WAAW,SAAS,IAAI,aAAa,CAAC,CACxC,UAAS,OAAO;KAEhB,UAAS,OAAO;AAGpB,QAAO;AACR;;AAGD,SAAS,aAAaC,MAAeC,SAA0B;AAC7D,KAAI,mBAAsB,SAAS,KAAM;CACzC,MAAM,aAAa,SAAS,WAAW,OAAO,KAAK,UAAU,KAAK;AAClE,KAAI,IAAI,SAAS,QACf,SAAQ,eAAe,IAAI,OAAO;AAEpC,QAAO;AACR;;AAGD,SAAgB,0BACdC,SACmD;CACnD,MAAMC,SAA4D,OAAO,SAAS,eAAe;EAC/F,MAAM,EACJ,aACA,SAAS,iBAAiB,EAC1B,gBACA,iBAAiB,MACjB,iBAAiB,MACjB,gBAAgB;GAAC;GAAW;GAAY;GAAY;GAAU;EAAQ,GACtE,iBAAiB,CAAE,GACpB,GAAG;GAAE,GAAG;GAAS,GAAG;EAAY;EAGjC,MAAM,aAAa,eAAe,cAAc;EAChD,MAAM,UAAU,eAAe,WAAW;EAC1C,MAAM,kBAAkB,eAAe,mBAAmB;EAC1D,MAAM,cAAc,eAAe,eAAe,KAAK;EACvD,MAAM,kBAAkB,eAAe,iBAAiB;EAExD,MAAM,eAAe,iBACjB,CAAIC,KAA2BC,OAAgB,eAAe,IAAI,KAAK,GAAG,GAC1E,CAAIC,MAA4BD,OAAgB,IAAI;EAExD,IAAIE;EACJ,IAAIC;EACJ,IAAIC;AAEJ,MAAI,gBAAgB;GAClB,MAAM,QAAQ,SAAS,YAAY;AACnC,oBAAiB,MAAM,cAAc,6BAA6B;AAClE,qBAAkB,MAAM,gBAAgB,gCAAgC,EAAE,MAAM,KAAM,EAAC;AACvF,mBAAgB,MAAM,cAAc,8BAA8B;EACnE;AAGD,UAAQ,QAAQ,aAAa,CAACC,SAAyBC,OAAqB,SAAS;AACnF,OAAI,cAAc,KAAK,CAAC,UAAU,QAAQ,IAAI,WAAW,MAAM,CAAC,EAAE;AAChE,UAAM;AACN;GACD;GAED,MAAM,gBAAiB,QAAQ,QAAQ,mBAA8B,YAAY;AACjF,SAAM,OAAO,gBAAgB,cAAc;GAG3C,IAAI,UAAU;GACd,IAAI,SAAS;GACb,MAAM,aAAa,iBAAiB,QAAM,eAAe;AAEzD,OAAI,YAAY;AACd,eAAW,aAAa,uBAAuB,cAAc;IAK7D,MAAM,gBAAgB,QAAQ,cAAc;AAC5C,QAAI,eAAe;AACjB,gBAAW,aAAa,cAAc,cAAc;AACpD,gBAAW,YAAY,EAAE,QAAQ,OAAO,GAAG,cAAc,EAAE;IAC5D;IAED,MAAM,cAAc,WAAW,aAAa;AAC5C,cAAU,YAAY;AACtB,aAAS,YAAY;GACtB;GAED,MAAMC,iBAAuC;IAC3C;IACA;IACA;IACA,WAAW,KAAK,KAAK;GACtB;AACD,WAAQ,mBAAmB;AAE3B,gBAAa,gBAAgB,MAAM;IACjC,MAAMC,UAAmC;KACvC,gBAAgB;KAChB,UAAU;KACV,SAAS;KACT,QAAQ,QAAQ;KAChB,KAAK,QAAQ;KACb,IAAI,QAAQ;KACZ,WAAW,QAAQ,QAAQ;IAC5B;AAED,QAAI,WACF,SAAQ,UAAU,cAChB,QAAQ,SACR,gBACD;AAGH,WAAO,KAAK,SAAS,mBAAmB;AACxC,UAAM;GACP,EAAC;EACH,EAAC;AAGF,MAAI,QACF,SAAQ,QAAQ,cAAc,CAACH,SAAyBI,QAAsB,SAAS;GACrF,MAAM,MAAM,QAAQ;AACpB,QAAK,QAAQ,QAAQ,MAAM;AACzB,UAAM;AACN;GACD;AAED,gBAAa,KAAK,MAAM;AACtB,WAAO,MACL;KACE,gBAAgB,IAAI;KACpB,MAAM,aAAa,aAAa,QAAQ,MAAM,YAAY,CAAC;IAC5D,GACD,eACD;AACD,UAAM;GACP,EAAC;EACH,EAAC;AAIJ,MAAI,gBACF,SAAQ,QAAQ,UAAU,CAACJ,SAAyBC,OAAqB,SAAS,SAAS;GACzF,MAAM,MAAM,QAAQ;AACpB,QAAK,KAAK;AACR,SAAK,MAAM,QAAQ;AACnB;GACD;AAED,gBAAa,KAAK,MAAM;AACtB,WAAO,MACL;KACE,gBAAgB,IAAI;KACpB,YAAY,MAAM;KAClB,MAAM,aAAa,aAAa,SAAS,YAAY,CAAC;IACvD,GACD,gBACD;AACD,SAAK,MAAM,QAAQ;GACpB,EAAC;EACH,EAAC;AAIJ,UAAQ,QAAQ,cAAc,CAACD,SAAyBC,OAAqB,SAAS;GACpF,MAAM,MAAM,QAAQ;AACpB,QAAK,KAAK;AACR,UAAM;AACN;GACD;GAED,MAAM,aAAa,KAAK,KAAK,GAAG,IAAI;GACpC,MAAM,QAAQ,QAAQ,cAAc,OAAO,QAAQ;GACnD,MAAM,SAAS;IAAE,QAAQ,QAAQ;IAAQ;IAAO,aAAa,OAAO,MAAM,WAAW;GAAE;AAEvF,gBAAa,KAAK,MAAM;AACtB,WAAO,KACL;KACE,gBAAgB,IAAI;KACpB,QAAQ,QAAQ;KAChB,YAAY,MAAM;KAClB;IACD,GACD,oBACD;AAED,QAAI,gBAAgB;AAClB,oBAAe,IAAI,GAAG,OAAO;AAC7B,qBAAgB,OAAO,YAAY,OAAO;AAC1C,SAAI,MAAM,cAAc,IAAK,eAAc,IAAI,GAAG,OAAO;IAC1D;AAGD,QAAI,gBAAgB;KAClB,MAAM,aAAa,QAAM,eAAe;AACxC,SAAI,WACF,YAAW,aAAa,yBAAyB,WAAW;IAE/D;AAED,UAAM;GACP,EAAC;EACH,EAAC;AAGF,UAAQ,QACN,WACA,CAACD,SAAyBI,QAAsBC,OAAc,SAAS;GACrE,MAAM,MAAM,QAAQ;AACpB,QAAK,KAAK;AACR,UAAM;AACN;GACD;AAED,gBAAa,KAAK,MAAM;AACtB,WAAO,MACL;KACE,gBAAgB,IAAI;KACpB,QAAQ,QAAQ;KAChB,KAAK,QAAQ;KACb,OAAO,MAAM;IACd,GACD,iBACD;AAED,QAAI,gBAAgB;KAClB,MAAM,aAAa,QAAM,eAAe;AACxC,SAAI,YAAY;AACd,iBAAW,UAAU;OAAE,MAAM,iBAAe;OAAO,SAAS,MAAM;MAAS,EAAC;AAC5E,iBAAW,gBAAgB,MAAM;KAClC;IACF;AAED,UAAM;GACP,EAAC;EACH,EACF;CACF;AAED,QAAO,GAAG,QAAQ;EAChB,MAAM;EACN,SAAS;CACV,EAAC;AACH;;;;;AC/RD,SAAgB,iBAAiBC,SAAwC;CACvE,MAAM,EACJ,SACA,aACA,UAAU,KACV,QAAQ,CAAE,GACV,gBAAgB,YAAY,CAAE,GAC9B,UAAU,CAAE,GACZ,YACD,GAAG;CAEJ,MAAMC,SAAiB,YAAY,UAAU,iBAAiB;CAC9D,MAAM,aAAa,MAAM,YAAY,QAAQ,YAAY;CACzD,MAAM,iBAAiB,YAAY,SAAS,WAAW;CACvD,MAAM,iBAAiB,YAAY,SAAS,WAAW;CAEvD,MAAM,SAAS,MAAM,OAAO;EAC1B;EACA;EACA,SAAS;GAAE,gBAAgB;GAAoB,GAAG;EAAS;CAC5D,EAAC;CAGF,IAAIC,iBAAkF;CACtF,IAAIC,kBAAqF;CACzF,IAAIC,gBAAiF;AAErF,KAAI,gBAAgB;EAClB,MAAM,QAAQ,UAAU,cAAc,YAAY,EAAE;AACpD,mBAAiB,MAAM,cAAc,6BAA6B;AAClE,oBAAkB,MAAM,gBAAgB,gCAAgC,EAAE,MAAM,KAAM,EAAC;AACvF,kBAAgB,MAAM,cAAc,8BAA8B;CACnE;AAGD,QAAO,aAAa,QAAQ,IAAI,CAACC,WAAuC;AACtE,MAAI,gBAAgB;GAClB,MAAMC,UAAkC,CAAE;AAC1C,iBAAY,OAAO,UAAQ,QAAQ,EAAE,QAAQ;AAC7C,OAAI,QAAQ,YAAa,QAAO,QAAQ,IAAI,eAAe,QAAQ,YAAY;AAC/E,OAAI,QAAQ,WAAY,QAAO,QAAQ,IAAI,cAAc,QAAQ,WAAW;EAC7E;EAED,MAAM,SAAS,YAAY;AAC3B,MAAI,QAAQ,cAAe,QAAO,QAAQ,IAAI,gBAAgB,OAAO,cAAc;AAElF,SAAgE,cAAc,KAAK,KAAK;AAEzF,SAAO,MACL;GACE,QAAQ,OAAO,QAAQ,aAAa;GACpC,MAAM,EAAE,OAAO,WAAW,GAAG,EAAE,OAAO,IAAI;GAC1C,eAAe;EAChB,GACD,wBACD;AAED,SAAO;CACR,EAAC;AAGF,QAAO,aAAa,SAAS,IAC3B,CAACC,aAA4B;EAC3B,MAAM,SAAS,SAAS;EACxB,MAAM,aAAa,KAAK,KAAK,IAAI,OAAO,eAAe,KAAK,KAAK;EACjE,MAAM,SAAS;GACb,gBAAgB;GAChB,QAAQ,OAAO,QAAQ,aAAa,IAAI;GACxC,aAAa,OAAO,SAAS,OAAO;EACrC;AAED,SAAO,MACL;GACE,QAAQ,OAAO,QAAQ,aAAa;GACpC,YAAY,SAAS;GACrB;GACA,eAAe;EAChB,GACD,yBACD;AAED,MAAI,gBAAgB;AAClB,mBAAgB,IAAI,GAAG,OAAO;AAC9B,oBAAiB,OAAO,YAAY,OAAO;EAC5C;AAED,SAAO;CACR,GACD,CAAC,UAAU;EACT,MAAM,SAAS,MAAM;EAGrB,MAAM,aAAa,SAAS,KAAK,KAAK,IAAI,OAAO,eAAe,KAAK,KAAK,IAAI;EAC9E,MAAM,aAAa,MAAM,UAAU,UAAU;EAC7C,MAAM,SAAS;GACb,gBAAgB;GAChB,QAAQ,QAAQ,QAAQ,aAAa,IAAI;GACzC,aAAa,OAAO,WAAW;EAChC;AAED,SAAO,MACL;GACE,QAAQ,QAAQ,QAAQ,aAAa;GACrC;GACA;GACA,OAAO,MAAM;GACb,eAAe;EAChB,GACD,sBACD;AAED,MAAI,gBAAgB;AAClB,mBAAgB,IAAI,GAAG,OAAO;AAC9B,oBAAiB,OAAO,YAAY,OAAO;AAC3C,kBAAe,IAAI,GAAG,OAAO;EAC9B;AAED,SAAO,QAAQ,OAAO,MAAM;CAC7B,EACF;AAGD,KAAI,MAAM,YAAY,OAAO;EAC3B,MAAMC,cAAiC;GACrC,SAAS,MAAM,WAAW;GAC1B,YAAY,CAAC,gBAAgB,MAAM,cAAc,OAAQ,KAAK,IAAI,GAAG,aAAa,EAAE;GACpF,gBAAgB,CAAC,UAAU;IACzB,MAAM,SAAS,CAAC,MAAM,QAAQ,UAAU,OAAO,aAAa;IAC5D,MAAM,aAAa,IAAI,IAAI;KAAC;KAAO;KAAQ;KAAW;KAAO;IAAS;AACtE,SAAK,MAAM,uBAAuB,WAAW,IAAI,OAAO,CAAE,QAAO;IAEjE,MAAM,mBAAmB,MAAM,oBAAoB;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;IAAI;AACjF,YAAQ,MAAM,YAAY,iBAAiB,SAAS,MAAM,UAAU,UAAU,EAAE;GACjF;GACD,SAAS,CAAC,YAAY,OAAO,kBAAkB;AAC7C,WAAO,KACL;KACE;KACA,MAAM,EAAE,cAAc,WAAW,GAAG,EAAE,cAAc,IAAI;KACxD,OAAO,MAAM;IACd,GACD,mBACD;GACF;EACF;AACD,aAAW,QAAQ,YAAY;CAChC;AAGD,KAAI,UAAU,YAAY,MAAM;EAC9B,MAAM,kBAAkB,OAAO,QAAQ,KAAK,OAAO;EAEnD,MAAM,UAAU,IAAI,eAClB,OAAOC,WAA+B,gBAAgB,OAAO,EAC7D;GACE;GACA,cAAc,UAAU,gBAAgB;GACxC,0BAA0B,UAAU,4BAA4B;GAChE,iBAAiB;EAClB;AAEH,UAAQ,GAAG,QAAQ,MAAM,OAAO,KAAK,EAAE,eAAe,YAAa,GAAE,uBAAuB,CAAC;AAC7F,UAAQ,GAAG,YAAY,MACrB,OAAO,KAAK,EAAE,eAAe,YAAa,GAAE,4BAA4B,CACzE;AACD,UAAQ,GAAG,SAAS,MAClB,OAAO,KAAK,EAAE,eAAe,YAAa,GAAE,yBAAyB,CACtE;AAED,SAAO,UAAW,CAACA,WACjB,QAAQ,KAAK,OAAO;AACtB,SAAO,MAAO,CAACC,KAAaC,WAC1B,QAAQ,KAAK;GAAE,GAAG;GAAQ,QAAQ;GAAO;EAAK,EAAC;AACjD,SAAO,OAAQ,CAACD,KAAaE,MAAgBD,WAC3C,QAAQ,KAAK;GAAE,GAAG;GAAQ,QAAQ;GAAQ;GAAK;EAAM,EAAC;AACxD,SAAO,MAAO,CAACD,KAAaE,MAAgBD,WAC1C,QAAQ,KAAK;GAAE,GAAG;GAAQ,QAAQ;GAAO;GAAK;EAAM,EAAC;AACvD,SAAO,SAAU,CAACD,KAAaC,WAC7B,QAAQ,KAAK;GAAE,GAAG;GAAQ,QAAQ;GAAU;EAAK,EAAC;AACpD,SAAO,QAAS,CAACD,KAAaE,MAAgBD,WAC5C,QAAQ,KAAK;GAAE,GAAG;GAAQ,QAAQ;GAAS;GAAK;EAAM,EAAC;AAGxD,SAAyE,oBACxE;CACH;AAED,QAAO;AACR;;;;ACpKD,MAAM,WAAW;CACf,SAAS;CACT,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,cAAc;AACf;AAED,SAAS,aAAaE,OAAuB;CAC3C,IAAI,QAAQ;CACZ,IAAI,MAAM,MAAM;AAChB,QAAO,QAAQ,OAAO,MAAM,WAAW,IAAK,UAAS;AACrD,QAAO,MAAM,SAAS,MAAM,MAAM,OAAO,IAAK,QAAO;AACrD,QAAO,MAAM,MAAM,OAAO,IAAI;AAC/B;AAED,SAAS,qBAAqBA,OAAuB;CACnD,IAAI,MAAM,MAAM;AAChB,QAAO,MAAM,KAAK,MAAM,MAAM,OAAO,IAAK,QAAO;AACjD,QAAO,MAAM,MAAM,GAAG,IAAI;AAC3B;;;;;;;;AASD,SAAS,gBAAgBC,KAAuB;CAC9C,MAAM,MAAM,eAAe,QAAQ,IAAI,QAAQ,aAAa,GAAG;AAC/D,QACE,IAAI,SAAS,oBAAoB,IACjC,IAAI,SAAS,YAAY,IACzB,IAAI,SAAS,uBAAuB,IACpC,IAAI,SAAS,gBAAgB;AAEhC;AAED,eAAe,gBAA2C;AACxD,KAAI;EACF,MAAM,MAAO,MAAM,OAAO;AAC1B,SAAQ,IAAI,WAAW;CACxB,QAAO;AACN,QAAM,IAAI,MACR;CAEH;AACF;AAED,IAAa,cAAb,MAAkD;CAChD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA,gBAAwB;;CAExB,cAA4C;CAE5C,YAAYC,SAA6B;AACvC,OAAK,SAAS,QAAQ;EAEtB,MAAM,MAAM,QAAQ;AACpB,OAAK,UAAU,qBAAqB,IAAI,WAAW,SAAS,QAAQ;AACpE,OAAK,aAAa,IAAI,cAAc,SAAS;AAC7C,OAAK,OAAO,IAAI;AAChB,OAAK,aAAa,aAAa,IAAI,cAAc,SAAS,WAAW;AACrE,OAAK,YAAY,IAAI,aAAa,SAAS;AAC3C,OAAK,eAAe,IAAI,gBAAgB,SAAS;AACjD,OAAK,YAAY,IAAI;EAErB,MAAM,UAAU,QAAQ,mBACpB,QAAQ,QAAQ,QAAQ,iBAAiB,GACzC,eAAe;AAEnB,OAAK,gBAAgB,QAAQ,KAAK,CAAC,WACjC,OAAO;GACL,YAAY;GACZ,UAAU,KAAK;GACf,WAAW,KAAK;EACjB,EAAC,CACH;CACF;CAED,MAAc,eAA8B;AAC1C,MAAI,KAAK,cAAe;AACxB,MAAI,KAAK,YAAa,QAAO,KAAK;AAElC,OAAK,cAAc,KAAK,gBAAgB,CAAC,QAAQ,MAAM;AAErD,QAAK,KAAK,cAAe,MAAK,cAAc;EAC7C,EAAC;AACF,SAAO,KAAK;CACb;CAED,MAAc,iBAAgC;EAC5C,MAAM,SAAS,MAAM,KAAK;AAE1B,MAAI,KAAK,eAAe,SAAS;GAC/B,MAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAK,MACH,OAAM,IAAI,MAAM;AAElB,UAAO,QAAQ;AACf,QAAK,gBAAgB;AACrB,QAAK,OAAO,KAAK,EAAE,YAAY,QAAS,GAAE,gCAAgC;AAC1E;EACD;AAED,OAAK,KAAK,KACR,OAAM,IAAI,MACR;EAIJ,IAAIC;AACJ,MAAI;AACF,SAAM,MAAM,SAAS,KAAK,WAAW,OAAO;EAC7C,SAAQ,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,SAAM,IAAI,OAAO,0CAA0C,KAAK,UAAU,IAAI,IAAI;EACnF;EAED,MAAM,SAAS,MAAM,OAAO,gBAAgB;GAC1C,MAAM,KAAK;GACX;GACA,aAAa,KAAK;EACnB,EAAC;AACF,SAAO,QAAQ,OAAO,KAAK;AAC3B,OAAK,gBAAgB;AAErB,OAAK,OAAO,KACV;GACE,YAAY;GACZ,MAAM,KAAK;GACX,eAAe,OAAO,KAAK;EAC5B,GACD,qDACD;CACF;CAED,MAAM,UAAUC,MAA+C;AAC7D,QAAM,KAAK,cAAc;EACzB,MAAM,SAAS,MAAM,KAAK;EAC1B,MAAM,YAAY,EAAE,KAAK,WAAW,QAAQ,aAAa,KAAK,CAAC;AAC/D,OAAK,OAAO,MAAM,EAAE,MAAM,SAAU,GAAE,4BAA4B;AAElE,MAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAAK,SAAS;AAC5C,UAAO,SAAS,KAAK;EACtB,SAAQ,KAAK;AACZ,QAAK,gBAAgB,IAAI,CAAE,OAAM;AAIjC,QAAK,OAAO,KACV;IAAE,MAAM;IAAU,OAAO,eAAe,QAAQ,IAAI,UAAU;GAAK,GACnE,qEACD;AACD,QAAK,gBAAgB;AACrB,QAAK,cAAc;AACnB,SAAM,KAAK,cAAc;GACzB,MAAM,WAAW,MAAM,OAAO,KAAK,SAAS;AAC5C,UAAO,SAAS,KAAK;EACtB;CACF;CAED,MAAM,eAAeA,MAAcC,KAA0C;EAC3E,MAAM,UAAU,MAAM,KAAK,UAAU,KAAK;AAC1C,SAAO,QAAQ;CAChB;CAED,kBAA2B;AACzB,SAAO,KAAK;CACb;CAED,MAAM,cAAgC;AACpC,MAAI;AACF,SAAM,KAAK,cAAc;AACzB,UAAO;EACR,QAAO;AACN,UAAO;EACR;CACF;AACF;AAED,SAAgB,kBAAkBH,SAA0C;AAC1E,QAAO,IAAI,YAAY;AACxB;;;;AClMD,MAAM,sBAAsB,IAAI;AAEhC,SAAS,gBAAgBI,SAAiBC,SAAiBC,QAAuB;AAChF,KAAI,oBAAoB,IAAI,QAAQ,CAAE;AACtC,qBAAoB,IAAI,QAAQ;CAChC,MAAM,OACH,aAAa,QAAQ,mCACJ,QAAQ;AAC5B,KAAI,OACF,QAAO,KAAK;EAAE,YAAY;EAAS,aAAa;CAAS,GAAE,IAAI;KAE/D,SAAQ,MAAM,iCAAiC,IAAI,EAAE;AAExD;;AAGD,SAAgB,iBAAiBC,OAAkD;CACjF,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,SAAS,YAAY,MAAM;CACjC,MAAM,EACJ,aACA,gBACA,aACA,UAAU,gBACV,MACA,SAAS,eACT,gBAAgB,sBAChB,WAAW,iBACX,UAAU,gBACX,GAAG;CAEJ,MAAM,WAAW;EACf,SAAS,eAAe,WAAW;EACnC,SAAS,eAAe,WAAW;EACnC,SAAS,eAAe,WAAW;CACpC;CAED,MAAM,iBAAiB,sBAAsB;CAE7C,IAAIC;CACJ,IAAIC;AACJ,KAAI;AACF,WAAS,aAAa;GACpB;GACA;GACA;GACA,OAAO,cAAc,SAAS;GAO9B,aAAa,cAAc,eAAe,QAAQ,OAAO,UAAU;GACnE;GACA,uBAAuB,gBAAgB;EACxC,EAAC;AACF,kBAAgB,OAAO;CACxB,SAAQ,KAAK;AACZ,iBAAgB,IAAc;AAC9B,WAAS;GACP,OAAO,CAAC,KAAK,QAAQ,QAAQ,IAAI,KAAK,UAAU;IAAE,OAAO;IAAS,GAAG;IAAK;GAAK,EAAC,CAAC;GACjF,MAAM,CAAC,KAAK,QAAQ,QAAQ,IAAI,KAAK,UAAU;IAAE,OAAO;IAAQ,GAAG;IAAK;GAAK,EAAC,CAAC;GAC/E,MAAM,CAAC,KAAK,QAAQ,QAAQ,KAAK,KAAK,UAAU;IAAE,GAAG;IAAK;GAAK,EAAC,CAAC;GACjE,OAAO,CAAC,KAAK,QAAQ,QAAQ,MAAM,KAAK,UAAU;IAAE,GAAG;IAAK;GAAK,EAAC,CAAC;GACnE,OAAO,MAAM;GACb,MAAM;EACP;AACD,UAAQ,MAAM,0DAA0D,aAAa;CACtF;CAOD,IAAIC;CACJ,IAAIC;AACJ,KAAI,SAAS,WAAW,SAAS,QAC/B,KAAI;AACF,iBAAe,mBAAmB;GAChC,UAAU,KAAK;GACf,YAAY,KAAK;EAClB,EAAC;CACH,SAAQ,KAAK;AACZ,iBAAgB,IAAc;AAC9B,SAAO,MAAM,EAAE,OAAO,aAAc,GAAE,oDAAoD;CAC3F;CAGH,IAAIC,iBAAgC;CACpC,IAAIC;AACJ,KAAI,SAAS,QACX,KAAI;AACF,eAAa;GACX;GACA;GACA;GACA,UAAU,KAAK;GACf,qBAAqB,eAAe;GACpC;EACD,EAAC;AACF,mBAAiB,UAAU,YAAY;AACvC,SAAO,KAAK;GAAE,SAAS;GAAW,UAAU,KAAK;EAAU,GAAE,kBAAkB;CAChF,SAAQ,KAAK;AACZ,iBAAgB,IAAc;AAC9B,SAAO,MAAM,EAAE,OAAO,aAAc,GAAE,oDAAoD;CAC3F;AAKH,KAAI,SAAS,YAAY,SAAS,YAAY,aAC5C,KAAI;AACF,eAAa;GACX;GACA;GACA;GACA,UAAU,KAAK;GACf,YAAY,KAAK;EAClB,EAAC;CACH,SAAQ,KAAK;AACZ,iBAAgB,IAAc;AAC9B,SAAO,MAAM,EAAE,OAAO,aAAc,GAAE,oDAAoD;CAC3F;UACQ,gBAAgB,iBAAiB,aAE1C,iBAAgB,yCAAyC,aAAa;CAGxE,IAAIC,gBAA8B;AAClC,KAAI,SAAS,YAAY,cAAc;AACrC,kBAAgB,SAAS,YAAY;AACrC,SAAO,KAAK;GAAE,SAAS;GAAW,WAAW,EAAE,KAAK,kBAAkB;EAAK,GAAE,kBAAkB;CAChG;AAED,QAAO,KAAK;EAAE;EAAa;EAAgB;EAAa;CAAU,GAAE,yBAAyB;AAG7F,KAAI,eAAe,cAAc;EAC/B,MAAM,iBAAiB,eAAe,kBAAkB;EAExD,MAAM,aAAa,CAACC,QAAgBC,QAAuB;GACzD,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI;AAChE,UAAO,MACL;IAAE,OAAO,MAAM;IAAS,OAAO,MAAM;IAAO;GAAQ,GACpD,6CACD;GAED,MAAMC,gBAAiC,CAAE;AACzC,OAAI,SAAS,WAAW,kBAAkB,CAAE,eAAc,KAAK,iBAAiB,CAAC;AACjF,OAAI,SAAS,WAAW,kBAAkB,CAAE,eAAc,KAAK,iBAAiB,CAAC;GAEjF,MAAM,UAAU,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,eAAe;AAClF,WAAQ,KAAK,CAAC,QAAQ,WAAW,cAAc,EAAE,OAAQ,EAAC,CAAC,QAAQ,MAAM;AACvE,YAAQ,KAAK,EAAE;GAChB,EAAC;EACH;AAED,UAAQ,GAAG,qBAAqB,CAAC,QAAQ,WAAW,qBAAqB,IAAI,CAAC;AAC9E,UAAQ,GAAG,sBAAsB,CAAC,WAAW,WAAW,sBAAsB,OAAO,CAAC;AACtF,SAAO,KAAK,EAAE,eAAgB,GAAE,kCAAkC;CACnE;CAGD,MAAM,cAAc,OAAUC,MAAcC,OAAyC;EACnF,MAAM,SAAS,kBAAkB,UAAU,YAAY;AAEvD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAO,gBAAgB,MAAM,OAAO,SAAS;AAC3C,QAAI;KACF,MAAM,SAAS,MAAM,IAAI;AACzB,UAAK,UAAU,EAAE,MAAM,eAAe,GAAI,EAAC;AAC3C,UAAK,KAAK;AACV,aAAQ,OAAO;IAChB,SAAQ,KAAK;KACZ,MAAM,QAAQ;AACd,UAAK,UAAU;MAAE,MAAM,eAAe;MAAO,SAAS,MAAM;KAAS,EAAC;AACtE,UAAK,gBAAgB,MAAM;AAC3B,UAAK,KAAK;AACV,YAAO,MAAM;MAAE,MAAM;MAAM,OAAO,MAAM;KAAS,GAAE,cAAc;AACjE,YAAO,MAAM;IACd;GACF,EAAC;EACH;CACF;CAED,MAAMC,gBAAqC;EACzC;EACA,QAAQ;EACR,OAAO;EACP,WAAW,CAACC,SAAkB,UAAU,QAAQ,YAAY;EAC5D,UAAU,CAACH,MAAcI,YAAqB,SAAS,MAAM,QAAQ;EACrE;EACA;EACA,OAAO;CACR;CAGD,MAAMC,aAAyB,EAC7B,cAAc,CAAC,YAAY,iBAAyB;EAAE,GAAG;EAAS;CAAY,EAAC,CAChF;CAGD,IAAIC,UAAgC;AACpC,KAAI,OAAO,OAAO,QAChB,KAAI;AACF,YAAU,kBAAkB;GAAE,QAAQ,OAAO;GAAO;EAAQ,EAAC;AAC7D,SAAO,KACL;GAAE,SAAS,OAAO,MAAM;GAAS,YAAY,OAAO,MAAM;EAAY,GACtE,mCACD;CACF,SAAQ,KAAK;AACZ,SAAO,MACL,EAAE,OAAQ,IAAc,QAAS,GACjC,2DACD;CACF;CAIH,MAAM,oBAAoB,MAAoB;EAC5C,MAAM,aAAa,SAAS,YAAa,gBAAgB,kBAAkB;EAC3E,MAAM,aAAa,SAAS,YAAa,gBAAgB,kBAAkB;EAC3E,MAAM,aAAa;EAEnB,MAAM,QAAQ,aAAa,aAAa;EACxC,MAAM,YACF,cAAc,SAAS,cAAc,cAAc,SAAS,aAAa;EAE7E,IAAIC,SAAiC;AACrC,OAAK,MAAO,UAAS,UAAU,cAAc;AAE7C,SAAO;GACL;GACA,WAAW,IAAI,OAAO,aAAa;GACnC,SAAS;GACT,SAAS;GACT,QAAQ,KAAK,OAAO,KAAK,KAAK,GAAG,aAAa,IAAK;GACnD,YAAY;IACV,SAAS;KACP,SAAS,SAAS;KAClB,SAAS,SAAS,UAAU,aAAa,eAAe,SAAS;KACjE,SAAS;IACV;IACD,SAAS;KACP,SAAS,SAAS;KAClB,SAAS,SAAS,UAAU,aAAa,eAAe,SAAS;KACjE,SAAS;IACV;IACD,SAAS;KACP,SAAS,SAAS;KAClB,QAAQ,eAAe,SAAS;KAChC,SAAS;IACV;GACF;EACF;CACF;CAED,MAAM,aAAa,YAA2B;AAC5C,SAAO,KAAK,CAAE,GAAE,8BAA8B;EAC9C,MAAMC,WAA4B,CAAE;AACpC,MAAI,SAAS,WAAW,kBAAkB,CAAE,UAAS,KAAK,iBAAiB,CAAC;AAC5E,MAAI,SAAS,WAAW,kBAAkB,CAAE,UAAS,KAAK,iBAAiB,CAAC;AAC5E,QAAM,QAAQ,IAAI,SAAS;AAC3B,SAAO,KAAK,CAAE,GAAE,+BAA+B;CAChD;CAED,MAAM,YAAY,MAAe;AAC/B,MAAI,SAAS,YAAY,iBAAiB,kBAAkB,CAAE,QAAO;AACrE,MAAI,SAAS,YAAY,iBAAiB,kBAAkB,CAAE,QAAO;AACrE,SAAO;CACR;CAED,MAAM,YAAY,OAAUP,IAA0BQ,aAAyC;AAC7F,MAAI;AACF,UAAO,MAAM,IAAI;EAClB,SAAQ,KAAK;GACZ,MAAM,QAAQ;AACd,UAAO,MAAM;IAAE,OAAO,MAAM;IAAS,OAAO,MAAM;GAAO,GAAE,uBAAuB;AAClF,UAAO;EACR;CACF;CAED,MAAMC,YAA6B;EACjC,QAAQ;EACR,SAAS;EACT,UAAU;EACV,SAAS;CACV;CAGD,MAAMC,aAAiC;EACrC;EACA;EAGA;EACA,MAAM;EACN;EACA;EAGA;EACA,SAAS;EAET,eAAe,0BAA0B;GACvC;GACA;GACA;GACA,gBAAgB,SAAS,YAAY;GACrC,gBAAgB,SAAS,YAAY;GACrC,gBAAgB;EACjB,EAAC;EAGF,QAAQ;EACR,OAAO;EAEP,WAAW,CAACR,SAAkB;AAC5B,mBAAgB,aAAa,2BAA2B,OAAO;AAC/D,UAAO,cAAc,UAAU,KAAK;EACrC;EACD,UAAU,CAACH,MAAcI,YAAqB;AAC5C,mBAAgB,YAAY,0BAA0B,OAAO;AAC7D,UAAO,cAAc,SAAS,MAAM,QAAQ;EAC7C;EACD,iBAAiB,MAAM;AACrB,mBAAgB,mBAAmB,iCAAiC,OAAO;AAC3E,UAAO,cAAc,iBAAiB;EACvC;EACD,eAAe,MAAM;AACnB,mBAAgB,iBAAiB,+BAA+B,OAAO;AACvE,UAAO,cAAc,eAAe;EACrC;EACD,kBAAkB,CAAC,YAAY;AAC7B,mBAAgB,oBAAoB,qBAAqB,OAAO;AAChE,UAAO,WAAW,aAAa,QAAQ;EACxC;EACD,UAAU,MAAM;AACd,mBAAgB,YAAY,sBAAsB,OAAO;AACzD,UAAO,UAAU,UAAU;EAC5B;EACD,SAAS,MAAM;AACb,mBAAgB,WAAW,qBAAqB,OAAO;AACvD,UAAO,UAAU,SAAS;EAC3B;EACD,QAAQ,MAAM;AACZ,mBAAgB,UAAU,oBAAoB,OAAO;AACrD,UAAO,UAAU,QAAQ;EAC1B;EACD,OAAO,CAAC,MAAM,OAAO;AACnB,mBAAgB,SAAS,uBAAuB,OAAO;AACvD,UAAO,cAAc,MAAM,MAAM,GAAG;EACrC;EACD,SAAS,CAAC,IAAI,aAAa;AACzB,mBAAgB,WAAW,qBAAqB,OAAO;AACvD,UAAO,UAAU,QAAQ,IAAI,SAAS;EACvC;CACF;AAED,QAAO;AACR;;AAGD,MAAa,qBAAqB;;;;AC9WlC,SAAS,WAAWQ,KAAmC;AACrD,eAAc,QAAQ,YAAY,QAAQ;AAC3C;AASD,eAAe,YAA+B;AAC5C,KAAI;EAEF,MAAMC,YAAqC,MAAM,OAAO;EACxD,MAAMC,eAAwC,MAAM,OAClD;AAEF,SAAO;GACL,UAAU,UAAU;GACpB,kBAAkB,UAAU;GAC5B,kBAAkB,UAAU;GAC5B,mBAAmB,UAAU;GAC7B,qBAAqB,UAAU;GAC/B,sBAAsB,UAAU;GAChC,cAAc,aAAa;EAC5B;CACF,SAAQ,KAAK;EACZ,MAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI;AAC5D,QAAM,IAAI,OACP,2HAA2H,EAAE,QAAQ;CAEzI;AACF;;;;;;AAOD,IAAa,mBAAb,MAAqD;CACnD,WAA2B;CAE3B;CACA,aAA8B,WAAW;CAEzC,YAAYC,UAAmC,CAAE,GAAE;AACjD,MAAI,QAAQ,QAAQ;AAClB,QAAK,gBAAgB,QAAQ,QAAQ,QAAQ,OAAO;AACpD;EACD;AAED,OAAK,gBAAgB,CAAC,YAAY;GAChC,MAAM,MAAM,MAAM,KAAK;AACvB,UAAO,IAAI,IAAI,SAAS,QAAQ,iBAAiB,CAAE;EACpD,IAAG;CACL;CAED,MAAc,SAAgC;AAC5C,SAAO,KAAK;CACb;CAED,MAAM,UACJC,KACAC,MACAC,UAA4B,CAAE,GACJ;EAC1B,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,MAAM,MAAM,GAAG,KACnB,IAAI,IAAI,iBAAiB;GACvB,QAAQ,IAAI;GACZ,KAAK,IAAI;GACT,MAAM;GACN,aAAa,QAAQ;GACrB,cAAc,QAAQ;GACtB,UAAU,QAAQ;EACnB,GACF;AAED,SAAO;GACL,MAAM,KAAK;GACX,WAAW,KAAK;EACjB;CACF;CAED,MAAM,UAAUF,KAA0C;EACxD,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,MAAM,MAAM,GAAG,KAAK,IAAI,IAAI,iBAAiB;GAAE,QAAQ,IAAI;GAAQ,KAAK,IAAI;EAAK,GAAE;AAEzF,OAAK,KAAK,MAAM;GACd,MAAM,MAAM,IAAI,OAAO,oCAAoC,IAAI,OAAO,GAAG,IAAI,IAAI;AAChF,OAAkC,OAAO;AAC1C,SAAM;EACP;AAED,SAAO;GACL,MAAM,IAAI;GACV,aAAa,IAAI;GACjB,eAAe,IAAI;GACnB,MAAM,IAAI;GACV,WAAW,IAAI;GACf,UAAU,IAAI;GACd,cAAc,IAAI;EACnB;CACF;CAED,MAAM,WAAWA,KAA2C;EAC1D,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,MAAI;GACF,MAAM,MAAM,MAAM,GAAG,KAAK,IAAI,IAAI,kBAAkB;IAAE,QAAQ,IAAI;IAAQ,KAAK,IAAI;GAAK,GAAE;AAC1F,UAAO;IACL,QAAQ;IACR,aAAa,IAAI;IACjB,eAAe,IAAI;IACnB,MAAM,IAAI;IACV,WAAW,IAAI;IACf,UAAU,IAAI;IACd,cAAc,IAAI;GACnB;EACF,SAAQ,KAAK;AACZ,OAAI,WAAW,IAAI,EAAE;IACnB,MAAM,OAAO,OAAO,IAAI,QAAQ,GAAG;AACnC,QAAI,SAAS,eAAgB,OAAM;IACnC,MAAM,aAAa,IAAI,WAAW;AAClC,QAAI,eAAe,OAAO,KAAK,SAAS,WAAW,IAAI,KAAK,SAAS,YAAY,CAC/E,QAAO,EAAE,QAAQ,MAAO;GAE3B;AACD,SAAM;EACP;CACF;CAED,MAAM,aAAaA,KAA+B;EAChD,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,QAAM,GAAG,KAAK,IAAI,IAAI,oBAAoB;GAAE,QAAQ,IAAI;GAAQ,KAAK,IAAI;EAAK,GAAE;CACjF;CAED,MAAM,YAAYG,SAAyD;EACzE,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,MAAM,MAAM,GAAG,KACnB,IAAI,IAAI,qBAAqB;GAC3B,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,mBAAmB,QAAQ;GAC3B,SAAS,QAAQ;EAClB,GACF;EAED,MAAM,WAAY,KAAK,YAAY,CAAE;AAOrC,SAAO;GACL,SAAS,SACN,OAAO,CAAC,aAA8C,EAAE,QAAQ,SAAS,CACzE,IAAI,CAAC,OAAO;IACX,KAAK,EAAE;IACP,MAAM,EAAE;IACR,MAAM,EAAE;IACR,cAAc,EAAE;GACjB,GAAE;GACL,uBAAuB,KAAK;EAC7B;CACF;CAED,MAAM,iBAAiBH,KAAgBI,SAAmD;EACxF,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,SAAO,IAAI,aACT,IACA,IAAI,IAAI,iBAAiB;GACvB,QAAQ,IAAI;GACZ,KAAK,IAAI;GACT,qBAAqB,QAAQ;EAC9B,IACD,EAAE,WAAW,QAAQ,iBAAkB,EACxC;CACF;CAED,MAAM,iBAAiBJ,KAAgBK,SAAmD;EACxF,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,SAAO,IAAI,aACT,IACA,IAAI,IAAI,iBAAiB;GACvB,QAAQ,IAAI;GACZ,KAAK,IAAI;GACT,aAAa,QAAQ;EACtB,IACD,EAAE,WAAW,QAAQ,iBAAkB,EACxC;CACF;AACF;;;;AClOD,MAAM,sBAAsB;AAC5B,MAAM,oBAAoB;AAgB1B,SAAS,SAASC,MAA4C;AAC5D,YAAW,SAAS,SAAU,QAAO,OAAO,KAAK,KAAK;AACtD,KAAI,OAAO,SAAS,KAAK,CAAE,QAAO;AAClC,KAAI,gBAAgB,WAAY,QAAO,OAAO,KAAK,KAAK;AAExD,QAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;EAC9C,MAAMC,SAAmB,CAAE;EAC3B,MAAM,QAAQ,WAAW,MAAM;AAC7B,QAAK,QAAQ,IAAI,MAAM,8CAA8C;AACrE,UAAO,IAAI,MAAM,8CAA8C;EAChE,GAAE,kBAAkB;AAErB,OAAK,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,OAAO,SAAS,MAAM,GAAG,QAAQ,OAAO,KAAK,MAAM,CAAC,CAAC;AAC5F,OAAK,GAAG,OAAO,MAAM;AACnB,gBAAa,MAAM;AACnB,WAAQ,OAAO,OAAO,OAAO,CAAC;EAC/B,EAAC;AACF,OAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,gBAAa,MAAM;AACnB,UAAO,IAAI;EACZ,EAAC;CACH;AACF;AAED,SAAS,YAAYC,KAAqB;AACxC,SAAQ,GAAG,WAAW,MAAM,CAAC,OAAO,IAAI,CAAC,OAAO,MAAM,CAAC;AACxD;;;;;AAMD,IAAa,sBAAb,MAAwD;CACtD,WAA2B;CAE3B,UAA2B,IAAI;CAC/B;CACA,cAAsB;CAEtB,YAAYC,UAAsC,CAAE,GAAE;AACpD,OAAK,aAAa,QAAQ,cAAc;CACzC;CAED,UAAkBC,QAA2C;EAC3D,IAAI,IAAI,KAAK,QAAQ,IAAI,OAAO;AAChC,OAAK,GAAG;AACN,OAAI,IAAI;AACR,QAAK,QAAQ,IAAI,QAAQ,EAAE;EAC5B;AACD,SAAO;CACR;CAED,MAAM,UACJC,KACAL,MACAM,UAA4B,CAAE,GACJ;EAC1B,MAAM,MAAM,MAAM,SAAS,KAAK;EAChC,MAAM,MAAM,KAAK,UAAU,IAAI,OAAO;EACtC,MAAM,SAAS,IAAI,IAAI,IAAI,IAAI;AAE/B,MAAI,SAAS,KAAK,eAAe,KAAK,WACpC,OAAM,IAAI,OAAO,iDAAiD,KAAK,WAAW;EAGpF,MAAMC,MAAoB;GACxB,MAAM;GACN,aAAa,QAAQ;GACrB,cAAc,QAAQ;GACtB,UAAU,QAAQ;GAClB,MAAM,YAAY,IAAI;GACtB,cAAc,IAAI;EACnB;AACD,MAAI,IAAI,IAAI,KAAK,IAAI;AACrB,MAAI,MAAO,MAAK;AAChB,SAAO,EAAE,MAAM,IAAI,KAAM;CAC1B;CAED,MAAM,UAAUF,KAA0C;EACxD,MAAM,MAAM,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI;AACnD,OAAK,KAAK;GACR,MAAM,MAAM,IAAI,OAAO,oBAAoB,IAAI,OAAO,GAAG,IAAI,IAAI;AAChE,OAAkC,OAAO;AAC1C,SAAM;EACP;AACD,SAAO;GACL,MAAM,SAAS,KAAK,IAAI,KAAK;GAC7B,aAAa,IAAI;GACjB,eAAe,IAAI,KAAK;GACxB,MAAM,IAAI;GACV,UAAU,IAAI;GACd,cAAc,IAAI;EACnB;CACF;CAED,MAAM,WAAWA,KAA2C;EAC1D,MAAM,MAAM,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI;AACnD,OAAK,IAAK,QAAO,EAAE,QAAQ,MAAO;AAClC,SAAO;GACL,QAAQ;GACR,aAAa,IAAI;GACjB,eAAe,IAAI,KAAK;GACxB,MAAM,IAAI;GACV,UAAU,IAAI;GACd,cAAc,IAAI;EACnB;CACF;CAED,MAAM,aAAaA,KAA+B;EAChD,MAAM,MAAM,KAAK,UAAU,IAAI,OAAO;AACtC,MAAI,IAAI,OAAO,IAAI,IAAI,CACrB,MAAK;CAER;CAED,MAAM,YAAYG,SAAyD;EACzE,MAAM,EAAE,QAAQ,SAAS,IAAI,mBAAmB,GAAG;EACnD,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,WAAW,IAAK,CAAC;EAChE,MAAM,MAAM,KAAK,UAAU,OAAO;EAClC,MAAM,MAAM,CAAC,GAAG,IAAI,SAAS,AAAC,EAC3B,OAAO,CAAC,CAAC,IAAI,KAAK,IAAI,WAAW,OAAO,CAAC,CACzC,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM;GACpB;GACA,MAAM,IAAI,KAAK;GACf,MAAM,IAAI;GACV,cAAc,IAAI;EACnB,GAAE,CACF,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,cAAc,EAAE,IAAI,CAAC;EAE7C,IAAI,aAAa;AACjB,MAAI,mBAAmB;GACrB,MAAM,MAAM,IAAI,UAAU,CAAC,MAAM,EAAE,QAAQ,kBAAkB;AAC7D,OAAI,QAAA,GACF,OAAM,IAAI,OAAO,kDAAkD,kBAAkB;AAEvF,gBAAa,MAAM;EACpB;EAED,MAAM,OAAO,IAAI,MAAM,YAAY,aAAa,QAAQ;EACxD,MAAM,UAAU,aAAa,UAAU,IAAI;AAE3C,SAAO;GACL,SAAS;GACT,uBAAuB,UAAU,KAAK,KAAK,SAAS,IAAI;EACzD;CACF;CAED,MAAM,iBAAiBC,MAAiBC,UAAoD;AAC1F,QAAM,IAAI,MAAM;CACjB;CAED,MAAM,iBAAiBD,MAAiBE,UAAoD;AAC1F,QAAM,IAAI,MAAM;CACjB;AACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["correlationId: string","context: RequestContext","fn: () => T","key: K","updates: Partial<RequestContext>","key: string","value: unknown","data: Record<string, unknown>","REDACT_PATHS: ReadonlyArray<string>","VALUE_PATTERNS: ReadonlyArray<{ label: string; pattern: RegExp }>","SENSITIVE_KEYS: ReadonlySet<string>","value: unknown","value: string","depth: number","obj: Record<string, unknown>","result: Record<string, unknown>","body: T","additionalPaths: string[]","options: LoggerOptions","result: Record<string, unknown>","pinoLogger: PinoLogger","serviceName: string","baseBindings: object","level: string","obj: object","msg?: string","fallback: Logger","globalLogger: Logger | null","logger: Logger","meterProvider: MeterProvider | null","options: Pick<MetricsOptions, 'endpoint' | 'intervalMs'>","options: MetricsOptions","name: string","version: string","headers: Record<string, unknown>","redactList: string[]","redacted: Record<string, unknown>","body: unknown","maxSize: number","options: FastifyPluginOptions","plugin: FastifyPluginAsync<Partial<FastifyPluginOptions>>","ctx: PluginRequestContext","fn: () => T","_ctx: PluginRequestContext","requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']>","requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']>","requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']>","request: FastifyRequest","reply: FastifyReply","requestContext: PluginRequestContext","logData: Record<string, unknown>","_reply: FastifyReply","error: Error","options: HttpClientOptions","logger: Logger","requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null","requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']> | null","requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null","config: InternalAxiosRequestConfig","carrier: Record<string, string>","response: AxiosResponse","retryConfig: IAxiosRetryConfig","config: AxiosRequestConfig","url: string","config?: AxiosRequestConfig","data?: unknown","value: string","err: unknown","options: VaultClientOptions","jwt: string","path: string","key: string","oldPath: string","newPath: string","logger?: Logger","input: FoundationConfigInput","logger: Logger","loggingError: string | undefined","metricReader: ReturnType<typeof createMetricReader> | undefined","metricsError: string | undefined","tracerInstance: Tracer | null","tracingError: string | undefined","meterInstance: Meter | null","origin: string","err: unknown","flushPromises: Promise<void>[]","name: string","fn: () => T | Promise<T>","observability: ObservabilityModule","name?: string","version?: string","httpModule: HttpModule","secrets: SecretsModule | null","status: HealthStatus['status']","promises: Promise<void>[]","fallback?: T","lifecycle: LifecycleModule","foundation: FoundationInstance","err: unknown","clientMod: Record<string, unknown>","presignerMod: Record<string, unknown>","options: AwsS3ObjectStoreOptions","ref: ObjectRef","body: ObjectBody","options: PutObjectOptions","options: ListObjectsOptions","options: PresignGetObjectOptions","options: PresignPutObjectOptions","body: ObjectBody","chunks: Buffer[]","buf: Buffer","options: InMemoryObjectStoreOptions","bucket: string","ref: ObjectRef","options: PutObjectOptions","obj: StoredObject","options: ListObjectsOptions","_ref: ObjectRef","_options: PresignGetObjectOptions","_options: PresignPutObjectOptions"],"sources":["../src/features/context.ts","../src/features/redaction.ts","../src/features/logging.ts","../src/features/metrics.ts","../src/integrations/fastify-plugin.ts","../src/integrations/http-client.ts","../src/features/vault.ts","../src/foundation.ts","../src/integrations/object-store/aws-s3.ts","../src/integrations/object-store/in-memory.ts"],"sourcesContent":["/**\n * Context Manager\n *\n * Uses OTEL Baggage as the primary store for correlationId when tracing\n * is active. Falls back to AsyncLocalStorage when tracing is disabled,\n * ensuring correlation IDs work regardless of OTEL state.\n */\n\nimport { AsyncLocalStorage } from 'async_hooks';\nimport {\n propagation,\n context as otelContext,\n type Context as OtelContext,\n} from '@opentelemetry/api';\n\nexport interface RequestContext {\n correlationId?: string;\n traceId?: string;\n spanId?: string;\n startTime?: number;\n contextData?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface ContextManager {\n getContext(): RequestContext | undefined;\n run<T>(context: RequestContext, fn: () => T): T;\n get<K extends keyof RequestContext>(key: K): RequestContext[K] | undefined;\n update(updates: Partial<RequestContext>): RequestContext | undefined;\n\n /** Set an arbitrary context value (e.g. tenant_id, user_id) — auto-injected into every log line */\n setContextValue(key: string, value: unknown): void;\n /** Get a single context value by key */\n getContextValue(key: string): unknown;\n /** Get all arbitrary context data */\n getContextData(): Record<string, unknown>;\n /** Bulk-set multiple context values */\n setContextData(data: Record<string, unknown>): void;\n /** Clear all arbitrary context data */\n clearContextData(): void;\n}\n\nconst BAGGAGE_CORRELATION_KEY = 'correlation.id';\n\nfunction setBaggageCorrelationId(correlationId: string): OtelContext {\n const currentBaggage = propagation.getBaggage(otelContext.active());\n const baggage = (currentBaggage ?? propagation.createBaggage()).setEntry(\n BAGGAGE_CORRELATION_KEY,\n { value: correlationId }\n );\n return propagation.setBaggage(otelContext.active(), baggage);\n}\n\nfunction getBaggageCorrelationId(): string | undefined {\n const baggage = propagation.getBaggage(otelContext.active());\n return baggage?.getEntry(BAGGAGE_CORRELATION_KEY)?.value;\n}\n\n/** Create a new context manager instance */\nexport function createContextManager(): ContextManager {\n const als = new AsyncLocalStorage<RequestContext>();\n\n return {\n getContext(): RequestContext | undefined {\n const alsCtx = als.getStore();\n if (!alsCtx) return undefined;\n\n // Prefer OTEL Baggage for correlationId when available\n const baggageCorrelationId = getBaggageCorrelationId();\n if (baggageCorrelationId && baggageCorrelationId !== alsCtx.correlationId) {\n return { ...alsCtx, correlationId: baggageCorrelationId };\n }\n return alsCtx;\n },\n\n run<T>(context: RequestContext, fn: () => T): T {\n // Store correlationId in OTEL Baggage for cross-service propagation\n if (context.correlationId) {\n const otelCtx = setBaggageCorrelationId(context.correlationId);\n return otelContext.with(otelCtx, () => als.run(context, fn));\n }\n return als.run(context, fn);\n },\n\n get<K extends keyof RequestContext>(key: K): RequestContext[K] | undefined {\n if (key === 'correlationId') {\n return (getBaggageCorrelationId() ?? als.getStore()?.correlationId) as RequestContext[K];\n }\n return als.getStore()?.[key];\n },\n\n update(updates: Partial<RequestContext>): RequestContext | undefined {\n const current = als.getStore();\n if (!current) return undefined;\n if (updates.correlationId) {\n setBaggageCorrelationId(updates.correlationId);\n }\n Object.assign(current, updates);\n return current;\n },\n\n setContextValue(key: string, value: unknown): void {\n const current = als.getStore();\n if (!current) return;\n if (!current.contextData) current.contextData = {};\n current.contextData[key] = value;\n },\n\n getContextValue(key: string): unknown {\n return als.getStore()?.contextData?.[key];\n },\n\n getContextData(): Record<string, unknown> {\n return als.getStore()?.contextData ?? {};\n },\n\n setContextData(data: Record<string, unknown>): void {\n const current = als.getStore();\n if (!current) return;\n current.contextData = { ...(current.contextData ?? {}), ...data };\n },\n\n clearContextData(): void {\n const current = als.getStore();\n if (!current) return;\n current.contextData = {};\n },\n };\n}\n","/**\n * Log Redaction & PII Sanitization\n *\n * Two-layer approach:\n * - Layer A: Pino native `redact` (fast-redact) for key-based redaction on every log call.\n * Compiled at init, near-zero runtime cost.\n * - Layer B: Deep-traverse sanitizer for value-pattern detection (JWTs, AWS keys, etc.).\n * Only used for request/response body logging — NOT on every log call.\n */\n\nconst PLACEHOLDER = '[REDACTED]';\n\n/**\n * Key names that Pino's fast-redact will censor automatically.\n * Supports wildcards: '*.password' matches nested keys one level deep.\n */\nexport const REDACT_PATHS: ReadonlyArray<string> = [\n 'password',\n 'passwd',\n 'pass',\n 'pwd',\n 'secret',\n 'secretKey',\n 'secret_key',\n 'token',\n 'accessToken',\n 'access_token',\n 'refreshToken',\n 'refresh_token',\n 'idToken',\n 'id_token',\n 'apiKey',\n 'api_key',\n 'apiSecret',\n 'api_secret',\n 'authorization',\n 'auth',\n 'credentials',\n 'privateKey',\n 'private_key',\n 'cookie',\n 'setCookie',\n 'set_cookie',\n 'creditCard',\n 'credit_card',\n 'cardNumber',\n 'card_number',\n 'ccNumber',\n 'cc_number',\n 'cvv',\n 'cvc',\n 'securityCode',\n 'security_code',\n 'accountNumber',\n 'account_number',\n 'ssn',\n 'socialSecurity',\n 'social_security',\n 'dateOfBirth',\n 'date_of_birth',\n 'dob',\n // Wildcard variants for one-level nesting\n '*.password',\n '*.secret',\n '*.token',\n '*.apiKey',\n '*.api_key',\n '*.authorization',\n '*.cookie',\n '*.credentials',\n '*.creditCard',\n '*.cardNumber',\n '*.cvv',\n '*.ssn',\n '*.privateKey',\n '*.private_key',\n];\n\n/**\n * Value patterns that indicate a secret regardless of the key name.\n * Used only for body sanitization (Layer B).\n */\nconst VALUE_PATTERNS: ReadonlyArray<{ label: string; pattern: RegExp }> = [\n { label: 'jwt', pattern: /^eyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]+$/ },\n { label: 'aws_access_key', pattern: /(?:^|[^A-Za-z0-9])AKIA[0-9A-Z]{16}(?:$|[^A-Za-z0-9])/ },\n { label: 'stripe_key', pattern: /^[sr]k_(live|test)_[A-Za-z0-9]{10,}$/ },\n { label: 'openai_key', pattern: /^sk-[A-Za-z0-9_-]{20,}$/ },\n { label: 'github_token', pattern: /^gh[ps]_[A-Za-z0-9]{36,}$/ },\n { label: 'pem_private_key', pattern: /-----BEGIN\\s+(RSA\\s+|EC\\s+)?PRIVATE\\s+KEY-----/ },\n { label: 'connection_string', pattern: /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/[^:]+:[^@]+@/ },\n { label: 'bearer_token', pattern: /^Bearer\\s+\\S{10,}$/i },\n];\n\nconst SENSITIVE_KEYS: ReadonlySet<string> = new Set(\n REDACT_PATHS.filter((p) => !p.includes('*')).map((k) => k.toLowerCase())\n);\n\nconst MAX_DEPTH = 10;\nconst MAX_KEYS = 200;\n\nfunction mask(value: unknown): string {\n if (typeof value !== 'string' || value.length <= 8) return PLACEHOLDER;\n return `${value.slice(0, 4)}${'*'.repeat(Math.min(value.length - 8, 20))}${value.slice(-4)}`;\n}\n\nfunction isSensitiveValue(value: string): boolean {\n return VALUE_PATTERNS.some((p) => p.pattern.test(value));\n}\n\nfunction sanitizeValue(value: unknown, depth: number): unknown {\n if (depth > MAX_DEPTH) return value;\n if (typeof value === 'string' && isSensitiveValue(value)) return mask(value);\n if (Array.isArray(value)) return value.map((item) => sanitizeValue(item, depth + 1));\n if (value !== null && typeof value === 'object') {\n return sanitizeObject(value as Record<string, unknown>, depth + 1);\n }\n return value;\n}\n\nfunction sanitizeObject(obj: Record<string, unknown>, depth: number): Record<string, unknown> {\n if (depth > MAX_DEPTH) return obj;\n const keys = Object.keys(obj);\n if (keys.length > MAX_KEYS) return obj;\n\n const result: Record<string, unknown> = {};\n for (const key of keys) {\n const value = obj[key];\n if (SENSITIVE_KEYS.has(key.toLowerCase())) {\n result[key] = typeof value === 'string' ? mask(value) : PLACEHOLDER;\n } else {\n result[key] = sanitizeValue(value, depth);\n }\n }\n return result;\n}\n\n/**\n * Deep-traverse sanitizer for request/response bodies.\n * Checks both key names (deny-list) and value patterns (JWT, AWS keys, etc.).\n * NOT intended for every log call — use Pino native `redact` for that.\n */\nexport function sanitizeBody<T>(body: T): T {\n if (body === null || body === undefined) return body;\n if (typeof body === 'string') {\n return (isSensitiveValue(body) ? mask(body) : body) as T;\n }\n if (typeof body !== 'object') return body;\n if (Array.isArray(body)) return body.map((item) => sanitizeValue(item, 0)) as T;\n return sanitizeObject(body as Record<string, unknown>, 0) as T;\n}\n\n/** Build the Pino `redact` config object for fast-redact integration */\nexport function buildPinoRedactConfig(additionalPaths: string[] = []): {\n paths: string[];\n censor: string;\n} {\n return {\n paths: [...REDACT_PATHS, ...additionalPaths],\n censor: PLACEHOLDER,\n };\n}\n","/**\n * Structured Logging with Pino\n */\n\nimport pino, { Logger as PinoLogger } from 'pino';\nimport { trace } from '@opentelemetry/api';\nimport { type ContextManager } from './context';\nimport { buildPinoRedactConfig } from './redaction';\n\nexport interface LoggerOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n level: 'debug' | 'info' | 'warn' | 'error';\n prettyPrint: boolean;\n contextManager?: ContextManager;\n additionalRedactPaths?: string[];\n}\n\nexport interface Logger {\n debug: (obj: object, msg?: string) => void;\n info: (obj: object, msg?: string) => void;\n warn: (obj: object, msg?: string) => void;\n error: (obj: object, msg?: string) => void;\n child: (bindings: object) => Logger;\n readonly pino: PinoLogger;\n}\n\n/** Create a structured logger with automatic trace context injection */\nexport function createLogger(options: LoggerOptions): Logger {\n const {\n serviceName,\n serviceVersion,\n environment,\n level,\n prettyPrint,\n contextManager,\n additionalRedactPaths = [],\n } = options;\n\n const pinoLogger = pino({\n level,\n redact: buildPinoRedactConfig(additionalRedactPaths),\n base: { service: serviceName, version: serviceVersion, env: environment },\n mixin: () => {\n const span = trace.getActiveSpan();\n const spanContext = span?.spanContext();\n const ctx = contextManager?.getContext();\n\n const traceId = spanContext?.traceId || ctx?.traceId;\n const spanId = spanContext?.spanId || ctx?.spanId;\n const correlationId = ctx?.correlationId;\n const contextData = ctx?.contextData;\n\n const result: Record<string, unknown> = {\n trace_id: traceId,\n span_id: spanId,\n correlation_id: correlationId,\n };\n\n if (contextData && Object.keys(contextData).length > 0) {\n result.context = contextData;\n }\n\n return result;\n },\n formatters: { level: (label) => ({ level: label }) },\n transport: prettyPrint\n ? {\n target: 'pino-pretty',\n options: { colorize: true, translateTime: 'SYS:standard', ignore: 'pid,hostname' },\n }\n : undefined,\n });\n\n return wrapPinoLogger(pinoLogger);\n}\n\nfunction wrapPinoLogger(pinoLogger: PinoLogger): Logger {\n return {\n debug: (obj, msg) => pinoLogger.debug(obj, msg),\n info: (obj, msg) => pinoLogger.info(obj, msg),\n warn: (obj, msg) => pinoLogger.warn(obj, msg),\n error: (obj, msg) => pinoLogger.error(obj, msg),\n child: (bindings) => wrapPinoLogger(pinoLogger.child(bindings)),\n pino: pinoLogger,\n };\n}\n\n/** Fallback logger when Pino is not available */\nexport function createFallbackLogger(\n serviceName: string = 'unknown',\n baseBindings: object = {}\n): Logger {\n const log = (level: string, obj: object, msg?: string): void => {\n const logObj = {\n timestamp: new Date().toISOString(),\n level,\n service: serviceName,\n ...baseBindings,\n ...obj,\n msg,\n };\n const payload = JSON.stringify(logObj);\n if (level === 'warn') console.warn(payload);\n else if (level === 'error') console.error(payload);\n else console.log(payload);\n };\n\n const fallback: Logger = {\n debug: (obj, msg) => log('debug', obj, msg),\n info: (obj, msg) => log('info', obj, msg),\n warn: (obj, msg) => log('warn', obj, msg),\n error: (obj, msg) => log('error', obj, msg),\n child: (bindings) => createFallbackLogger(serviceName, { ...baseBindings, ...bindings }),\n pino: null as unknown as PinoLogger,\n };\n\n return fallback;\n}\n\n// Global logger for standalone usage\nlet globalLogger: Logger | null = null;\n\nexport function setGlobalLogger(logger: Logger): void {\n globalLogger = logger;\n}\n\nexport function getGlobalLogger(): Logger {\n return globalLogger || createFallbackLogger();\n}\n","/**\n * OpenTelemetry Metrics Setup\n */\n\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n type IMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { metrics, type Meter } from '@opentelemetry/api';\nimport { getDefaultOtelEndpoint } from '../config';\n\nexport interface MetricsOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n intervalMs?: number;\n}\n\nlet meterProvider: MeterProvider | null = null;\nlet isInitialized = false;\n\n/**\n * Build a gRPC OTLP metric reader for the tracing NodeSDK to own.\n *\n * Preferred path: hand this reader to `setupTracing({ metricReader })` so a single\n * NodeSDK owns both traces and metrics — one global MeterProvider, exporting to the\n * same gRPC collector. This avoids the duplicate global-MeterProvider registration\n * that occurs when NodeSDK and a standalone `setupMetrics` both try to register.\n *\n * The MeterProvider, resource, and global registration are owned by NodeSDK; this\n * function only constructs the reader/exporter.\n */\nexport function createMetricReader(\n options: Pick<MetricsOptions, 'endpoint' | 'intervalMs'> = {}\n): IMetricReader {\n const { endpoint = getDefaultOtelEndpoint(), intervalMs = 5000 } = options;\n const exporter = new OTLPMetricExporter({ url: endpoint });\n const reader = new PeriodicExportingMetricReader({\n exporter,\n exportIntervalMillis: intervalMs,\n });\n isInitialized = true;\n return reader;\n}\n\n/**\n * Initialize a standalone metrics pipeline with its own MeterProvider.\n *\n * Fallback for when tracing (and thus the NodeSDK) is disabled but metrics are\n * enabled. When tracing is on, prefer {@link createMetricReader} + NodeSDK so a\n * single SDK owns both signals.\n */\nexport function setupMetrics(options: MetricsOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Metrics already initialized');\n return;\n }\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n intervalMs = 5000,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const metricExporter = new OTLPMetricExporter({ url: endpoint });\n const metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: intervalMs,\n });\n\n meterProvider = new MeterProvider({ resource, readers: [metricReader] });\n metrics.setGlobalMeterProvider(meterProvider);\n isInitialized = true;\n}\n\n/** Shutdown metrics gracefully */\nexport async function shutdownMetrics(): Promise<void> {\n if (!meterProvider) return;\n try {\n await meterProvider.shutdown();\n isInitialized = false;\n meterProvider = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down metrics:', error);\n throw error;\n }\n}\n\nexport function getMeter(name: string, version: string = '1.0.0'): Meter {\n return metrics.getMeter(name, version);\n}\n\nexport function isMetricsEnabled(): boolean {\n return isInitialized;\n}\n\nexport { metrics };\nexport type { Meter };\n","/**\n * Fastify Observability Plugin\n */\n\nimport type { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';\n// IMPORTANT: Do NOT import fastify-plugin at the top level. It must be loaded\n// lazily inside createObservabilityPlugin so that require('fastify') happens\n// AFTER the OpenTelemetry SDK has started and registered its require hooks.\n// A top-level import here would pull fastify into the require cache before\n// @opentelemetry/instrumentation-fastify can intercept it.\nimport { randomUUID } from 'crypto';\nimport { trace, SpanStatusCode } from '@opentelemetry/api';\nimport { type ContextManager } from '../features/context';\nimport { type Logger, getGlobalLogger } from '../features/logging';\nimport { getMeter } from '../features/metrics';\nimport { sanitizeBody } from '../features/redaction';\n\ninterface PluginRequestContext {\n correlationId: string;\n traceId: string;\n spanId: string;\n startTime: number;\n [key: string]: unknown;\n}\n\nexport interface RequestLoggingOptions {\n logHeaders?: boolean;\n logBody?: boolean;\n logResponseBody?: boolean;\n maxBodySize?: number;\n redactHeaders?: string[];\n}\n\nexport interface FastifyPluginOptions {\n serviceName: string;\n logger?: Logger;\n contextManager?: ContextManager;\n tracingEnabled?: boolean;\n metricsEnabled?: boolean;\n excludeRoutes?: string[];\n /** Request/response logging options */\n requestLogging?: RequestLoggingOptions;\n}\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n __requestContext?: PluginRequestContext;\n }\n}\n\n// Default headers to redact for security\nconst DEFAULT_REDACT_HEADERS = [\n 'authorization',\n 'cookie',\n 'x-api-key',\n 'x-auth-token',\n 'set-cookie',\n];\n\n/** Redact sensitive headers */\nfunction redactHeaders(\n headers: Record<string, unknown>,\n redactList: string[]\n): Record<string, unknown> {\n const redacted: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (redactList.includes(key.toLowerCase())) {\n redacted[key] = '[REDACTED]';\n } else {\n redacted[key] = value;\n }\n }\n return redacted;\n}\n\n/** Truncate body if too large */\nfunction truncateBody(body: unknown, maxSize: number): unknown {\n if (body === undefined || body === null) return undefined;\n const str = typeof body === 'string' ? body : JSON.stringify(body);\n if (str.length > maxSize) {\n return `[TRUNCATED - ${str.length} bytes]`;\n }\n return body;\n}\n\n/** Create a configured Fastify observability plugin */\nexport function createObservabilityPlugin(\n options: FastifyPluginOptions\n): FastifyPluginAsync<Partial<FastifyPluginOptions>> {\n const plugin: FastifyPluginAsync<Partial<FastifyPluginOptions>> = async (fastify, pluginOpts) => {\n const {\n serviceName,\n logger = getGlobalLogger(),\n contextManager,\n tracingEnabled = true,\n metricsEnabled = true,\n excludeRoutes = ['/health', '/health/', '/healthz', '/ready', '/live'],\n requestLogging = {},\n } = { ...options, ...pluginOpts };\n\n // Request logging config\n const logHeaders = requestLogging.logHeaders ?? true;\n const logBody = requestLogging.logBody ?? false;\n const logResponseBody = requestLogging.logResponseBody ?? false;\n const maxBodySize = requestLogging.maxBodySize ?? 10 * 1024; // 10KB\n const headersToRedact = requestLogging.redactHeaders ?? DEFAULT_REDACT_HEADERS;\n\n const runInContext = contextManager\n ? <T>(ctx: PluginRequestContext, fn: () => T) => contextManager.run(ctx, fn)\n : <T>(_ctx: PluginRequestContext, fn: () => T) => fn();\n\n let requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']>;\n let requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']>;\n let requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']>;\n\n if (metricsEnabled) {\n const meter = getMeter(serviceName);\n requestCounter = meter.createCounter('http.server.requests.total');\n requestDuration = meter.createHistogram('http.server.request.duration', { unit: 'ms' });\n requestErrors = meter.createCounter('http.server.requests.errors');\n }\n\n // onRequest - Enrich existing OTEL span, set up context, log headers\n fastify.addHook('onRequest', (request: FastifyRequest, reply: FastifyReply, done) => {\n if (excludeRoutes.some((route) => request.url.startsWith(route))) {\n done();\n return;\n }\n\n const correlationId = (request.headers['x-request-id'] as string) || randomUUID();\n reply.header('x-request-id', correlationId);\n\n // Enrich existing OTEL auto-instrumented span instead of creating a duplicate\n let traceId = '';\n let spanId = '';\n const activeSpan = tracingEnabled ? trace.getActiveSpan() : undefined;\n\n if (activeSpan) {\n activeSpan.setAttribute('http.correlation_id', correlationId);\n\n // Name the HTTP server span with the Fastify route template. The HTTP\n // instrumentation creates the span before Fastify resolves the route, so\n // we patch in low-cardinality route identity once Fastify knows it.\n const routeTemplate = request.routeOptions?.url;\n if (routeTemplate) {\n activeSpan.setAttribute('http.route', routeTemplate);\n activeSpan.updateName(`${request.method} ${routeTemplate}`);\n }\n\n const spanContext = activeSpan.spanContext();\n traceId = spanContext.traceId;\n spanId = spanContext.spanId;\n }\n\n const requestContext: PluginRequestContext = {\n correlationId,\n traceId,\n spanId,\n startTime: Date.now(),\n };\n request.__requestContext = requestContext;\n\n runInContext(requestContext, () => {\n const logData: Record<string, unknown> = {\n correlation_id: correlationId,\n trace_id: traceId || undefined,\n span_id: spanId || undefined,\n method: request.method,\n url: request.url,\n ip: request.ip,\n userAgent: request.headers['user-agent'],\n };\n\n if (logHeaders) {\n logData.headers = redactHeaders(\n request.headers as Record<string, unknown>,\n headersToRedact\n );\n }\n\n logger.info(logData, 'Request received');\n done();\n });\n });\n\n // preHandler - Log request body (if enabled)\n if (logBody) {\n fastify.addHook('preHandler', (request: FastifyRequest, _reply: FastifyReply, done) => {\n const ctx = request.__requestContext;\n if (!ctx || !request.body) {\n done();\n return;\n }\n\n runInContext(ctx, () => {\n logger.debug(\n {\n correlation_id: ctx.correlationId,\n body: sanitizeBody(truncateBody(request.body, maxBodySize)),\n },\n 'Request body'\n );\n done();\n });\n });\n }\n\n // onSend - Log response body (if enabled)\n if (logResponseBody) {\n fastify.addHook('onSend', (request: FastifyRequest, reply: FastifyReply, payload, done) => {\n const ctx = request.__requestContext;\n if (!ctx) {\n done(null, payload);\n return;\n }\n\n runInContext(ctx, () => {\n logger.debug(\n {\n correlation_id: ctx.correlationId,\n statusCode: reply.statusCode,\n body: sanitizeBody(truncateBody(payload, maxBodySize)),\n },\n 'Response body'\n );\n done(null, payload);\n });\n });\n }\n\n // onResponse - Record metrics, enrich active span\n fastify.addHook('onResponse', (request: FastifyRequest, reply: FastifyReply, done) => {\n const ctx = request.__requestContext;\n if (!ctx) {\n done();\n return;\n }\n\n const durationMs = Date.now() - ctx.startTime;\n const route = request.routeOptions?.url || request.url;\n const labels = { method: request.method, route, status_code: String(reply.statusCode) };\n\n runInContext(ctx, () => {\n logger.info(\n {\n correlation_id: ctx.correlationId,\n method: request.method,\n statusCode: reply.statusCode,\n durationMs,\n },\n 'Request completed'\n );\n\n if (metricsEnabled) {\n requestCounter.add(1, labels);\n requestDuration.record(durationMs, labels);\n if (reply.statusCode >= 400) requestErrors.add(1, labels);\n }\n\n // Enrich the OTEL auto-instrumented span with response metadata\n if (tracingEnabled) {\n const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.setAttribute('http.response_time_ms', durationMs);\n }\n }\n\n done();\n });\n });\n\n // onError - Record exception on active span\n fastify.addHook(\n 'onError',\n (request: FastifyRequest, _reply: FastifyReply, error: Error, done) => {\n const ctx = request.__requestContext;\n if (!ctx) {\n done();\n return;\n }\n\n runInContext(ctx, () => {\n logger.error(\n {\n correlation_id: ctx.correlationId,\n method: request.method,\n url: request.url,\n error: error.message,\n },\n 'Request failed'\n );\n\n if (tracingEnabled) {\n const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n activeSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n activeSpan.recordException(error);\n }\n }\n\n done();\n });\n }\n );\n };\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fp = require('fastify-plugin') as typeof import('fastify-plugin').default;\n return fp(plugin, {\n name: 'neoiq-observability',\n fastify: '>=4.x',\n }) as FastifyPluginAsync<Partial<FastifyPluginOptions>>;\n}\n","/**\n * HTTP Client with Observability\n */\n\nimport axios from 'axios';\nimport type {\n AxiosInstance,\n AxiosRequestConfig,\n InternalAxiosRequestConfig,\n AxiosResponse,\n} from 'axios';\nimport axiosRetry, { IAxiosRetryConfig } from 'axios-retry';\nimport CircuitBreaker from 'opossum';\nimport { context, propagation } from '@opentelemetry/api';\nimport type { HttpClientOptions, HttpClient } from '../types';\nimport { getGlobalLogger, type Logger } from '../features/logging';\nimport { getMeter } from '../features/metrics';\n\n/** Create a configured HTTP client with full observability */\nexport function createHttpClient(options: HttpClientOptions): HttpClient {\n const {\n baseURL,\n serviceName,\n timeout = 30000,\n retry = {},\n circuitBreaker: cbOptions = {},\n headers = {},\n foundation,\n } = options;\n\n const logger: Logger = foundation?.logger || getGlobalLogger();\n const getContext = () => foundation?.context.getContext();\n const metricsEnabled = foundation?.features.metrics ?? true;\n const tracingEnabled = foundation?.features.tracing ?? true;\n\n const client = axios.create({\n baseURL,\n timeout,\n headers: { 'Content-Type': 'application/json', ...headers },\n });\n\n // Metrics\n let requestCounter: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null = null;\n let requestDuration: ReturnType<ReturnType<typeof getMeter>['createHistogram']> | null = null;\n let requestErrors: ReturnType<ReturnType<typeof getMeter>['createCounter']> | null = null;\n\n if (metricsEnabled) {\n const meter = getMeter(`http-client-${serviceName}`);\n requestCounter = meter.createCounter('http.client.requests.total');\n requestDuration = meter.createHistogram('http.client.request.duration', { unit: 'ms' });\n requestErrors = meter.createCounter('http.client.requests.errors');\n }\n\n // Request interceptor - Add trace context\n client.interceptors.request.use((config: InternalAxiosRequestConfig) => {\n if (tracingEnabled) {\n const carrier: Record<string, string> = {};\n propagation.inject(context.active(), carrier);\n if (carrier.traceparent) config.headers.set('traceparent', carrier.traceparent);\n if (carrier.tracestate) config.headers.set('tracestate', carrier.tracestate);\n }\n\n const reqCtx = getContext();\n if (reqCtx?.correlationId) config.headers.set('x-request-id', reqCtx.correlationId);\n\n (config as InternalAxiosRequestConfig & { __startTime: number }).__startTime = Date.now();\n\n logger.debug(\n {\n method: config.method?.toUpperCase(),\n url: `${config.baseURL || ''}${config.url}`,\n targetService: serviceName,\n },\n 'Outbound HTTP request'\n );\n\n return config;\n });\n\n // Response interceptor - Log and record metrics\n client.interceptors.response.use(\n (response: AxiosResponse) => {\n const config = response.config as InternalAxiosRequestConfig & { __startTime?: number };\n const durationMs = Date.now() - (config.__startTime || Date.now());\n const labels = {\n target_service: serviceName,\n method: config.method?.toUpperCase() || 'GET',\n status_code: String(response.status),\n };\n\n logger.debug(\n {\n method: config.method?.toUpperCase(),\n statusCode: response.status,\n durationMs,\n targetService: serviceName,\n },\n 'Outbound HTTP response'\n );\n\n if (metricsEnabled) {\n requestCounter?.add(1, labels);\n requestDuration?.record(durationMs, labels);\n }\n\n return response;\n },\n (error) => {\n const config = error.config as\n | (InternalAxiosRequestConfig & { __startTime?: number })\n | undefined;\n const durationMs = config ? Date.now() - (config.__startTime || Date.now()) : 0;\n const statusCode = error.response?.status || 0;\n const labels = {\n target_service: serviceName,\n method: config?.method?.toUpperCase() || 'GET',\n status_code: String(statusCode),\n };\n\n logger.error(\n {\n method: config?.method?.toUpperCase(),\n statusCode,\n durationMs,\n error: error.message,\n targetService: serviceName,\n },\n 'Outbound HTTP error'\n );\n\n if (metricsEnabled) {\n requestCounter?.add(1, labels);\n requestDuration?.record(durationMs, labels);\n requestErrors?.add(1, labels);\n }\n\n return Promise.reject(error);\n }\n );\n\n // Retry config (opt-out via retry.enabled = false)\n if (retry.enabled !== false) {\n const retryConfig: IAxiosRetryConfig = {\n retries: retry.retries ?? 3,\n retryDelay: (retryCount) => (retry.retryDelay ?? 1000) * Math.pow(2, retryCount - 1),\n retryCondition: (error) => {\n const method = (error.config?.method ?? 'get').toUpperCase();\n const IDEMPOTENT = new Set(['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE']);\n if (!retry.retryNonIdempotent && !IDEMPOTENT.has(method)) return false;\n\n const retryStatusCodes = retry.retryStatusCodes ?? [408, 429, 500, 502, 503, 504];\n return !error.response || retryStatusCodes.includes(error.response?.status || 0);\n },\n onRetry: (retryCount, error, requestConfig) => {\n logger.warn(\n {\n retryCount,\n url: `${requestConfig.baseURL || ''}${requestConfig.url}`,\n error: error.message,\n },\n 'Retrying request'\n );\n },\n };\n axiosRetry(client, retryConfig);\n }\n\n // Circuit breaker — opt-in via circuitBreaker.enabled = true\n if (cbOptions.enabled === true) {\n const originalRequest = client.request.bind(client);\n\n const breaker = new CircuitBreaker(\n async (config: AxiosRequestConfig) => originalRequest(config),\n {\n timeout,\n resetTimeout: cbOptions.resetTimeout ?? 30000,\n errorThresholdPercentage: cbOptions.errorThresholdPercentage ?? 50,\n volumeThreshold: 10,\n }\n );\n breaker.on('open', () => logger.warn({ targetService: serviceName }, 'Circuit breaker OPEN'));\n breaker.on('halfOpen', () =>\n logger.info({ targetService: serviceName }, 'Circuit breaker HALF-OPEN')\n );\n breaker.on('close', () =>\n logger.info({ targetService: serviceName }, 'Circuit breaker CLOSED')\n );\n\n client.request = ((config: AxiosRequestConfig) =>\n breaker.fire(config)) as typeof client.request;\n client.get = ((url: string, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'GET', url })) as typeof client.get;\n client.post = ((url: string, data?: unknown, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'POST', url, data })) as typeof client.post;\n client.put = ((url: string, data?: unknown, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'PUT', url, data })) as typeof client.put;\n client.delete = ((url: string, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'DELETE', url })) as typeof client.delete;\n client.patch = ((url: string, data?: unknown, config?: AxiosRequestConfig) =>\n breaker.fire({ ...config, method: 'PATCH', url, data })) as typeof client.patch;\n\n // Preserve direct access for edge cases\n (client as AxiosInstance & { __originalRequest: typeof originalRequest }).__originalRequest =\n originalRequest;\n }\n\n return client;\n}\n\nexport type { HttpClient, HttpClientOptions };\nexport type { AxiosInstance, AxiosRequestConfig, AxiosResponse };\n","/**\n * HashiCorp Vault client.\n *\n * Supports two auth methods:\n * - kubernetes: reads a service-account JWT and exchanges it for a Vault token\n * - token: uses VAULT_TOKEN directly (for local development)\n *\n * Reads KV v2 secrets. No in-memory cache: callers are expected to read a\n * secret at most a few times per process. If a hot read path emerges, add\n * caching at that call site or here, deliberately.\n *\n * `node-vault` is loaded dynamically so it stays an optional peer dependency.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type { VaultConfig } from '../config';\nimport type { Logger } from './logging';\nimport type { SecretsModule } from '../types';\n\ninterface NodeVaultClient {\n token: string;\n kubernetesLogin(options: {\n role: string;\n jwt: string;\n mount_point?: string;\n }): Promise<{ auth: { client_token: string; lease_duration: number } }>;\n read(path: string): Promise<{ data: { data: Record<string, string> } }>;\n}\n\ntype NodeVaultFactory = (options: {\n apiVersion: string;\n endpoint: string;\n token?: string;\n namespace?: string;\n}) => NodeVaultClient;\n\nexport interface VaultClientOptions {\n config: VaultConfig;\n logger: Logger;\n /** Inject a node-vault factory (used in tests). */\n nodeVaultFactory?: NodeVaultFactory;\n}\n\nconst DEFAULTS = {\n address: 'https://vault.beta.commerceiq.ai',\n authMethod: 'kubernetes' as const,\n mountPoint: 'secret',\n tokenPath: '/var/run/secrets/kubernetes.io/serviceaccount/token',\n k8sAuthMount: 'kubernetes',\n};\n\nfunction stripSlashes(value: string): string {\n let start = 0;\n let end = value.length;\n while (start < end && value[start] === '/') start += 1;\n while (end > start && value[end - 1] === '/') end -= 1;\n return value.slice(start, end);\n}\n\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === '/') end -= 1;\n return value.slice(0, end);\n}\n\n/**\n * Heuristic: does this error look like Vault rejecting our token vs. a\n * different failure (path missing, network, malformed payload)?\n *\n * `node-vault` surfaces Vault's \"permission denied\" / 403 / \"missing client\n * token\" messages as plain Error instances; we match on the message text.\n */\nfunction isAuthLikeError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message.toLowerCase() : '';\n return (\n msg.includes('permission denied') ||\n msg.includes('forbidden') ||\n msg.includes('missing client token') ||\n msg.includes('invalid token')\n );\n}\n\nasync function loadNodeVault(): Promise<NodeVaultFactory> {\n try {\n const mod = (await import('node-vault' as string)) as Record<string, unknown>;\n return (mod.default ?? mod) as NodeVaultFactory;\n } catch {\n throw new Error(\n 'node-vault is not installed. Install it as a peer dependency: npm install node-vault'\n );\n }\n}\n\nexport class VaultClient implements SecretsModule {\n private readonly logger: Logger;\n private readonly address: string;\n private readonly authMethod: 'kubernetes' | 'token';\n private readonly role: string | undefined;\n private readonly mountPoint: string;\n private readonly tokenPath: string;\n private readonly k8sAuthMount: string;\n private readonly namespace: string | undefined;\n\n private readonly clientPromise: Promise<NodeVaultClient>;\n private authenticated = false;\n /** In-flight auth so concurrent getSecret() calls don't all trigger kubernetesLogin. */\n private authPromise: Promise<void> | null = null;\n\n constructor(options: VaultClientOptions) {\n this.logger = options.logger;\n\n const cfg = options.config;\n this.address = stripTrailingSlashes(cfg.address ?? DEFAULTS.address);\n this.authMethod = cfg.authMethod ?? DEFAULTS.authMethod;\n this.role = cfg.role;\n this.mountPoint = stripSlashes(cfg.mountPoint ?? DEFAULTS.mountPoint);\n this.tokenPath = cfg.tokenPath ?? DEFAULTS.tokenPath;\n this.k8sAuthMount = cfg.k8sAuthMount ?? DEFAULTS.k8sAuthMount;\n this.namespace = cfg.namespace;\n\n const factory = options.nodeVaultFactory\n ? Promise.resolve(options.nodeVaultFactory)\n : loadNodeVault();\n\n this.clientPromise = factory.then((create) =>\n create({\n apiVersion: 'v1',\n endpoint: this.address,\n namespace: this.namespace,\n })\n );\n }\n\n private async authenticate(): Promise<void> {\n if (this.authenticated) return;\n if (this.authPromise) return this.authPromise;\n\n this.authPromise = this.doAuthenticate().finally(() => {\n // Allow retry on failure; on success the `authenticated` short-circuit takes over.\n if (!this.authenticated) this.authPromise = null;\n });\n return this.authPromise;\n }\n\n private async doAuthenticate(): Promise<void> {\n const client = await this.clientPromise;\n\n if (this.authMethod === 'token') {\n const token = process.env.VAULT_TOKEN;\n if (!token) {\n throw new Error('VAULT_TOKEN env var is required for token auth');\n }\n client.token = token;\n this.authenticated = true;\n this.logger.info({ authMethod: 'token' }, 'Vault authenticated via token');\n return;\n }\n\n if (!this.role) {\n throw new Error(\n 'Vault role is required for Kubernetes auth (set vault.role or VAULT_AUTH_ROLE)'\n );\n }\n\n let jwt: string;\n try {\n jwt = await readFile(this.tokenPath, 'utf8');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to read service-account token at ${this.tokenPath}: ${msg}`);\n }\n\n const result = await client.kubernetesLogin({\n role: this.role,\n jwt,\n mount_point: this.k8sAuthMount,\n });\n client.token = result.auth.client_token;\n this.authenticated = true;\n\n this.logger.info(\n {\n authMethod: 'kubernetes',\n role: this.role,\n leaseDuration: result.auth.lease_duration,\n },\n 'Vault authenticated via Kubernetes service account'\n );\n }\n\n async getSecret(path: string): Promise<Record<string, string>> {\n await this.authenticate();\n const client = await this.clientPromise;\n const fullPath = `${this.mountPoint}/data/${stripSlashes(path)}`;\n this.logger.debug({ path: fullPath }, 'Reading secret from Vault');\n\n try {\n const response = await client.read(fullPath);\n return response.data.data;\n } catch (err) {\n if (!isAuthLikeError(err)) throw err;\n // Cached token is probably expired (Vault tokens have a TTL — the\n // `authenticated` latch above doesn't know about lease expiry). Clear\n // the cached auth state and try the full login flow exactly once.\n this.logger.warn(\n { path: fullPath, error: err instanceof Error ? err.message : err },\n 'Vault read denied; clearing cached auth and re-authenticating once'\n );\n this.authenticated = false;\n this.authPromise = null;\n await this.authenticate();\n const response = await client.read(fullPath);\n return response.data.data;\n }\n }\n\n async getSecretValue(path: string, key: string): Promise<string | undefined> {\n const secrets = await this.getSecret(path);\n return secrets[key];\n }\n\n isAuthenticated(): boolean {\n return this.authenticated;\n }\n\n async checkHealth(): Promise<boolean> {\n try {\n await this.authenticate();\n return true;\n } catch {\n return false;\n }\n }\n}\n\nexport function createVaultClient(options: VaultClientOptions): VaultClient {\n return new VaultClient(options);\n}\n","/**\n * Foundation - Main Entry Point\n */\n\nimport { parseConfig, type FoundationConfig, type FoundationConfigInput } from './config';\nimport { createContextManager, type ContextManager, type RequestContext } from './features/context';\nimport { createLogger, setGlobalLogger, type Logger } from './features/logging';\nimport {\n setupTracing,\n shutdownTracing,\n getTracer,\n getActiveSpan,\n getTraceContext,\n isTracingEnabled,\n SpanStatusCode,\n type Tracer,\n} from './features/tracing';\nimport {\n createMetricReader,\n setupMetrics,\n shutdownMetrics,\n getMeter,\n isMetricsEnabled,\n type Meter,\n} from './features/metrics';\nimport {\n createObservabilityPlugin,\n type FastifyPluginOptions,\n} from './integrations/fastify-plugin';\nimport { createHttpClient as createHttpClientInternal } from './integrations/http-client';\nimport { createVaultClient } from './features/vault';\nimport type {\n FoundationInstance,\n HealthStatus,\n ComponentHealth,\n ObservabilityModule,\n HttpModule,\n LifecycleModule,\n HttpClientOptions,\n HttpClient,\n SecretsModule,\n} from './types';\n\nconst deprecationWarnings = new Set<string>();\n\nfunction warnDeprecation(oldPath: string, newPath: string, logger?: Logger): void {\n if (deprecationWarnings.has(oldPath)) return;\n deprecationWarnings.add(oldPath);\n const msg =\n `foundation.${oldPath}() is deprecated. ` +\n `Use foundation.${newPath}() instead. This alias will be removed in the next major version.`;\n if (logger) {\n logger.warn({ deprecated: oldPath, replacement: newPath }, msg);\n } else {\n console.warn(`[neoiq-foundation] DEPRECATED: ${msg}`);\n }\n}\n\n/** Create a fully configured observability foundation */\nexport function createFoundation(input: FoundationConfigInput): FoundationInstance {\n const startTime = Date.now();\n const config = parseConfig(input);\n const {\n serviceName,\n serviceVersion,\n environment,\n features: featuresConfig,\n otel,\n logging: loggingConfig,\n requestLogging: requestLoggingConfig,\n redaction: redactionConfig,\n shutdown: shutdownConfig,\n } = config;\n\n const features = {\n tracing: featuresConfig.tracing ?? true,\n metrics: featuresConfig.metrics ?? true,\n logging: featuresConfig.logging ?? true,\n };\n\n const contextManager = createContextManager();\n\n let logger: Logger;\n let loggingError: string | undefined;\n try {\n logger = createLogger({\n serviceName,\n serviceVersion,\n environment,\n level: loggingConfig.level ?? 'info',\n // Pretty-print only when stdout is an interactive terminal (local dev). In a\n // pod stdout is a pipe (isTTY undefined), so logs stay newline-delimited JSON\n // for the log collector. Keying this on environment==='development' broke log\n // ingestion for services deployed to dev/beta envs (pino-pretty emits multi-line,\n // ANSI-colored output the collector can't parse). Callers can still force it via\n // logging.prettyPrint.\n prettyPrint: loggingConfig.prettyPrint ?? process.stdout.isTTY === true,\n contextManager,\n additionalRedactPaths: redactionConfig.additionalPaths,\n });\n setGlobalLogger(logger);\n } catch (err) {\n loggingError = (err as Error).message;\n logger = {\n debug: (obj, msg) => console.log(JSON.stringify({ level: 'debug', ...obj, msg })),\n info: (obj, msg) => console.log(JSON.stringify({ level: 'info', ...obj, msg })),\n warn: (obj, msg) => console.warn(JSON.stringify({ ...obj, msg })),\n error: (obj, msg) => console.error(JSON.stringify({ ...obj, msg })),\n child: () => logger,\n pino: null as never,\n };\n console.error('[neoiq-foundation] Logger setup failed, using console:', loggingError);\n }\n\n // Metrics + tracing share one OpenTelemetry NodeSDK so there is a single global\n // MeterProvider exporting to the same gRPC collector as traces. When tracing is on\n // and metrics are on, the metric reader is built here and handed to setupTracing\n // (the NodeSDK owns it). Registering a separate MeterProvider alongside NodeSDK\n // would trigger a duplicate global registration and silently drop metrics.\n let metricReader: ReturnType<typeof createMetricReader> | undefined;\n let metricsError: string | undefined;\n if (features.metrics && features.tracing) {\n try {\n metricReader = createMetricReader({\n endpoint: otel.endpoint,\n intervalMs: otel.metricsIntervalMs,\n });\n } catch (err) {\n metricsError = (err as Error).message;\n logger.error({ error: metricsError }, 'Metrics setup failed - continuing without metrics');\n }\n }\n\n let tracerInstance: Tracer | null = null;\n let tracingError: string | undefined;\n if (features.tracing) {\n try {\n setupTracing({\n serviceName,\n serviceVersion,\n environment,\n endpoint: otel.endpoint,\n autoInstrumentation: featuresConfig.autoInstrumentation,\n metricReader,\n });\n tracerInstance = getTracer(serviceName);\n logger.info({ feature: 'tracing', endpoint: otel.endpoint }, 'Tracing enabled');\n } catch (err) {\n tracingError = (err as Error).message;\n logger.error({ error: tracingError }, 'Tracing setup failed - continuing without tracing');\n }\n }\n\n // Standalone metrics path: metrics enabled but tracing disabled, so there is no\n // NodeSDK to own the MeterProvider — set one up directly.\n if (features.metrics && !features.tracing && !metricsError) {\n try {\n setupMetrics({\n serviceName,\n serviceVersion,\n environment,\n endpoint: otel.endpoint,\n intervalMs: otel.metricsIntervalMs,\n });\n } catch (err) {\n metricsError = (err as Error).message;\n logger.error({ error: metricsError }, 'Metrics setup failed - continuing without metrics');\n }\n } else if (metricReader && tracingError && !metricsError) {\n // Unified metrics ride on the NodeSDK; if it failed to start, metrics are dead.\n metricsError = `metrics SDK (tracing) failed to start: ${tracingError}`;\n }\n\n let meterInstance: Meter | null = null;\n if (features.metrics && !metricsError) {\n meterInstance = getMeter(serviceName);\n logger.info({ feature: 'metrics', interval: `${otel.metricsIntervalMs}ms` }, 'Metrics enabled');\n }\n\n logger.info({ serviceName, serviceVersion, environment, features }, 'Foundation initialized');\n\n // Crash-flush: flush telemetry on uncaughtException/unhandledRejection before exit\n if (shutdownConfig.flushOnCrash) {\n const flushTimeoutMs = shutdownConfig.flushTimeoutMs ?? 5000;\n\n const crashFlush = (origin: string, err: unknown): void => {\n const error = err instanceof Error ? err : new Error(String(err));\n logger.error(\n { error: error.message, stack: error.stack, origin },\n 'Process crash detected, flushing telemetry'\n );\n\n const flushPromises: Promise<void>[] = [];\n if (features.tracing && isTracingEnabled()) flushPromises.push(shutdownTracing());\n if (features.metrics && isMetricsEnabled()) flushPromises.push(shutdownMetrics());\n\n const timeout = new Promise<void>((resolve) => setTimeout(resolve, flushTimeoutMs));\n Promise.race([Promise.allSettled(flushPromises), timeout]).finally(() => {\n process.exit(1);\n });\n };\n\n process.on('uncaughtException', (err) => crashFlush('uncaughtException', err));\n process.on('unhandledRejection', (reason) => crashFlush('unhandledRejection', reason));\n logger.info({ flushTimeoutMs }, 'Crash-flush handlers registered');\n }\n\n // --- Sub-module: observability ---\n const traceInSpan = async <T>(name: string, fn: () => T | Promise<T>): Promise<T> => {\n const tracer = tracerInstance || getTracer(serviceName);\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(name, async (span) => {\n try {\n const result = await fn();\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n resolve(result);\n } catch (err) {\n const error = err as Error;\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.end();\n logger.error({ span: name, error: error.message }, 'Span failed');\n reject(error);\n }\n });\n });\n };\n\n const observability: ObservabilityModule = {\n logger,\n tracer: tracerInstance,\n meter: meterInstance,\n getTracer: (name?: string) => getTracer(name || serviceName),\n getMeter: (name: string, version?: string) => getMeter(name, version),\n getTraceContext,\n getActiveSpan,\n trace: traceInSpan,\n };\n\n // --- Sub-module: http ---\n const httpModule: HttpModule = {\n createClient: (options) => createHttpClientInternal({ ...options, foundation }),\n };\n\n // --- Sub-module: secrets (Vault) ---\n let secrets: SecretsModule | null = null;\n if (config.vault?.enabled) {\n try {\n secrets = createVaultClient({ config: config.vault, logger });\n logger.info(\n { address: config.vault.address, authMethod: config.vault.authMethod },\n 'Vault secrets module initialized'\n );\n } catch (err) {\n logger.error(\n { error: (err as Error).message },\n 'Vault initialization failed - continuing without secrets'\n );\n }\n }\n\n // --- Sub-module: lifecycle ---\n const buildHealthStatus = (): HealthStatus => {\n const tracingUp = !features.tracing || (!tracingError && isTracingEnabled());\n const metricsUp = !features.metrics || (!metricsError && isMetricsEnabled());\n const loggingUp = !loggingError;\n\n const allUp = tracingUp && metricsUp && loggingUp;\n const allDown =\n (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && !loggingUp;\n\n let status: HealthStatus['status'] = 'healthy';\n if (!allUp) status = allDown ? 'unhealthy' : 'degraded';\n\n return {\n status,\n timestamp: new Date().toISOString(),\n service: serviceName,\n version: serviceVersion,\n uptime: Math.floor((Date.now() - startTime) / 1000),\n components: {\n tracing: {\n enabled: features.tracing,\n status: !features.tracing ? 'disabled' : tracingError ? 'down' : 'up',\n message: tracingError,\n },\n metrics: {\n enabled: features.metrics,\n status: !features.metrics ? 'disabled' : metricsError ? 'down' : 'up',\n message: metricsError,\n },\n logging: {\n enabled: features.logging,\n status: loggingError ? 'down' : 'up',\n message: loggingError,\n },\n },\n };\n };\n\n const shutdownFn = async (): Promise<void> => {\n logger.info({}, 'Shutting down foundation...');\n const promises: Promise<void>[] = [];\n if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());\n if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());\n await Promise.all(promises);\n logger.info({}, 'Foundation shutdown complete');\n };\n\n const isReadyFn = (): boolean => {\n if (features.tracing && !tracingError && !isTracingEnabled()) return false;\n if (features.metrics && !metricsError && !isMetricsEnabled()) return false;\n return true;\n };\n\n const safeRunFn = async <T>(fn: () => T | Promise<T>, fallback?: T): Promise<T | undefined> => {\n try {\n return await fn();\n } catch (err) {\n const error = err as Error;\n logger.error({ error: error.message, stack: error.stack }, 'safeRun caught error');\n return fallback;\n }\n };\n\n const lifecycle: LifecycleModule = {\n health: buildHealthStatus,\n isReady: isReadyFn,\n shutdown: shutdownFn,\n safeRun: safeRunFn,\n };\n\n // --- Assemble foundation instance ---\n const foundation: FoundationInstance = {\n config,\n features,\n\n // Sub-modules\n observability,\n http: httpModule,\n lifecycle,\n secrets,\n\n // Top-level ergonomic shortcuts\n logger,\n context: contextManager,\n\n fastifyPlugin: createObservabilityPlugin({\n serviceName,\n logger,\n contextManager,\n tracingEnabled: features.tracing && !tracingError,\n metricsEnabled: features.metrics && !metricsError,\n requestLogging: requestLoggingConfig,\n }),\n\n // Backward-compat aliases (deprecated)\n tracer: tracerInstance,\n meter: meterInstance,\n\n getTracer: (name?: string) => {\n warnDeprecation('getTracer', 'observability.getTracer', logger);\n return observability.getTracer(name);\n },\n getMeter: (name: string, version?: string) => {\n warnDeprecation('getMeter', 'observability.getMeter', logger);\n return observability.getMeter(name, version);\n },\n getTraceContext: () => {\n warnDeprecation('getTraceContext', 'observability.getTraceContext', logger);\n return observability.getTraceContext();\n },\n getActiveSpan: () => {\n warnDeprecation('getActiveSpan', 'observability.getActiveSpan', logger);\n return observability.getActiveSpan();\n },\n createHttpClient: (options) => {\n warnDeprecation('createHttpClient', 'http.createClient', logger);\n return httpModule.createClient(options);\n },\n shutdown: () => {\n warnDeprecation('shutdown', 'lifecycle.shutdown', logger);\n return lifecycle.shutdown();\n },\n isReady: () => {\n warnDeprecation('isReady', 'lifecycle.isReady', logger);\n return lifecycle.isReady();\n },\n health: () => {\n warnDeprecation('health', 'lifecycle.health', logger);\n return lifecycle.health();\n },\n trace: (name, fn) => {\n warnDeprecation('trace', 'observability.trace', logger);\n return observability.trace(name, fn);\n },\n safeRun: (fn, fallback) => {\n warnDeprecation('safeRun', 'lifecycle.safeRun', logger);\n return lifecycle.safeRun(fn, fallback);\n },\n };\n\n return foundation;\n}\n\n/** Alias for createFoundation */\nexport const setupObservability = createFoundation;\n\nexport { SpanStatusCode };\nexport type { FoundationConfig, FoundationConfigInput };\nexport type { Logger };\nexport type { ContextManager, RequestContext };\nexport type { Tracer, Meter };\nexport type { FastifyPluginOptions };\nexport type {\n FoundationInstance,\n HealthStatus,\n ComponentHealth,\n ObservabilityModule,\n HttpModule,\n LifecycleModule,\n HttpClientOptions,\n HttpClient,\n SecretsModule,\n};\n","import type {\n GetObjectResult,\n HeadObjectResult,\n ListObjectsOptions,\n ListObjectsResult,\n ObjectBody,\n ObjectRef,\n PresignGetObjectOptions,\n PresignPutObjectOptions,\n PutObjectOptions,\n PutObjectResult,\n} from './types';\nimport type { ObjectStore } from './object-store';\n\n/**\n * Minimal interface for the subset of AWS S3Client we use.\n * Keeps the module free of `any` while the SDK is dynamically imported.\n */\ninterface S3ClientLike {\n send(command: unknown): Promise<Record<string, unknown>>;\n}\n\ninterface AwsS3Sdk {\n S3Client: new (options: Record<string, unknown>) => S3ClientLike;\n PutObjectCommand: new (input: Record<string, unknown>) => unknown;\n GetObjectCommand: new (input: Record<string, unknown>) => unknown;\n HeadObjectCommand: new (input: Record<string, unknown>) => unknown;\n DeleteObjectCommand: new (input: Record<string, unknown>) => unknown;\n ListObjectsV2Command: new (input: Record<string, unknown>) => unknown;\n getSignedUrl: (\n client: S3ClientLike,\n command: unknown,\n options: { expiresIn: number }\n ) => Promise<string>;\n}\n\ninterface AwsErrorLike {\n name?: string;\n message?: string;\n $metadata?: { httpStatusCode?: number };\n}\n\nfunction isAwsError(err: unknown): err is AwsErrorLike {\n return typeof err === 'object' && err !== null;\n}\n\nexport interface AwsS3ObjectStoreOptions {\n /** Provide an existing S3 client instance (advanced use). */\n client?: S3ClientLike;\n /** Options forwarded to `new S3Client(...)` when `client` isn't provided. */\n clientOptions?: Record<string, unknown>;\n}\n\nasync function loadAwsS3(): Promise<AwsS3Sdk> {\n try {\n // Dynamic import — these are optional peer deps and may not be installed\n const clientMod: Record<string, unknown> = await import('@aws-sdk/client-s3' as string);\n const presignerMod: Record<string, unknown> = await import(\n '@aws-sdk/s3-request-presigner' as string\n );\n return {\n S3Client: clientMod.S3Client as AwsS3Sdk['S3Client'],\n PutObjectCommand: clientMod.PutObjectCommand as AwsS3Sdk['PutObjectCommand'],\n GetObjectCommand: clientMod.GetObjectCommand as AwsS3Sdk['GetObjectCommand'],\n HeadObjectCommand: clientMod.HeadObjectCommand as AwsS3Sdk['HeadObjectCommand'],\n DeleteObjectCommand: clientMod.DeleteObjectCommand as AwsS3Sdk['DeleteObjectCommand'],\n ListObjectsV2Command: clientMod.ListObjectsV2Command as AwsS3Sdk['ListObjectsV2Command'],\n getSignedUrl: presignerMod.getSignedUrl as AwsS3Sdk['getSignedUrl'],\n };\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n throw new Error(\n `AWS SDK not available. Install optional peer deps: @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner. Original error: ${e.message}`\n );\n }\n}\n\n/**\n * AwsS3ObjectStore - wraps AWS S3 behind the provider-agnostic `ObjectStore` interface.\n *\n * This implementation uses dynamic imports so `neoiq-foundation-node` can be used without AWS SDK.\n */\nexport class AwsS3ObjectStore implements ObjectStore {\n public readonly provider = 'aws-s3';\n\n private readonly clientPromise: Promise<S3ClientLike>;\n private readonly awsPromise = loadAwsS3();\n\n constructor(options: AwsS3ObjectStoreOptions = {}) {\n if (options.client) {\n this.clientPromise = Promise.resolve(options.client);\n return;\n }\n\n this.clientPromise = (async () => {\n const aws = await this.awsPromise;\n return new aws.S3Client(options.clientOptions ?? {});\n })();\n }\n\n private async client(): Promise<S3ClientLike> {\n return this.clientPromise;\n }\n\n async putObject(\n ref: ObjectRef,\n body: ObjectBody,\n options: PutObjectOptions = {}\n ): Promise<PutObjectResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n const res = await s3.send(\n new aws.PutObjectCommand({\n Bucket: ref.bucket,\n Key: ref.key,\n Body: body,\n ContentType: options.contentType,\n CacheControl: options.cacheControl,\n Metadata: options.metadata,\n })\n );\n\n return {\n etag: res?.ETag as string | undefined,\n versionId: res?.VersionId as string | undefined,\n };\n }\n\n async getObject(ref: ObjectRef): Promise<GetObjectResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n const res = await s3.send(new aws.GetObjectCommand({ Bucket: ref.bucket, Key: ref.key }));\n\n if (!res?.Body) {\n const err = new Error(`S3 GetObject returned empty body: ${ref.bucket}/${ref.key}`);\n (err as Error & { code?: string }).code = 'EMPTY_BODY';\n throw err;\n }\n\n return {\n body: res.Body as GetObjectResult['body'],\n contentType: res.ContentType as string | undefined,\n contentLength: res.ContentLength as number | undefined,\n etag: res.ETag as string | undefined,\n versionId: res.VersionId as string | undefined,\n metadata: res.Metadata as Record<string, string> | undefined,\n lastModified: res.LastModified as Date | undefined,\n };\n }\n\n async headObject(ref: ObjectRef): Promise<HeadObjectResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n try {\n const res = await s3.send(new aws.HeadObjectCommand({ Bucket: ref.bucket, Key: ref.key }));\n return {\n exists: true,\n contentType: res.ContentType as string | undefined,\n contentLength: res.ContentLength as number | undefined,\n etag: res.ETag as string | undefined,\n versionId: res.VersionId as string | undefined,\n metadata: res.Metadata as Record<string, string> | undefined,\n lastModified: res.LastModified as Date | undefined,\n };\n } catch (err) {\n if (isAwsError(err)) {\n const name = String(err.name ?? '');\n if (name === 'NoSuchBucket') throw err;\n const httpStatus = err.$metadata?.httpStatusCode;\n if (httpStatus === 404 || name.includes('NotFound') || name.includes('NoSuchKey')) {\n return { exists: false };\n }\n }\n throw err;\n }\n }\n\n async deleteObject(ref: ObjectRef): Promise<void> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n await s3.send(new aws.DeleteObjectCommand({ Bucket: ref.bucket, Key: ref.key }));\n }\n\n async listObjects(options: ListObjectsOptions): Promise<ListObjectsResult> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n const res = await s3.send(\n new aws.ListObjectsV2Command({\n Bucket: options.bucket,\n Prefix: options.prefix,\n ContinuationToken: options.continuationToken,\n MaxKeys: options.maxKeys,\n })\n );\n\n const contents = (res?.Contents ?? []) as Array<{\n Key?: string;\n Size?: number;\n ETag?: string;\n LastModified?: Date;\n }>;\n\n return {\n objects: contents\n .filter((o): o is typeof o & { Key: string } => typeof o.Key === 'string')\n .map((o) => ({\n key: o.Key,\n size: o.Size,\n etag: o.ETag,\n lastModified: o.LastModified,\n })),\n nextContinuationToken: res?.NextContinuationToken as string | undefined,\n };\n }\n\n async presignGetObject(ref: ObjectRef, options: PresignGetObjectOptions): Promise<string> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n return aws.getSignedUrl(\n s3,\n new aws.GetObjectCommand({\n Bucket: ref.bucket,\n Key: ref.key,\n ResponseContentType: options.responseContentType,\n }),\n { expiresIn: options.expiresInSeconds }\n );\n }\n\n async presignPutObject(ref: ObjectRef, options: PresignPutObjectOptions): Promise<string> {\n const aws = await this.awsPromise;\n const s3 = await this.client();\n return aws.getSignedUrl(\n s3,\n new aws.PutObjectCommand({\n Bucket: ref.bucket,\n Key: ref.key,\n ContentType: options.contentType,\n }),\n { expiresIn: options.expiresInSeconds }\n );\n }\n}\n","import { createHash } from 'node:crypto';\nimport { Readable } from 'node:stream';\nimport type {\n GetObjectResult,\n HeadObjectResult,\n ListObjectsOptions,\n ListObjectsResult,\n ObjectBody,\n ObjectRef,\n PresignGetObjectOptions,\n PresignPutObjectOptions,\n PutObjectOptions,\n PutObjectResult,\n} from './types';\nimport type { ObjectStore } from './object-store';\n\nconst DEFAULT_MAX_OBJECTS = 10_000;\nconst STREAM_TIMEOUT_MS = 30_000;\n\ntype StoredObject = {\n body: Buffer;\n contentType?: string;\n cacheControl?: string;\n metadata?: Record<string, string>;\n etag: string;\n lastModified: Date;\n};\n\nexport interface InMemoryObjectStoreOptions {\n /** Maximum number of objects across all buckets. Throws when exceeded. */\n maxObjects?: number;\n}\n\nfunction toBuffer(body: ObjectBody): Promise<Buffer> | Buffer {\n if (typeof body === 'string') return Buffer.from(body);\n if (Buffer.isBuffer(body)) return body;\n if (body instanceof Uint8Array) return Buffer.from(body);\n\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n const timer = setTimeout(() => {\n body.destroy(new Error('InMemoryObjectStore: stream read timed out'));\n reject(new Error('InMemoryObjectStore: stream read timed out'));\n }, STREAM_TIMEOUT_MS);\n\n body.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));\n body.on('end', () => {\n clearTimeout(timer);\n resolve(Buffer.concat(chunks));\n });\n body.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\nfunction computeEtag(buf: Buffer): string {\n return `\"${createHash('md5').update(buf).digest('hex')}\"`;\n}\n\n/**\n * InMemoryObjectStore - useful for local dev, unit tests, and as a safe default.\n * NOTE: presign* methods are not meaningful here and will throw.\n */\nexport class InMemoryObjectStore implements ObjectStore {\n public readonly provider = 'in-memory';\n\n private readonly buckets = new Map<string, Map<string, StoredObject>>();\n private readonly maxObjects: number;\n private objectCount = 0;\n\n constructor(options: InMemoryObjectStoreOptions = {}) {\n this.maxObjects = options.maxObjects ?? DEFAULT_MAX_OBJECTS;\n }\n\n private bucketMap(bucket: string): Map<string, StoredObject> {\n let m = this.buckets.get(bucket);\n if (!m) {\n m = new Map();\n this.buckets.set(bucket, m);\n }\n return m;\n }\n\n async putObject(\n ref: ObjectRef,\n body: ObjectBody,\n options: PutObjectOptions = {}\n ): Promise<PutObjectResult> {\n const buf = await toBuffer(body);\n const map = this.bucketMap(ref.bucket);\n const isNew = !map.has(ref.key);\n\n if (isNew && this.objectCount >= this.maxObjects) {\n throw new Error(`InMemoryObjectStore: max object limit reached (${this.maxObjects})`);\n }\n\n const obj: StoredObject = {\n body: buf,\n contentType: options.contentType,\n cacheControl: options.cacheControl,\n metadata: options.metadata,\n etag: computeEtag(buf),\n lastModified: new Date(),\n };\n map.set(ref.key, obj);\n if (isNew) this.objectCount++;\n return { etag: obj.etag };\n }\n\n async getObject(ref: ObjectRef): Promise<GetObjectResult> {\n const obj = this.bucketMap(ref.bucket).get(ref.key);\n if (!obj) {\n const err = new Error(`Object not found: ${ref.bucket}/${ref.key}`);\n (err as Error & { code?: string }).code = 'OBJECT_NOT_FOUND';\n throw err;\n }\n return {\n body: Readable.from(obj.body),\n contentType: obj.contentType,\n contentLength: obj.body.length,\n etag: obj.etag,\n metadata: obj.metadata,\n lastModified: obj.lastModified,\n };\n }\n\n async headObject(ref: ObjectRef): Promise<HeadObjectResult> {\n const obj = this.bucketMap(ref.bucket).get(ref.key);\n if (!obj) return { exists: false };\n return {\n exists: true,\n contentType: obj.contentType,\n contentLength: obj.body.length,\n etag: obj.etag,\n metadata: obj.metadata,\n lastModified: obj.lastModified,\n };\n }\n\n async deleteObject(ref: ObjectRef): Promise<void> {\n const map = this.bucketMap(ref.bucket);\n if (map.delete(ref.key)) {\n this.objectCount--;\n }\n }\n\n async listObjects(options: ListObjectsOptions): Promise<ListObjectsResult> {\n const { bucket, prefix = '', continuationToken } = options;\n const maxKeys = Math.max(1, Math.floor(options.maxKeys ?? 1000));\n const map = this.bucketMap(bucket);\n const all = [...map.entries()]\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, obj]) => ({\n key,\n size: obj.body.length,\n etag: obj.etag,\n lastModified: obj.lastModified,\n }))\n .sort((a, b) => a.key.localeCompare(b.key));\n\n let startIndex = 0;\n if (continuationToken) {\n const idx = all.findIndex((o) => o.key === continuationToken);\n if (idx === -1) {\n throw new Error(`InMemoryObjectStore: invalid continuationToken \"${continuationToken}\"`);\n }\n startIndex = idx + 1;\n }\n\n const page = all.slice(startIndex, startIndex + maxKeys);\n const hasMore = startIndex + maxKeys < all.length;\n\n return {\n objects: page,\n nextContinuationToken: hasMore ? page[page.length - 1]?.key : undefined,\n };\n }\n\n async presignGetObject(_ref: ObjectRef, _options: PresignGetObjectOptions): Promise<string> {\n throw new Error('InMemoryObjectStore does not support presigned URLs');\n }\n\n async presignPutObject(_ref: ObjectRef, _options: PresignPutObjectOptions): Promise<string> {\n throw new Error('InMemoryObjectStore does not support presigned URLs');\n }\n}\n"],"mappings":"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"}
|
|
@@ -40,7 +40,7 @@ const AutoInstrumentationConfigSchema = zod.z.object({
|
|
|
40
40
|
mysql: zod.z.boolean().default(true),
|
|
41
41
|
redis: zod.z.boolean().default(true),
|
|
42
42
|
ioredis: zod.z.boolean().default(true),
|
|
43
|
-
grpc: zod.z.boolean().default(
|
|
43
|
+
grpc: zod.z.boolean().default(false),
|
|
44
44
|
fs: zod.z.boolean().default(false),
|
|
45
45
|
dns: zod.z.boolean().default(false)
|
|
46
46
|
}).partial();
|
|
@@ -193,7 +193,7 @@ function buildInstrumentationConfig(config) {
|
|
|
193
193
|
const result = {};
|
|
194
194
|
for (const [key, instrumentationName] of Object.entries(mapping)) {
|
|
195
195
|
const userSetting = config[key];
|
|
196
|
-
const defaultValue = key !== "fs" && key !== "dns";
|
|
196
|
+
const defaultValue = key !== "fs" && key !== "dns" && key !== "grpc";
|
|
197
197
|
result[instrumentationName] = { enabled: userSetting ?? defaultValue };
|
|
198
198
|
}
|
|
199
199
|
return result;
|
|
@@ -338,4 +338,4 @@ Object.defineProperty(exports, 'shutdownTracing', {
|
|
|
338
338
|
return shutdownTracing;
|
|
339
339
|
}
|
|
340
340
|
});
|
|
341
|
-
//# sourceMappingURL=tracing-
|
|
341
|
+
//# sourceMappingURL=tracing-CDX5DHsr.js.map
|