@iskra-bun/core 0.1.0 → 0.1.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @iskra-bun/core
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f9654df: `createLogger` now enables `pino-pretty` only when `NODE_ENV !== 'production'`. In production it emits structured JSON with no transport, so logs pipe cleanly to aggregators and containers.
8
+ - `createLogger` now redacts sensitive fields from log output. Keys such as `password`, `pass`, `apiKey`, `token` and `secret` (including nested occurrences), along with `config.env` and `*.data`, are replaced with `[REDACTED]` in both development and production, so credentials and config no longer leak into logs.
9
+ - f9654df: New process-management features:
10
+
11
+ - `spawn(name, config)` and `kill(name)` to add or gracefully remove individual processes at runtime, instead of only at boot.
12
+ - Configurable exponential restart backoff (`restartBackoff: { initialMs, maxMs, factor }` on `ProcessConfig`) replacing the fixed 1s restart delay, so a crash-looping process backs off instead of hammering a broken dependency. Defaults to the previous 1s when unset.
13
+
3
14
  ## 0.1.0
4
15
 
5
16
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -65,6 +65,14 @@ interface AppConfig {
65
65
  };
66
66
  [key: string]: any;
67
67
  }
68
+ interface RestartBackoffConfig {
69
+ /** Initial delay in ms before the first restart. Default: 1000 */
70
+ initialMs?: number;
71
+ /** Maximum delay cap in ms. Default: 30000 */
72
+ maxMs?: number;
73
+ /** Multiplier applied to the delay after each restart. Default: 2 */
74
+ factor?: number;
75
+ }
68
76
  interface ProcessConfig {
69
77
  command: string;
70
78
  args?: string[];
@@ -73,6 +81,8 @@ interface ProcessConfig {
73
81
  maxRestarts?: number;
74
82
  restartCooldown?: number;
75
83
  env?: Record<string, string>;
84
+ /** Exponential backoff settings for restarts. Defaults to 1000 ms flat (no backoff). */
85
+ restartBackoff?: RestartBackoffConfig;
76
86
  }
77
87
 
78
88
  declare class App {
@@ -320,4 +330,4 @@ declare const AppConfigSchema: z.ZodObject<{
320
330
  type AppConfigInput = z.input<typeof AppConfigSchema>;
321
331
  type AppConfigOutput = z.output<typeof AppConfigSchema>;
322
332
 
323
- export { App, type AppConfig, type AppConfigInput, type AppConfigOutput, AppConfigSchema, ConfigError, type Context, type Driver, DriverError, type ErrorCode, ErrorCodes, IskraError, type IskraErrorOptions, LifecycleError, type Logger, type OtelConfig, type Plugin, PluginError, type ProcessConfig, createLogger, initOtel, loadAppConfig, shutdownOtel };
333
+ export { App, type AppConfig, type AppConfigInput, type AppConfigOutput, AppConfigSchema, ConfigError, type Context, type Driver, DriverError, type ErrorCode, ErrorCodes, IskraError, type IskraErrorOptions, LifecycleError, type Logger, type OtelConfig, type Plugin, PluginError, type ProcessConfig, type RestartBackoffConfig, createLogger, initOtel, loadAppConfig, shutdownOtel };
package/dist/index.js CHANGED
@@ -4,13 +4,35 @@ import mitt from "mitt";
4
4
  // src/logger/index.ts
5
5
  import pino from "pino";
6
6
  var createLogger = (name, level = "info") => {
7
+ const isDev = process.env.NODE_ENV !== "production";
7
8
  return pino({
8
9
  name,
9
10
  level,
10
- transport: {
11
- target: "pino-pretty",
12
- options: {
13
- colorize: true
11
+ redact: {
12
+ paths: [
13
+ "password",
14
+ "*.password",
15
+ "pass",
16
+ "*.pass",
17
+ "apiKey",
18
+ "*.apiKey",
19
+ "*.apiSecret",
20
+ "token",
21
+ "*.token",
22
+ "*.authToken",
23
+ "secret",
24
+ "*.secret",
25
+ "config.env",
26
+ "*.data"
27
+ ],
28
+ censor: "[REDACTED]"
29
+ },
30
+ ...isDev && {
31
+ transport: {
32
+ target: "pino-pretty",
33
+ options: {
34
+ colorize: true
35
+ }
14
36
  }
15
37
  }
16
38
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/app.ts","../src/logger/index.ts","../src/config/loader.ts","../src/config/schema.ts","../src/errors.ts","../src/otel.ts"],"sourcesContent":["import mitt, { type Emitter } from 'mitt';\nimport { createLogger, type Logger } from './logger';\nimport { loadAppConfig } from './config/loader';\nimport type { AppConfig, Driver, Plugin, Context } from './types';\nimport { LifecycleError } from './errors';\nimport { initOtel, shutdownOtel } from './otel';\n\nexport class App {\n public config: AppConfig;\n public logger: Logger;\n public events: Emitter<any>;\n private drivers: Driver[] = [];\n public context: Map<string, any> = new Map();\n\n constructor(config?: AppConfig) {\n this.events = mitt();\n // Temporary config until init() is called (or passed in constructor)\n this.config = config || { name: 'Bootstrapping', logger: { level: 'info' } } as AppConfig;\n this.logger = createLogger(this.config.name, this.config.logger?.level);\n }\n\n async init() {\n // If config was not passed, load it\n if (this.config.name === 'Bootstrapping') {\n this.config = await loadAppConfig();\n this.logger = createLogger(this.config.name, this.config.logger?.level);\n }\n\n // Initialize OpenTelemetry SDK before drivers (so auto-instrumentation patches libraries)\n if (this.config.otel && this.config.otel.enabled !== false) {\n await initOtel(this.config.otel, this.config.name);\n this.logger.info({ endpoint: this.config.otel.endpoint }, 'OpenTelemetry initialized');\n }\n\n this.logger.info('Initializing App...');\n\n for (const driver of this.drivers) {\n if (driver.init) {\n this.logger.debug(`Initializing driver: ${driver.name}`);\n await driver.init(this);\n }\n }\n }\n\n register(driver: Driver) {\n this.drivers.push(driver);\n return this;\n }\n\n use(plugin: Plugin) {\n plugin.install(this);\n return this;\n }\n\n on(event: string, handler: (ctx: Context) => Promise<void> | void) {\n this.events.on(event, async (payload) => {\n const ctx: Context = {\n app: this,\n logger: this.logger.child({ event }),\n payload,\n reply: (data) => this.events.emit(`${event}:reply`, data)\n };\n\n try {\n await handler(ctx);\n } catch (err) {\n this.logger.error({ err, event }, 'Error handling event');\n }\n });\n return this;\n }\n\n emit(event: string, payload: any) {\n this.events.emit(event, payload);\n }\n\n async start() {\n await this.init();\n this.logger.info('Starting drivers...');\n await Promise.all(this.drivers.map(d => d.start ? d.start() : Promise.resolve()));\n this.logger.info('App started successfully 🚀');\n }\n\n async stop() {\n this.logger.info('Stopping app...');\n const results = await Promise.allSettled(this.drivers.map(d => d.stop ? d.stop() : Promise.resolve()));\n\n const failures = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected');\n if (failures.length > 0) {\n this.logger.error({ failures }, 'Some drivers failed to stop');\n throw new LifecycleError('Some drivers failed to stop', {\n failures,\n context: { driverCount: this.drivers.length, failedCount: failures.length },\n });\n }\n\n // Shutdown OTel SDK after drivers (flush pending spans/metrics)\n await shutdownOtel();\n\n this.logger.info('App stopped.');\n }\n}\n","import pino from 'pino';\n\nexport const createLogger = (name: string, level: string = 'info') => {\n return pino({\n name,\n level,\n transport: {\n target: 'pino-pretty',\n options: {\n colorize: true\n }\n }\n });\n};\n\nexport type Logger = pino.Logger;\n","import { loadConfig } from 'c12';\nimport { AppConfigSchema, type AppConfigOutput } from './schema';\nimport type { AppConfig } from '../types';\n\nexport async function loadAppConfig(cwd: string = process.cwd()): Promise<AppConfig> {\n const { config } = await loadConfig<AppConfig>({\n name: 'app',\n configFile: 'app.config',\n cwd,\n dotenv: true\n });\n\n const validConfig = AppConfigSchema.parse(config || {});\n return validConfig as AppConfig;\n}\n","import { z } from 'zod';\n\nexport const AppConfigSchema = z.object({\n name: z.string().default('IskraApp'),\n debug: z.boolean().default(false),\n logger: z.object({\n level: z.string().default('info')\n }).default({}),\n otel: z.object({\n enabled: z.boolean().default(true),\n endpoint: z.string().default('http://localhost:4318'),\n serviceName: z.string().optional(),\n serviceVersion: z.string().default('0.1.0'),\n environment: z.string().optional(),\n metricIntervalMs: z.number().default(60_000),\n resourceAttributes: z.record(z.string()).optional(),\n instrumentations: z.record(z.object({ enabled: z.boolean().optional() })).optional(),\n }).optional(),\n processes: z.record(z.object({\n command: z.string(),\n args: z.array(z.string()).optional(),\n mode: z.enum(['daemon', 'oneshot', 'stdio']).default('daemon'),\n restartOnCrash: z.boolean().default(false),\n env: z.record(z.string()).optional()\n })).optional()\n});\n\nexport type AppConfigInput = z.input<typeof AppConfigSchema>;\nexport type AppConfigOutput = z.output<typeof AppConfigSchema>;\n","export const ErrorCodes = {\n // General\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n NOT_INITIALIZED: 'NOT_INITIALIZED',\n ADAPTER_ERROR: 'ADAPTER_ERROR',\n\n // Config\n CONFIG_INVALID: 'CONFIG_INVALID',\n CONFIG_MISSING: 'CONFIG_MISSING',\n\n // Driver\n DRIVER_INIT_FAILED: 'DRIVER_INIT_FAILED',\n DRIVER_START_FAILED: 'DRIVER_START_FAILED',\n DRIVER_STOP_FAILED: 'DRIVER_STOP_FAILED',\n DRIVER_NOT_FOUND: 'DRIVER_NOT_FOUND',\n\n // Plugin\n PLUGIN_INSTALL_FAILED: 'PLUGIN_INSTALL_FAILED',\n\n // Lifecycle\n LIFECYCLE_START_FAILED: 'LIFECYCLE_START_FAILED',\n LIFECYCLE_STOP_FAILED: 'LIFECYCLE_STOP_FAILED',\n\n // HTTP\n BAD_REQUEST: 'BAD_REQUEST',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n NOT_FOUND: 'NOT_FOUND',\n CONFLICT: 'CONFLICT',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n\n // Data\n DATABASE_ERROR: 'DATABASE_ERROR',\n CONNECTION_ERROR: 'CONNECTION_ERROR',\n QUERY_ERROR: 'QUERY_ERROR',\n MIGRATION_ERROR: 'MIGRATION_ERROR',\n CACHE_ERROR: 'CACHE_ERROR',\n\n // Storage / Upload\n STORAGE_ERROR: 'STORAGE_ERROR',\n FILE_UPLOAD_ERROR: 'FILE_UPLOAD_ERROR',\n FILE_NOT_FOUND: 'FILE_NOT_FOUND',\n\n // Worker\n QUEUE_ERROR: 'QUEUE_ERROR',\n JOB_ERROR: 'JOB_ERROR',\n\n // Socket\n SOCKET_CONNECTION_ERROR: 'SOCKET_CONNECTION_ERROR',\n SOCKET_MESSAGE_ERROR: 'SOCKET_MESSAGE_ERROR',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\nexport interface IskraErrorOptions {\n code: ErrorCode;\n cause?: Error;\n context?: Record<string, unknown>;\n}\n\n/**\n * Clase base para todos los errores del framework Iskra.\n * Soporta encadenamiento de errores vía `cause` y metadata arbitraria en `context`.\n */\nexport class IskraError extends Error {\n public readonly code: ErrorCode;\n public readonly context: Record<string, unknown>;\n\n constructor(message: string, options: IskraErrorOptions) {\n super(message, { cause: options.cause });\n this.name = 'IskraError';\n this.code = options.code;\n this.context = options.context ?? {};\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n context: this.context,\n ...(this.cause instanceof Error ? { cause: this.cause.message } : {}),\n };\n }\n}\n\n// ─── Config Errors ───────────────────────────────────────────────────────────\n\nexport class ConfigError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.CONFIG_INVALID, ...options });\n this.name = 'ConfigError';\n }\n}\n\n// ─── Driver Errors ───────────────────────────────────────────────────────────\n\nexport class DriverError extends IskraError {\n constructor(message: string, options?: { code?: ErrorCode; cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: options?.code ?? ErrorCodes.DRIVER_INIT_FAILED, cause: options?.cause, context: options?.context });\n this.name = 'DriverError';\n }\n}\n\n// ─── Plugin Errors ───────────────────────────────────────────────────────────\n\nexport class PluginError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.PLUGIN_INSTALL_FAILED, ...options });\n this.name = 'PluginError';\n }\n}\n\n// ─── Lifecycle Errors ────────────────────────────────────────────────────────\n\nexport class LifecycleError extends IskraError {\n public readonly failures: PromiseRejectedResult[];\n\n constructor(message: string, options: { failures?: PromiseRejectedResult[]; cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.LIFECYCLE_STOP_FAILED, cause: options.cause, context: options.context });\n this.name = 'LifecycleError';\n this.failures = options.failures ?? [];\n }\n}\n","/**\n * OpenTelemetry SDK initialization for Iskra apps.\n *\n * All OTel packages are dynamic-imported so they remain optional peer deps.\n * Apps that don't set `config.otel` never load this code.\n *\n * The module specifiers are held in variables rather than passed as string\n * literals to `import()`. This stops `tsc` from statically resolving the\n * optional `@opentelemetry/*` packages at compile time, so the module\n * typechecks cleanly even when those peer deps are absent. At runtime the\n * imports still resolve normally, and a missing package degrades to a clear,\n * actionable error instead of an unhandled module-resolution failure.\n */\nimport type { OtelConfig } from './types';\n\nlet sdkInstance: any = null;\n\n/** Optional OTel package specifiers, indirected so tsc does not resolve them statically. */\nconst OTEL_MODULES = {\n sdkNode: '@opentelemetry/sdk-node',\n autoInstrumentations: '@opentelemetry/auto-instrumentations-node',\n traceExporter: '@opentelemetry/exporter-trace-otlp-http',\n metricExporter: '@opentelemetry/exporter-metrics-otlp-http',\n sdkMetrics: '@opentelemetry/sdk-metrics',\n resources: '@opentelemetry/resources',\n semconv: '@opentelemetry/semantic-conventions',\n} as const;\n\n/** Import an optional module by specifier; the indirection keeps tsc from resolving it. */\nfunction importOptional(specifier: string): Promise<any> {\n return import(specifier);\n}\n\n/**\n * Initialize the OpenTelemetry NodeSDK with OTLP exporters.\n * Must be called before any drivers start so auto-instrumentation can patch libraries.\n */\nexport async function initOtel(config: OtelConfig, appName: string): Promise<void> {\n if (sdkInstance) return; // Already initialized\n\n try {\n const [\n { NodeSDK },\n { getNodeAutoInstrumentations },\n { OTLPTraceExporter },\n { OTLPMetricExporter },\n { PeriodicExportingMetricReader },\n { Resource },\n semconv,\n ] = await Promise.all([\n importOptional(OTEL_MODULES.sdkNode),\n importOptional(OTEL_MODULES.autoInstrumentations),\n importOptional(OTEL_MODULES.traceExporter),\n importOptional(OTEL_MODULES.metricExporter),\n importOptional(OTEL_MODULES.sdkMetrics),\n importOptional(OTEL_MODULES.resources),\n importOptional(OTEL_MODULES.semconv),\n ]);\n\n const serviceName = config.serviceName || appName;\n const endpoint = config.endpoint || 'http://localhost:4318';\n\n const resource = new Resource({\n [semconv.ATTR_SERVICE_NAME]: serviceName,\n [semconv.ATTR_SERVICE_VERSION]: config.serviceVersion || '0.1.0',\n 'deployment.environment': config.environment || process.env.NODE_ENV || 'development',\n ...config.resourceAttributes,\n });\n\n const instrumentationOverrides: Record<string, any> = {\n '@opentelemetry/instrumentation-fs': { enabled: false },\n ...config.instrumentations,\n };\n\n const sdk = new NodeSDK({\n resource,\n traceExporter: new OTLPTraceExporter({ url: `${endpoint}/v1/traces` }),\n metricReader: new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({ url: `${endpoint}/v1/metrics` }),\n exportIntervalMillis: config.metricIntervalMs ?? 60_000,\n }),\n instrumentations: [getNodeAutoInstrumentations(instrumentationOverrides)],\n });\n\n sdk.start();\n sdkInstance = sdk;\n } catch (err: any) {\n // Provide a clear error when OTel packages are not installed\n if (err?.code === 'ERR_MODULE_NOT_FOUND' || err?.code === 'MODULE_NOT_FOUND' || err?.message?.includes('Cannot find')) {\n throw new Error(\n `[iskra/otel] OpenTelemetry packages are not installed. ` +\n `Install them with: bun add @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ` +\n `@opentelemetry/exporter-trace-otlp-http @opentelemetry/exporter-metrics-otlp-http ` +\n `@opentelemetry/sdk-metrics @opentelemetry/resources @opentelemetry/semantic-conventions`,\n );\n }\n throw err;\n }\n}\n\n/**\n * Gracefully shut down the OTel SDK, flushing any pending spans/metrics.\n */\nexport async function shutdownOtel(): Promise<void> {\n if (sdkInstance) {\n await sdkInstance.shutdown();\n sdkInstance = null;\n }\n}\n"],"mappings":";AAAA,OAAO,UAA4B;;;ACAnC,OAAO,UAAU;AAEV,IAAM,eAAe,CAAC,MAAc,QAAgB,WAAW;AAClE,SAAO,KAAK;AAAA,IACR;AAAA,IACA;AAAA,IACA,WAAW;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,UAAU;AAAA,MACd;AAAA,IACJ;AAAA,EACJ,CAAC;AACL;;;ACbA,SAAS,kBAAkB;;;ACA3B,SAAS,SAAS;AAEX,IAAM,kBAAkB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,OAAO,EAAE,QAAQ,UAAU;AAAA,EACnC,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EAChC,QAAQ,EAAE,OAAO;AAAA,IACb,OAAO,EAAE,OAAO,EAAE,QAAQ,MAAM;AAAA,EACpC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACb,MAAM,EAAE,OAAO;AAAA,IACX,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,UAAU,EAAE,OAAO,EAAE,QAAQ,uBAAuB;AAAA,IACpD,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,gBAAgB,EAAE,OAAO,EAAE,QAAQ,OAAO;AAAA,IAC1C,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,kBAAkB,EAAE,OAAO,EAAE,QAAQ,GAAM;AAAA,IAC3C,oBAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IAClD,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,SAAS;AAAA,EACvF,CAAC,EAAE,SAAS;AAAA,EACZ,WAAW,EAAE,OAAO,EAAE,OAAO;AAAA,IACzB,SAAS,EAAE,OAAO;AAAA,IAClB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACnC,MAAM,EAAE,KAAK,CAAC,UAAU,WAAW,OAAO,CAAC,EAAE,QAAQ,QAAQ;AAAA,IAC7D,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IACzC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACvC,CAAC,CAAC,EAAE,SAAS;AACjB,CAAC;;;ADrBD,eAAsB,cAAc,MAAc,QAAQ,IAAI,GAAuB;AACjF,QAAM,EAAE,OAAO,IAAI,MAAM,WAAsB;AAAA,IAC3C,MAAM;AAAA,IACN,YAAY;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,EACZ,CAAC;AAED,QAAM,cAAc,gBAAgB,MAAM,UAAU,CAAC,CAAC;AACtD,SAAO;AACX;;;AEdO,IAAM,aAAa;AAAA;AAAA,EAEtB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,eAAe;AAAA;AAAA,EAGf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA;AAAA,EAGhB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,uBAAuB;AAAA;AAAA,EAGvB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA;AAAA,EAGvB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,kBAAkB;AAAA;AAAA,EAGlB,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,aAAa;AAAA;AAAA,EAGb,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,gBAAgB;AAAA;AAAA,EAGhB,aAAa;AAAA,EACb,WAAW;AAAA;AAAA,EAGX,yBAAyB;AAAA,EACzB,sBAAsB;AAC1B;AAcO,IAAM,aAAN,cAAyB,MAAM;AAAA,EAClB;AAAA,EACA;AAAA,EAEhB,YAAY,SAAiB,SAA4B;AACrD,UAAM,SAAS,EAAE,OAAO,QAAQ,MAAM,CAAC;AACvC,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,UAAU,QAAQ,WAAW,CAAC;AAAA,EACvC;AAAA,EAEA,SAAS;AACL,WAAO;AAAA,MACH,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,GAAI,KAAK,iBAAiB,QAAQ,EAAE,OAAO,KAAK,MAAM,QAAQ,IAAI,CAAC;AAAA,IACvE;AAAA,EACJ;AACJ;AAIO,IAAM,cAAN,cAA0B,WAAW;AAAA,EACxC,YAAY,SAAiB,SAAgE;AACzF,UAAM,SAAS,EAAE,MAAM,WAAW,gBAAgB,GAAG,QAAQ,CAAC;AAC9D,SAAK,OAAO;AAAA,EAChB;AACJ;AAIO,IAAM,cAAN,cAA0B,WAAW;AAAA,EACxC,YAAY,SAAiB,SAAkF;AAC3G,UAAM,SAAS,EAAE,MAAM,SAAS,QAAQ,WAAW,oBAAoB,OAAO,SAAS,OAAO,SAAS,SAAS,QAAQ,CAAC;AACzH,SAAK,OAAO;AAAA,EAChB;AACJ;AAIO,IAAM,cAAN,cAA0B,WAAW;AAAA,EACxC,YAAY,SAAiB,SAAgE;AACzF,UAAM,SAAS,EAAE,MAAM,WAAW,uBAAuB,GAAG,QAAQ,CAAC;AACrE,SAAK,OAAO;AAAA,EAChB;AACJ;AAIO,IAAM,iBAAN,cAA6B,WAAW;AAAA,EAC3B;AAAA,EAEhB,YAAY,SAAiB,SAAmG;AAC5H,UAAM,SAAS,EAAE,MAAM,WAAW,uBAAuB,OAAO,QAAQ,OAAO,SAAS,QAAQ,QAAQ,CAAC;AACzG,SAAK,OAAO;AACZ,SAAK,WAAW,QAAQ,YAAY,CAAC;AAAA,EACzC;AACJ;;;AC5GA,IAAI,cAAmB;AAGvB,IAAM,eAAe;AAAA,EACjB,SAAS;AAAA,EACT,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AACb;AAGA,SAAS,eAAe,WAAiC;AACrD,SAAO,OAAO;AAClB;AAMA,eAAsB,SAAS,QAAoB,SAAgC;AAC/E,MAAI,YAAa;AAEjB,MAAI;AACA,UAAM;AAAA,MACF,EAAE,QAAQ;AAAA,MACV,EAAE,4BAA4B;AAAA,MAC9B,EAAE,kBAAkB;AAAA,MACpB,EAAE,mBAAmB;AAAA,MACrB,EAAE,8BAA8B;AAAA,MAChC,EAAE,SAAS;AAAA,MACX;AAAA,IACJ,IAAI,MAAM,QAAQ,IAAI;AAAA,MAClB,eAAe,aAAa,OAAO;AAAA,MACnC,eAAe,aAAa,oBAAoB;AAAA,MAChD,eAAe,aAAa,aAAa;AAAA,MACzC,eAAe,aAAa,cAAc;AAAA,MAC1C,eAAe,aAAa,UAAU;AAAA,MACtC,eAAe,aAAa,SAAS;AAAA,MACrC,eAAe,aAAa,OAAO;AAAA,IACvC,CAAC;AAED,UAAM,cAAc,OAAO,eAAe;AAC1C,UAAM,WAAW,OAAO,YAAY;AAEpC,UAAM,WAAW,IAAI,SAAS;AAAA,MAC1B,CAAC,QAAQ,iBAAiB,GAAG;AAAA,MAC7B,CAAC,QAAQ,oBAAoB,GAAG,OAAO,kBAAkB;AAAA,MACzD,0BAA0B,OAAO,eAAe,QAAQ,IAAI,YAAY;AAAA,MACxE,GAAG,OAAO;AAAA,IACd,CAAC;AAED,UAAM,2BAAgD;AAAA,MAClD,qCAAqC,EAAE,SAAS,MAAM;AAAA,MACtD,GAAG,OAAO;AAAA,IACd;AAEA,UAAM,MAAM,IAAI,QAAQ;AAAA,MACpB;AAAA,MACA,eAAe,IAAI,kBAAkB,EAAE,KAAK,GAAG,QAAQ,aAAa,CAAC;AAAA,MACrE,cAAc,IAAI,8BAA8B;AAAA,QAC5C,UAAU,IAAI,mBAAmB,EAAE,KAAK,GAAG,QAAQ,cAAc,CAAC;AAAA,QAClE,sBAAsB,OAAO,oBAAoB;AAAA,MACrD,CAAC;AAAA,MACD,kBAAkB,CAAC,4BAA4B,wBAAwB,CAAC;AAAA,IAC5E,CAAC;AAED,QAAI,MAAM;AACV,kBAAc;AAAA,EAClB,SAAS,KAAU;AAEf,QAAI,KAAK,SAAS,0BAA0B,KAAK,SAAS,sBAAsB,KAAK,SAAS,SAAS,aAAa,GAAG;AACnH,YAAM,IAAI;AAAA,QACN;AAAA,MAIJ;AAAA,IACJ;AACA,UAAM;AAAA,EACV;AACJ;AAKA,eAAsB,eAA8B;AAChD,MAAI,aAAa;AACb,UAAM,YAAY,SAAS;AAC3B,kBAAc;AAAA,EAClB;AACJ;;;ALrGO,IAAM,MAAN,MAAU;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACC,UAAoB,CAAC;AAAA,EACtB,UAA4B,oBAAI,IAAI;AAAA,EAE3C,YAAY,QAAoB;AAC5B,SAAK,SAAS,KAAK;AAEnB,SAAK,SAAS,UAAU,EAAE,MAAM,iBAAiB,QAAQ,EAAE,OAAO,OAAO,EAAE;AAC3E,SAAK,SAAS,aAAa,KAAK,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;AAAA,EAC1E;AAAA,EAEA,MAAM,OAAO;AAET,QAAI,KAAK,OAAO,SAAS,iBAAiB;AACtC,WAAK,SAAS,MAAM,cAAc;AAClC,WAAK,SAAS,aAAa,KAAK,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAK;AAAA,IAC1E;AAGA,QAAI,KAAK,OAAO,QAAQ,KAAK,OAAO,KAAK,YAAY,OAAO;AACxD,YAAM,SAAS,KAAK,OAAO,MAAM,KAAK,OAAO,IAAI;AACjD,WAAK,OAAO,KAAK,EAAE,UAAU,KAAK,OAAO,KAAK,SAAS,GAAG,2BAA2B;AAAA,IACzF;AAEA,SAAK,OAAO,KAAK,qBAAqB;AAEtC,eAAW,UAAU,KAAK,SAAS;AAC/B,UAAI,OAAO,MAAM;AACb,aAAK,OAAO,MAAM,wBAAwB,OAAO,IAAI,EAAE;AACvD,cAAM,OAAO,KAAK,IAAI;AAAA,MAC1B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,SAAS,QAAgB;AACrB,SAAK,QAAQ,KAAK,MAAM;AACxB,WAAO;AAAA,EACX;AAAA,EAEA,IAAI,QAAgB;AAChB,WAAO,QAAQ,IAAI;AACnB,WAAO;AAAA,EACX;AAAA,EAEA,GAAG,OAAe,SAAiD;AAC/D,SAAK,OAAO,GAAG,OAAO,OAAO,YAAY;AACrC,YAAM,MAAe;AAAA,QACjB,KAAK;AAAA,QACL,QAAQ,KAAK,OAAO,MAAM,EAAE,MAAM,CAAC;AAAA,QACnC;AAAA,QACA,OAAO,CAAC,SAAS,KAAK,OAAO,KAAK,GAAG,KAAK,UAAU,IAAI;AAAA,MAC5D;AAEA,UAAI;AACA,cAAM,QAAQ,GAAG;AAAA,MACrB,SAAS,KAAK;AACV,aAAK,OAAO,MAAM,EAAE,KAAK,MAAM,GAAG,sBAAsB;AAAA,MAC5D;AAAA,IACJ,CAAC;AACD,WAAO;AAAA,EACX;AAAA,EAEA,KAAK,OAAe,SAAc;AAC9B,SAAK,OAAO,KAAK,OAAO,OAAO;AAAA,EACnC;AAAA,EAEA,MAAM,QAAQ;AACV,UAAM,KAAK,KAAK;AAChB,SAAK,OAAO,KAAK,qBAAqB;AACtC,UAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,OAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAChF,SAAK,OAAO,KAAK,oCAA6B;AAAA,EAClD;AAAA,EAEA,MAAM,OAAO;AACT,SAAK,OAAO,KAAK,iBAAiB;AAClC,UAAM,UAAU,MAAM,QAAQ,WAAW,KAAK,QAAQ,IAAI,OAAK,EAAE,OAAO,EAAE,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAErG,UAAM,WAAW,QAAQ,OAAO,CAAC,MAAkC,EAAE,WAAW,UAAU;AAC1F,QAAI,SAAS,SAAS,GAAG;AACrB,WAAK,OAAO,MAAM,EAAE,SAAS,GAAG,6BAA6B;AAC7D,YAAM,IAAI,eAAe,+BAA+B;AAAA,QACpD;AAAA,QACA,SAAS,EAAE,aAAa,KAAK,QAAQ,QAAQ,aAAa,SAAS,OAAO;AAAA,MAC9E,CAAC;AAAA,IACL;AAGA,UAAM,aAAa;AAEnB,SAAK,OAAO,KAAK,cAAc;AAAA,EACnC;AACJ;","names":[]}
1
+ {"version":3,"sources":["../src/app.ts","../src/logger/index.ts","../src/config/loader.ts","../src/config/schema.ts","../src/errors.ts","../src/otel.ts"],"sourcesContent":["import mitt, { type Emitter } from 'mitt';\nimport { createLogger, type Logger } from './logger';\nimport { loadAppConfig } from './config/loader';\nimport type { AppConfig, Driver, Plugin, Context } from './types';\nimport { LifecycleError } from './errors';\nimport { initOtel, shutdownOtel } from './otel';\n\nexport class App {\n public config: AppConfig;\n public logger: Logger;\n public events: Emitter<any>;\n private drivers: Driver[] = [];\n public context: Map<string, any> = new Map();\n\n constructor(config?: AppConfig) {\n this.events = mitt();\n // Temporary config until init() is called (or passed in constructor)\n this.config = config || { name: 'Bootstrapping', logger: { level: 'info' } } as AppConfig;\n this.logger = createLogger(this.config.name, this.config.logger?.level);\n }\n\n async init() {\n // If config was not passed, load it\n if (this.config.name === 'Bootstrapping') {\n this.config = await loadAppConfig();\n this.logger = createLogger(this.config.name, this.config.logger?.level);\n }\n\n // Initialize OpenTelemetry SDK before drivers (so auto-instrumentation patches libraries)\n if (this.config.otel && this.config.otel.enabled !== false) {\n await initOtel(this.config.otel, this.config.name);\n this.logger.info({ endpoint: this.config.otel.endpoint }, 'OpenTelemetry initialized');\n }\n\n this.logger.info('Initializing App...');\n\n for (const driver of this.drivers) {\n if (driver.init) {\n this.logger.debug(`Initializing driver: ${driver.name}`);\n await driver.init(this);\n }\n }\n }\n\n register(driver: Driver) {\n this.drivers.push(driver);\n return this;\n }\n\n use(plugin: Plugin) {\n plugin.install(this);\n return this;\n }\n\n on(event: string, handler: (ctx: Context) => Promise<void> | void) {\n this.events.on(event, async (payload) => {\n const ctx: Context = {\n app: this,\n logger: this.logger.child({ event }),\n payload,\n reply: (data) => this.events.emit(`${event}:reply`, data)\n };\n\n try {\n await handler(ctx);\n } catch (err) {\n this.logger.error({ err, event }, 'Error handling event');\n }\n });\n return this;\n }\n\n emit(event: string, payload: any) {\n this.events.emit(event, payload);\n }\n\n async start() {\n await this.init();\n this.logger.info('Starting drivers...');\n await Promise.all(this.drivers.map(d => d.start ? d.start() : Promise.resolve()));\n this.logger.info('App started successfully 🚀');\n }\n\n async stop() {\n this.logger.info('Stopping app...');\n const results = await Promise.allSettled(this.drivers.map(d => d.stop ? d.stop() : Promise.resolve()));\n\n const failures = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected');\n if (failures.length > 0) {\n this.logger.error({ failures }, 'Some drivers failed to stop');\n throw new LifecycleError('Some drivers failed to stop', {\n failures,\n context: { driverCount: this.drivers.length, failedCount: failures.length },\n });\n }\n\n // Shutdown OTel SDK after drivers (flush pending spans/metrics)\n await shutdownOtel();\n\n this.logger.info('App stopped.');\n }\n}\n","import pino from 'pino';\n\nexport const createLogger = (name: string, level: string = 'info') => {\n const isDev = process.env.NODE_ENV !== 'production';\n return pino({\n name,\n level,\n redact: {\n paths: [\n 'password',\n '*.password',\n 'pass',\n '*.pass',\n 'apiKey',\n '*.apiKey',\n '*.apiSecret',\n 'token',\n '*.token',\n '*.authToken',\n 'secret',\n '*.secret',\n 'config.env',\n '*.data'\n ],\n censor: '[REDACTED]'\n },\n ...(isDev && {\n transport: {\n target: 'pino-pretty',\n options: {\n colorize: true\n }\n }\n })\n });\n};\n\nexport type Logger = pino.Logger;\n","import { loadConfig } from 'c12';\nimport { AppConfigSchema, type AppConfigOutput } from './schema';\nimport type { AppConfig } from '../types';\n\nexport async function loadAppConfig(cwd: string = process.cwd()): Promise<AppConfig> {\n const { config } = await loadConfig<AppConfig>({\n name: 'app',\n configFile: 'app.config',\n cwd,\n dotenv: true\n });\n\n const validConfig = AppConfigSchema.parse(config || {});\n return validConfig as AppConfig;\n}\n","import { z } from 'zod';\n\nexport const AppConfigSchema = z.object({\n name: z.string().default('IskraApp'),\n debug: z.boolean().default(false),\n logger: z.object({\n level: z.string().default('info')\n }).default({}),\n otel: z.object({\n enabled: z.boolean().default(true),\n endpoint: z.string().default('http://localhost:4318'),\n serviceName: z.string().optional(),\n serviceVersion: z.string().default('0.1.0'),\n environment: z.string().optional(),\n metricIntervalMs: z.number().default(60_000),\n resourceAttributes: z.record(z.string()).optional(),\n instrumentations: z.record(z.object({ enabled: z.boolean().optional() })).optional(),\n }).optional(),\n processes: z.record(z.object({\n command: z.string(),\n args: z.array(z.string()).optional(),\n mode: z.enum(['daemon', 'oneshot', 'stdio']).default('daemon'),\n restartOnCrash: z.boolean().default(false),\n env: z.record(z.string()).optional()\n })).optional()\n});\n\nexport type AppConfigInput = z.input<typeof AppConfigSchema>;\nexport type AppConfigOutput = z.output<typeof AppConfigSchema>;\n","export const ErrorCodes = {\n // General\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n NOT_INITIALIZED: 'NOT_INITIALIZED',\n ADAPTER_ERROR: 'ADAPTER_ERROR',\n\n // Config\n CONFIG_INVALID: 'CONFIG_INVALID',\n CONFIG_MISSING: 'CONFIG_MISSING',\n\n // Driver\n DRIVER_INIT_FAILED: 'DRIVER_INIT_FAILED',\n DRIVER_START_FAILED: 'DRIVER_START_FAILED',\n DRIVER_STOP_FAILED: 'DRIVER_STOP_FAILED',\n DRIVER_NOT_FOUND: 'DRIVER_NOT_FOUND',\n\n // Plugin\n PLUGIN_INSTALL_FAILED: 'PLUGIN_INSTALL_FAILED',\n\n // Lifecycle\n LIFECYCLE_START_FAILED: 'LIFECYCLE_START_FAILED',\n LIFECYCLE_STOP_FAILED: 'LIFECYCLE_STOP_FAILED',\n\n // HTTP\n BAD_REQUEST: 'BAD_REQUEST',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n NOT_FOUND: 'NOT_FOUND',\n CONFLICT: 'CONFLICT',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n\n // Data\n DATABASE_ERROR: 'DATABASE_ERROR',\n CONNECTION_ERROR: 'CONNECTION_ERROR',\n QUERY_ERROR: 'QUERY_ERROR',\n MIGRATION_ERROR: 'MIGRATION_ERROR',\n CACHE_ERROR: 'CACHE_ERROR',\n\n // Storage / Upload\n STORAGE_ERROR: 'STORAGE_ERROR',\n FILE_UPLOAD_ERROR: 'FILE_UPLOAD_ERROR',\n FILE_NOT_FOUND: 'FILE_NOT_FOUND',\n\n // Worker\n QUEUE_ERROR: 'QUEUE_ERROR',\n JOB_ERROR: 'JOB_ERROR',\n\n // Socket\n SOCKET_CONNECTION_ERROR: 'SOCKET_CONNECTION_ERROR',\n SOCKET_MESSAGE_ERROR: 'SOCKET_MESSAGE_ERROR',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\nexport interface IskraErrorOptions {\n code: ErrorCode;\n cause?: Error;\n context?: Record<string, unknown>;\n}\n\n/**\n * Clase base para todos los errores del framework Iskra.\n * Soporta encadenamiento de errores vía `cause` y metadata arbitraria en `context`.\n */\nexport class IskraError extends Error {\n public readonly code: ErrorCode;\n public readonly context: Record<string, unknown>;\n\n constructor(message: string, options: IskraErrorOptions) {\n super(message, { cause: options.cause });\n this.name = 'IskraError';\n this.code = options.code;\n this.context = options.context ?? {};\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n context: this.context,\n ...(this.cause instanceof Error ? { cause: this.cause.message } : {}),\n };\n }\n}\n\n// ─── Config Errors ───────────────────────────────────────────────────────────\n\nexport class ConfigError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.CONFIG_INVALID, ...options });\n this.name = 'ConfigError';\n }\n}\n\n// ─── Driver Errors ───────────────────────────────────────────────────────────\n\nexport class DriverError extends IskraError {\n constructor(message: string, options?: { code?: ErrorCode; cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: options?.code ?? ErrorCodes.DRIVER_INIT_FAILED, cause: options?.cause, context: options?.context });\n this.name = 'DriverError';\n }\n}\n\n// ─── Plugin Errors ───────────────────────────────────────────────────────────\n\nexport class PluginError extends IskraError {\n constructor(message: string, options?: { cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.PLUGIN_INSTALL_FAILED, ...options });\n this.name = 'PluginError';\n }\n}\n\n// ─── Lifecycle Errors ────────────────────────────────────────────────────────\n\nexport class LifecycleError extends IskraError {\n public readonly failures: PromiseRejectedResult[];\n\n constructor(message: string, options: { failures?: PromiseRejectedResult[]; cause?: Error; context?: Record<string, unknown> }) {\n super(message, { code: ErrorCodes.LIFECYCLE_STOP_FAILED, cause: options.cause, context: options.context });\n this.name = 'LifecycleError';\n this.failures = options.failures ?? [];\n }\n}\n","/**\n * OpenTelemetry SDK initialization for Iskra apps.\n *\n * All OTel packages are dynamic-imported so they remain optional peer deps.\n * Apps that don't set `config.otel` never load this code.\n *\n * The module specifiers are held in variables rather than passed as string\n * literals to `import()`. This stops `tsc` from statically resolving the\n * optional `@opentelemetry/*` packages at compile time, so the module\n * typechecks cleanly even when those peer deps are absent. At runtime the\n * imports still resolve normally, and a missing package degrades to a clear,\n * actionable error instead of an unhandled module-resolution failure.\n */\nimport type { OtelConfig } from './types';\n\nlet sdkInstance: any = null;\n\n/** Optional OTel package specifiers, indirected so tsc does not resolve them statically. */\nconst OTEL_MODULES = {\n sdkNode: '@opentelemetry/sdk-node',\n autoInstrumentations: '@opentelemetry/auto-instrumentations-node',\n traceExporter: '@opentelemetry/exporter-trace-otlp-http',\n metricExporter: '@opentelemetry/exporter-metrics-otlp-http',\n sdkMetrics: '@opentelemetry/sdk-metrics',\n resources: '@opentelemetry/resources',\n semconv: '@opentelemetry/semantic-conventions',\n} as const;\n\n/** Import an optional module by specifier; the indirection keeps tsc from resolving it. */\nfunction importOptional(specifier: string): Promise<any> {\n return import(specifier);\n}\n\n/**\n * Initialize the OpenTelemetry NodeSDK with OTLP exporters.\n * Must be called before any drivers start so auto-instrumentation can patch libraries.\n */\nexport async function initOtel(config: OtelConfig, appName: string): Promise<void> {\n if (sdkInstance) return; // Already initialized\n\n try {\n const [\n { NodeSDK },\n { getNodeAutoInstrumentations },\n { OTLPTraceExporter },\n { OTLPMetricExporter },\n { PeriodicExportingMetricReader },\n { Resource },\n semconv,\n ] = await Promise.all([\n importOptional(OTEL_MODULES.sdkNode),\n importOptional(OTEL_MODULES.autoInstrumentations),\n importOptional(OTEL_MODULES.traceExporter),\n importOptional(OTEL_MODULES.metricExporter),\n importOptional(OTEL_MODULES.sdkMetrics),\n importOptional(OTEL_MODULES.resources),\n importOptional(OTEL_MODULES.semconv),\n ]);\n\n const serviceName = config.serviceName || appName;\n const endpoint = config.endpoint || 'http://localhost:4318';\n\n const resource = new Resource({\n [semconv.ATTR_SERVICE_NAME]: serviceName,\n [semconv.ATTR_SERVICE_VERSION]: config.serviceVersion || '0.1.0',\n 'deployment.environment': config.environment || process.env.NODE_ENV || 'development',\n ...config.resourceAttributes,\n });\n\n const instrumentationOverrides: Record<string, any> = {\n '@opentelemetry/instrumentation-fs': { enabled: false },\n ...config.instrumentations,\n };\n\n const sdk = new NodeSDK({\n resource,\n traceExporter: new OTLPTraceExporter({ url: `${endpoint}/v1/traces` }),\n metricReader: new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({ url: `${endpoint}/v1/metrics` }),\n exportIntervalMillis: config.metricIntervalMs ?? 60_000,\n }),\n instrumentations: [getNodeAutoInstrumentations(instrumentationOverrides)],\n });\n\n sdk.start();\n sdkInstance = sdk;\n } catch (err: any) {\n // Provide a clear error when OTel packages are not installed\n if (err?.code === 'ERR_MODULE_NOT_FOUND' || err?.code === 'MODULE_NOT_FOUND' || err?.message?.includes('Cannot find')) {\n throw new Error(\n `[iskra/otel] OpenTelemetry packages are not installed. ` +\n `Install them with: bun add @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ` +\n `@opentelemetry/exporter-trace-otlp-http @opentelemetry/exporter-metrics-otlp-http ` +\n `@opentelemetry/sdk-metrics @opentelemetry/resources @opentelemetry/semantic-conventions`,\n );\n }\n throw err;\n }\n}\n\n/**\n * Gracefully shut down the OTel SDK, flushing any pending spans/metrics.\n */\nexport async function shutdownOtel(): Promise<void> {\n if (sdkInstance) {\n await sdkInstance.shutdown();\n sdkInstance = null;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iskra-bun/core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Nucleo del framework Iskra: App, DI, eventos, logger, errores.",
5
5
  "keywords": [
6
6
  "iskra",
@@ -1,15 +1,37 @@
1
1
  import pino from 'pino';
2
2
 
3
3
  export const createLogger = (name: string, level: string = 'info') => {
4
+ const isDev = process.env.NODE_ENV !== 'production';
4
5
  return pino({
5
6
  name,
6
7
  level,
7
- transport: {
8
- target: 'pino-pretty',
9
- options: {
10
- colorize: true
8
+ redact: {
9
+ paths: [
10
+ 'password',
11
+ '*.password',
12
+ 'pass',
13
+ '*.pass',
14
+ 'apiKey',
15
+ '*.apiKey',
16
+ '*.apiSecret',
17
+ 'token',
18
+ '*.token',
19
+ '*.authToken',
20
+ 'secret',
21
+ '*.secret',
22
+ 'config.env',
23
+ '*.data'
24
+ ],
25
+ censor: '[REDACTED]'
26
+ },
27
+ ...(isDev && {
28
+ transport: {
29
+ target: 'pino-pretty',
30
+ options: {
31
+ colorize: true
32
+ }
11
33
  }
12
- }
34
+ })
13
35
  });
14
36
  };
15
37
 
package/src/types.ts CHANGED
@@ -64,6 +64,15 @@ export interface AppConfig {
64
64
  [key: string]: any;
65
65
  }
66
66
 
67
+ export interface RestartBackoffConfig {
68
+ /** Initial delay in ms before the first restart. Default: 1000 */
69
+ initialMs?: number;
70
+ /** Maximum delay cap in ms. Default: 30000 */
71
+ maxMs?: number;
72
+ /** Multiplier applied to the delay after each restart. Default: 2 */
73
+ factor?: number;
74
+ }
75
+
67
76
  export interface ProcessConfig {
68
77
  command: string;
69
78
  args?: string[];
@@ -72,4 +81,6 @@ export interface ProcessConfig {
72
81
  maxRestarts?: number;
73
82
  restartCooldown?: number;
74
83
  env?: Record<string, string>;
84
+ /** Exponential backoff settings for restarts. Defaults to 1000 ms flat (no backoff). */
85
+ restartBackoff?: RestartBackoffConfig;
75
86
  }