UNPKG

syntropylog

Version:

An instance manager with observability for Node.js applications

286 lines 11.6 kB
/** * FILE: src/config.schema.ts * DESCRIPTION: Defines the Zod validation schemas for the entire library's configuration. * These schemas are the single source of truth for the configuration's structure and types. */ import { z } from 'zod'; import { Transport } from './logger/transports/Transport'; import { MaskingStrategy } from './masking/MaskingEngine'; /** * @description Schema for logger-specific options, including serialization and transports. * @private */ const loggerOptionsSchema = z .object({ name: z.string().optional(), level: z .enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent']) .optional(), serviceName: z.string().optional(), /** * An array of transport instances to be used by the logger. */ transports: z.array(z.instanceof(Transport)).optional(), /** * A dictionary of custom serializer functions. The key is the field * to look for in the log object, and the value is the function that transforms it. */ serializers: z .record(z.string(), z.function().args(z.any()).returns(z.string())) .optional(), /** * The maximum time in milliseconds a custom serializer can run before being timed out. * @default 50 */ serializerTimeoutMs: z.number().int().positive().default(50), /** Configuration for pretty printing logs in development. */ prettyPrint: z .object({ enabled: z.boolean().optional().default(false), }) .optional(), }) .optional(); /** * @description Reusable schema for retry options, commonly used in client configurations. * @private */ const retryOptionsSchema = z .object({ maxRetries: z.number().int().positive().optional(), retryDelay: z.number().int().positive().optional(), }) .optional(); /** * @description Schema for a single Redis instance, using a discriminated union for different connection modes. */ export const redisInstanceConfigSchema = z.discriminatedUnion('mode', [ z.object({ mode: z.literal('single'), instanceName: z.string(), url: z.string().url(), retryOptions: retryOptionsSchema, // --- NEW: Granular Logging Configuration for Redis --- logging: z .object({ /** Level for successful commands. @default 'debug' */ onSuccess: z.enum(['trace', 'debug', 'info']).default('debug'), /** Level for failed commands. @default 'error' */ onError: z.enum(['warn', 'error', 'fatal']).default('error'), /** Whether to log command parameters. @default true */ logCommandValues: z.boolean().default(true), /** Whether to log the return value of commands. @default false */ logReturnValue: z.boolean().default(false), }) .optional(), }), // Apply the same 'logging' object structure to 'sentinel' and 'cluster' modes z.object({ mode: z.literal('sentinel'), instanceName: z.string(), name: z.string(), sentinels: z.array(z.object({ host: z.string(), port: z.number() })), sentinelPassword: z.string().optional(), retryOptions: retryOptionsSchema, logging: z .object({ onSuccess: z.enum(['trace', 'debug', 'info']).default('debug'), onError: z.enum(['warn', 'error', 'fatal']).default('error'), logCommandValues: z.boolean().default(true), logReturnValue: z.boolean().default(false), }) .optional(), }), z.object({ mode: z.literal('cluster'), instanceName: z.string(), rootNodes: z.array(z.object({ host: z.string(), port: z.number() })), logging: z .object({ /** Level for successful commands. @default 'debug' */ onSuccess: z.enum(['trace', 'debug', 'info']).default('debug'), /** Level for failed commands. @default 'error' */ onError: z.enum(['warn', 'error', 'fatal']).default('error'), /** Whether to log command parameters. @default true */ logCommandValues: z.boolean().default(true), /** Whether to log the return value of commands. @default false */ logReturnValue: z.boolean().default(false), }) .optional(), }), ]); /** * @description Schema for the main Redis configuration block, containing all Redis instances. */ export const redisConfigSchema = z .object({ /** An array of Redis instance configurations. */ instances: z.array(redisInstanceConfigSchema), /** The name of the default Redis instance to use when no name is provided to `getInstance()`. */ default: z.string().optional(), }) .optional(); /** * @description Schema for a single HTTP client instance. */ export const httpInstanceConfigSchema = z.object({ instanceName: z.string(), adapter: z.custom((val) => { return (typeof val === 'object' && val !== null && 'request' in val && typeof val.request === 'function'); }, "The provided adapter is invalid. It must be an object with a 'request' method."), isDefault: z.boolean().optional(), propagate: z.array(z.string()).optional(), propagateFullContext: z.boolean().optional(), logging: z .object({ onSuccess: z.enum(['trace', 'debug', 'info']).default('info'), onError: z.enum(['warn', 'error', 'fatal']).default('error'), logSuccessBody: z.boolean().default(false), logSuccessHeaders: z.boolean().default(false), onRequest: z.enum(['trace', 'debug', 'info']).default('info'), logRequestBody: z.boolean().default(false), logRequestHeaders: z.boolean().default(false), }) .partial() .optional(), }); /** * @description Schema for the main HTTP configuration block. */ export const httpConfigSchema = z .object({ /** An array of HTTP client instance configurations. */ instances: z.array(httpInstanceConfigSchema), /** The name of the default HTTP client instance to use when no name is provided to `getInstance()`. */ default: z.string().optional(), }) .optional(); /** * @description Schema for the main data masking configuration block. */ const maskingConfigSchema = z .object({ /** Array of masking rules with patterns and strategies. */ rules: z.array(z.object({ /** Regex pattern to match field names */ pattern: z.union([z.string(), z.instanceof(RegExp)]), /** Masking strategy to apply */ strategy: z.nativeEnum(MaskingStrategy), /** Whether to preserve original length */ preserveLength: z.boolean().optional(), /** Character to use for masking */ maskChar: z.string().optional(), /** Custom masking function (for CUSTOM strategy) */ customMask: z.function().args(z.string()).returns(z.string()).optional(), })).optional(), /** Default mask character */ maskChar: z.string().optional(), /** Whether to preserve original length by default */ preserveLength: z.boolean().optional(), /** Enable default rules for common data types */ enableDefaultRules: z.boolean().optional(), }) .optional(); /** * @description Schema for a single message broker client instance. * It validates that a valid `IBrokerAdapter` is provided. * @private */ export const brokerInstanceConfigSchema = z.object({ instanceName: z.string(), adapter: z.custom((val) => { return (typeof val === 'object' && val !== null && typeof val.publish === 'function' && typeof val.subscribe === 'function'); }, 'The provided broker adapter is invalid.'), /** * An array of context keys to propagate as message headers/properties. * To propagate all keys, provide an array with a single wildcard: `['*']`. * If not provided, only `correlationId` and `transactionId` are propagated by default. */ propagate: z.array(z.string()).optional(), /** * @deprecated Use `propagate` instead. * If true, propagates the entire asynchronous context map as headers. * If false (default), only propagates `correlationId` and `transactionId`. */ propagateFullContext: z.boolean().optional(), isDefault: z.boolean().optional(), }); /** * @description Schema for the main message broker configuration block. */ export const brokerConfigSchema = z .object({ /** An array of broker client instance configurations. */ instances: z.array(brokerInstanceConfigSchema), /** The name of the default broker instance to use when no name is provided to `getInstance()`. */ default: z.string().optional(), }) .optional(); /** * @description Schema for the declarative logging matrix. * It controls which context properties are included in the final log output based on the log level. * @private */ const loggingMatrixSchema = z .object({ /** An array of context keys to include in logs by default. Can be overridden by level-specific rules. */ default: z.array(z.string()).optional(), /** An array of context keys to include for 'trace' level logs. Use `['*']` to include all context properties. */ trace: z.array(z.string()).optional(), /** An array of context keys to include for 'debug' level logs. Use `['*']` to include all context properties. */ debug: z.array(z.string()).optional(), /** An array of context keys to include for 'info' level logs. Use `['*']` to include all context properties. */ info: z.array(z.string()).optional(), /** An array of context keys to include for 'warn' level logs. Use `['*']` to include all context properties. */ warn: z.array(z.string()).optional(), /** An array of context keys to include for 'error' level logs. Use `['*']` to include all context properties. */ error: z.array(z.string()).optional(), /** An array of context keys to include for 'fatal' level logs. Use `['*']` to include all context properties. */ fatal: z.array(z.string()).optional(), }) .optional(); /** * @description The main schema for the entire SyntropyLog configuration. * This is the single source of truth for validating the user's configuration object. */ export const syntropyLogConfigSchema = z.object({ /** Logger-specific configuration. */ logger: loggerOptionsSchema, /** Declarative matrix to control context data in logs. */ loggingMatrix: loggingMatrixSchema, /** Redis client configuration. */ redis: redisConfigSchema, /** HTTP client configuration. */ http: httpConfigSchema, /** Message broker client configuration. */ brokers: brokerConfigSchema, /** Centralized data masking configuration. */ masking: maskingConfigSchema, /** Context propagation configuration. */ context: z .object({ /** The HTTP header name to use for the correlation ID. @default 'x-correlation-id' */ correlationIdHeader: z.string().optional(), /** The HTTP header name to use for the external transaction/trace ID. @default 'x-trace-id' */ transactionIdHeader: z.string().optional(), }) .optional(), /** * The maximum time in milliseconds to wait for a graceful shutdown before timing out. * @default 5000 */ shutdownTimeout: z .number({ description: 'The maximum time in ms to wait for a graceful shutdown.', }) .int() .positive() .optional(), }); //# sourceMappingURL=config.schema.js.map