@jmlq/logger 0.1.0-alpha.0 → 0.1.0-alpha.2

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.
@@ -1,9 +1,9 @@
1
- import { GetLogsFilter, ILogDatasource, Log } from "..";
1
+ import { IGetLogsFilter, ILogDatasource, ILog } from "../config";
2
2
  export declare class CompositeDatasource implements ILogDatasource {
3
3
  private readonly datasources;
4
4
  constructor(datasources: ILogDatasource[]);
5
- save(log: Log): Promise<void>;
6
- find(filter?: GetLogsFilter): Promise<Log[]>;
5
+ save(log: ILog): Promise<void>;
6
+ find(filter?: IGetLogsFilter): Promise<ILog[]>;
7
7
  flush(): Promise<void>;
8
8
  dispose(): Promise<void>;
9
9
  }
@@ -7,17 +7,48 @@ class CompositeDatasource {
7
7
  this.datasources = datasources;
8
8
  }
9
9
  async save(log) {
10
- await Promise.allSettled(this.datasources.map((ds) => ds.save(log)));
10
+ const results = await Promise.allSettled(this.datasources.map((ds) => ds.save(log)));
11
+ for (const r of results) {
12
+ if (r.status === "rejected") {
13
+ // Evita romper el flujo si un destino falla.
14
+ // Aquí podrías enrutar a "dead-letter", métricas, etc.
15
+ // eslint-disable-next-line no-console
16
+ console.warn("[CompositeDatasource] save failed:", r.reason);
17
+ }
18
+ }
11
19
  }
12
20
  async find(filter = {}) {
13
- const primary = this.datasources[0];
14
- return (await primary?.find?.(filter)) ?? [];
21
+ for (const ds of this.datasources) {
22
+ if (typeof ds.find === "function") {
23
+ try {
24
+ return await ds.find(filter);
25
+ }
26
+ catch (e) {
27
+ // eslint-disable-next-line no-console
28
+ console.warn("[CompositeDatasource] find failed on a datasource:", e);
29
+ // probar siguiente datasource
30
+ }
31
+ }
32
+ }
33
+ return [];
15
34
  }
16
35
  async flush() {
17
- await Promise.all(this.datasources.map((ds) => ds.flush?.() ?? Promise.resolve()));
36
+ const results = await Promise.allSettled(this.datasources.map((ds) => ds.flush?.() ?? Promise.resolve()));
37
+ for (const r of results) {
38
+ if (r.status === "rejected") {
39
+ // eslint-disable-next-line no-console
40
+ console.warn("[CompositeDatasource] flush failed:", r.reason);
41
+ }
42
+ }
18
43
  }
19
44
  async dispose() {
20
- await Promise.all(this.datasources.map((ds) => ds.dispose?.() ?? Promise.resolve()));
45
+ const results = await Promise.allSettled(this.datasources.map((ds) => ds.dispose?.() ?? Promise.resolve()));
46
+ for (const r of results) {
47
+ if (r.status === "rejected") {
48
+ // eslint-disable-next-line no-console
49
+ console.warn("[CompositeDatasource] dispose failed:", r.reason);
50
+ }
51
+ }
21
52
  }
22
53
  }
23
54
  exports.CompositeDatasource = CompositeDatasource;
@@ -0,0 +1,2 @@
1
+ export * from "./interfaces";
2
+ export * from "./types";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./interfaces"), exports);
18
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,67 @@
1
+ import { LogLevel, LogMessage, PiiReplacement } from "..";
2
+ export interface ILog {
3
+ level: LogLevel;
4
+ message: string;
5
+ meta?: unknown;
6
+ timestamp: number;
7
+ }
8
+ export interface IGetLogsFilter {
9
+ levelMin?: LogLevel;
10
+ since?: number;
11
+ until?: number;
12
+ }
13
+ export interface ILogDatasource {
14
+ save(log: ILog): Promise<void>;
15
+ find?(filter?: IGetLogsFilter): Promise<ILog[]>;
16
+ flush?(): Promise<void>;
17
+ dispose?(): Promise<void>;
18
+ }
19
+ export interface ILogger {
20
+ trace: (msg: LogMessage, meta?: unknown) => Promise<void>;
21
+ debug: (msg: LogMessage, meta?: unknown) => Promise<void>;
22
+ info: (msg: LogMessage, meta?: unknown) => Promise<void>;
23
+ warn: (msg: LogMessage, meta?: unknown) => Promise<void>;
24
+ error: (msg: LogMessage, meta?: unknown) => Promise<void>;
25
+ fatal: (msg: LogMessage, meta?: unknown) => Promise<void>;
26
+ flush?: () => Promise<void>;
27
+ dispose?: () => Promise<void>;
28
+ }
29
+ export interface IPiiPattern {
30
+ /** (Opcional) Nombre del patrón para debug/telemetría */
31
+ name?: string;
32
+ /** Expresión regular a aplicar sobre strings */
33
+ regex: RegExp;
34
+ /** Reemplazo, por defecto "[REDACTED]" si no se provee */
35
+ replacement?: PiiReplacement;
36
+ }
37
+ export interface IPiiRedactorOptions {
38
+ /** Habilita/deshabilita redacción (permite control fino además del flag redactPII) */
39
+ enabled?: boolean;
40
+ /**
41
+ * Incluye patrones default (true) y fusiona con los custom. Si false, usa solo los personalizados
42
+ * por el cliente.
43
+ */
44
+ includeDefaultPatterns?: boolean;
45
+ /** Patrones adicionales o propios del cliente */
46
+ patterns?: IPiiPattern[];
47
+ /**
48
+ * Redactar por nombre de clave (deep). Cualquier key que haga match será redacatada
49
+ * con "[REDACTED]". Acepta exacto o RegExp.
50
+ */
51
+ redactKeys?: Array<string | RegExp>;
52
+ /**
53
+ * Claves que NO se deben redactar aunque coincidan con patrones o redactKeys.
54
+ * Prioridad: preserveKeys > redactKeys > patterns.
55
+ */
56
+ preserveKeys?: Array<string | RegExp>;
57
+ }
58
+ export interface ICreateLoggerOptions {
59
+ minLevel?: LogLevel;
60
+ /** compatibilidad con lo que ya tienes */
61
+ redactPII?: boolean;
62
+ /** NUEVO: bloque de configuración PII */
63
+ pii?: IPiiRedactorOptions & {
64
+ /** atajo para permitir que el cliente mande solo patterns sin escribir PiiRedactorOptions */
65
+ patterns?: IPiiPattern[];
66
+ };
67
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ // ---- Contratos ----
3
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ export declare enum LogLevel {
2
+ TRACE = 0,
3
+ DEBUG = 1,
4
+ INFO = 2,
5
+ WARN = 3,
6
+ ERROR = 4,
7
+ FATAL = 5
8
+ }
9
+ export type PiiReplacement = string | ((match: string, keyPath: string[]) => string);
10
+ export type LogMessage = string | Record<string, unknown> | Error;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogLevel = void 0;
4
+ // ---- Types ----
5
+ var LogLevel;
6
+ (function (LogLevel) {
7
+ LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
8
+ LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
9
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
10
+ LogLevel[LogLevel["WARN"] = 3] = "WARN";
11
+ LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
12
+ LogLevel[LogLevel["FATAL"] = 5] = "FATAL";
13
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
@@ -0,0 +1 @@
1
+ export * from "./services";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./services"), exports);
@@ -0,0 +1 @@
1
+ export * from "./pii-redactor";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./pii-redactor"), exports);
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Domain Service: PiiRedactor
3
+ *
4
+ * Propósito:
5
+ * - Redactar/anonimizar PII en strings, objetos y arrays (deep).
6
+ * - Permitir que el cliente pase patrones adicionales/propios.
7
+ * - Controlar redacción por nombre de clave (whitelist/blacklist).
8
+ */
9
+ import { IPiiPattern, IPiiRedactorOptions } from "../../config";
10
+ export declare class PiiRedactor {
11
+ private readonly enabled;
12
+ private readonly patterns;
13
+ private readonly redactKeys;
14
+ private readonly preserveKeys;
15
+ constructor(opts?: IPiiRedactorOptions);
16
+ /** Patrones por defecto (seguros y genéricos). Ajusta según tu dominio/región si lo deseas. */
17
+ static defaultPatterns(): IPiiPattern[];
18
+ /** Punto de entrada público: redacción deep de cualquier estructura */
19
+ redact<T = unknown>(value: T): T;
20
+ /** === Internos === */
21
+ private redactValue;
22
+ private applyPatterns;
23
+ private isRedactedKey;
24
+ private isPreservedKey;
25
+ private keyRedactionReplacement;
26
+ private clone;
27
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ /**
3
+ * Domain Service: PiiRedactor
4
+ *
5
+ * Propósito:
6
+ * - Redactar/anonimizar PII en strings, objetos y arrays (deep).
7
+ * - Permitir que el cliente pase patrones adicionales/propios.
8
+ * - Controlar redacción por nombre de clave (whitelist/blacklist).
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.PiiRedactor = void 0;
12
+ class PiiRedactor {
13
+ constructor(opts = {}) {
14
+ const { enabled = true, includeDefaultPatterns = true, patterns = [], redactKeys = [], preserveKeys = [], } = opts;
15
+ this.enabled = enabled;
16
+ this.patterns = includeDefaultPatterns
17
+ ? [...PiiRedactor.defaultPatterns(), ...patterns]
18
+ : [...patterns];
19
+ this.redactKeys = redactKeys;
20
+ this.preserveKeys = preserveKeys;
21
+ }
22
+ /** Patrones por defecto (seguros y genéricos). Ajusta según tu dominio/región si lo deseas. */
23
+ static defaultPatterns() {
24
+ return [
25
+ // Tarjetas (13–19 dígitos con espacios o guiones) — patrón simple
26
+ {
27
+ name: "credit_card",
28
+ regex: /\b(?:\d[ -]*?){13,19}\b/g,
29
+ replacement: "[REDACTED_CARD]",
30
+ },
31
+ // Emails
32
+ {
33
+ name: "email",
34
+ regex: /\b[\w._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
35
+ replacement: "[REDACTED_EMAIL]",
36
+ },
37
+ // Teléfonos: 10+ dígitos (ajusta a tu región si hace falta)
38
+ {
39
+ name: "phone",
40
+ regex: /\b\d{10,}\b/g,
41
+ replacement: "[REDACTED_PHONE]",
42
+ },
43
+ // Documento genérico: ABC-123456… o similar
44
+ {
45
+ name: "document_id",
46
+ regex: /\b[A-Z]{1,3}-?\d{6,10}\b/g,
47
+ replacement: "[REDACTED_ID]",
48
+ },
49
+ // IPv4
50
+ {
51
+ name: "ipv4",
52
+ regex: /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.|$)){4}\b/g,
53
+ replacement: "[REDACTED_IP]",
54
+ },
55
+ ];
56
+ }
57
+ /** Punto de entrada público: redacción deep de cualquier estructura */
58
+ redact(value) {
59
+ if (!this.enabled)
60
+ return value;
61
+ return this.redactValue(value, []);
62
+ }
63
+ /** === Internos === */
64
+ redactValue(value, keyPath) {
65
+ // Strings → aplicar patrones
66
+ if (typeof value === "string") {
67
+ return this.applyPatterns(value, keyPath);
68
+ }
69
+ // Números / boolean / null / undefined → retornar tal cual
70
+ if (typeof value === "number" ||
71
+ typeof value === "boolean" ||
72
+ value === null ||
73
+ typeof value === "undefined") {
74
+ return value;
75
+ }
76
+ // Arrays → deep
77
+ if (Array.isArray(value)) {
78
+ return value.map((v, i) => this.redactValue(v, [...keyPath, String(i)]));
79
+ }
80
+ // Objetos → deep + reglas por nombre de clave
81
+ if (typeof value === "object") {
82
+ const src = value;
83
+ const out = {};
84
+ for (const [k, v] of Object.entries(src)) {
85
+ const path = [...keyPath, k];
86
+ if (this.isPreservedKey(k)) {
87
+ out[k] = this.clone(v); // explícitamente preservado
88
+ continue;
89
+ }
90
+ if (this.isRedactedKey(k)) {
91
+ out[k] = this.keyRedactionReplacement(k, path);
92
+ continue;
93
+ }
94
+ out[k] = this.redactValue(v, path);
95
+ }
96
+ return out;
97
+ }
98
+ // funciones, símbolos, etc.
99
+ return value;
100
+ }
101
+ applyPatterns(text, keyPath) {
102
+ let result = text;
103
+ for (const p of this.patterns) {
104
+ const replacement = p.replacement ?? "[REDACTED]";
105
+ // Soporta función de reemplazo con contexto (ruta de claves)
106
+ if (typeof replacement === "function") {
107
+ result = result.replace(p.regex, (m) => replacement(m, keyPath));
108
+ }
109
+ else {
110
+ result = result.replace(p.regex, replacement);
111
+ }
112
+ }
113
+ return result;
114
+ }
115
+ isRedactedKey(key) {
116
+ return this.redactKeys.some((r) => typeof r === "string" ? r === key : r.test(key));
117
+ }
118
+ isPreservedKey(key) {
119
+ return this.preserveKeys.some((r) => typeof r === "string" ? r === key : r.test(key));
120
+ }
121
+ keyRedactionReplacement(_key, _path) {
122
+ return "[REDACTED]";
123
+ }
124
+ clone(v) {
125
+ if (v && typeof v === "object") {
126
+ try {
127
+ return JSON.parse(JSON.stringify(v));
128
+ }
129
+ catch {
130
+ // fallback shallow
131
+ if (Array.isArray(v))
132
+ return [...v];
133
+ return { ...v };
134
+ }
135
+ }
136
+ return v;
137
+ }
138
+ }
139
+ exports.PiiRedactor = PiiRedactor;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export * from "./interfaces";
2
- export * from "./Factory";
3
- export * from "./Composite";
1
+ export * from "./composite";
2
+ export * from "./domain";
3
+ export * from "./presentation";
4
+ export * from "./config";
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./interfaces"), exports);
18
- __exportStar(require("./Factory"), exports);
19
- __exportStar(require("./Composite"), exports);
17
+ __exportStar(require("./composite"), exports);
18
+ __exportStar(require("./domain"), exports);
19
+ __exportStar(require("./presentation"), exports);
20
+ __exportStar(require("./config"), exports);
@@ -0,0 +1,2 @@
1
+ import { ICreateLoggerOptions, ILogDatasource, ILogger } from "../..";
2
+ export declare function createLogger(ds: ILogDatasource, opts?: ICreateLoggerOptions): ILogger;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLogger = createLogger;
4
+ const __1 = require("../..");
5
+ /**
6
+ * Normaliza cualquier LogMessage a string, aplicando PII (si procede) ANTES de serializar.
7
+ */
8
+ function normalizeLogMessage(value, redactEnabled, redactor) {
9
+ const v = redactEnabled ? redactor.redact(value) : value;
10
+ if (typeof v === "string")
11
+ return v;
12
+ // Si soportas Error en LogMessage, lo serializamos “bonito”
13
+ if (v instanceof Error) {
14
+ const err = v;
15
+ const obj = {
16
+ error: err.name || "Error",
17
+ message: err.message,
18
+ stack: err.stack,
19
+ code: err.code,
20
+ cause: err.cause,
21
+ };
22
+ try {
23
+ return JSON.stringify(obj);
24
+ }
25
+ catch {
26
+ return `${obj.error}: ${obj.message}`;
27
+ }
28
+ }
29
+ // Objeto → JSON
30
+ try {
31
+ return JSON.stringify(v);
32
+ }
33
+ catch {
34
+ // fallback ultra defensivo
35
+ return String(v);
36
+ }
37
+ }
38
+ function createLogger(ds, opts = {}) {
39
+ const minLevel = opts.minLevel ?? __1.LogLevel.INFO;
40
+ // Compatibilidad: redactPII (boolean) o pii.enabled
41
+ const redactEnabled = (opts.redactPII ?? false) || (opts.pii?.enabled ?? false);
42
+ // Inicializa redactor con opciones del cliente (incluye defaults por defecto)
43
+ const redactor = new __1.PiiRedactor({
44
+ enabled: redactEnabled,
45
+ includeDefaultPatterns: opts.pii?.includeDefaultPatterns ?? true,
46
+ patterns: opts.pii?.patterns,
47
+ redactKeys: opts.pii?.redactKeys,
48
+ preserveKeys: opts.pii?.preserveKeys,
49
+ });
50
+ const emit = async (level, message, meta) => {
51
+ if (level < minLevel)
52
+ return;
53
+ const msgStr = normalizeLogMessage(message, redactEnabled, redactor);
54
+ const metaVal = redactEnabled ? redactor.redact(meta) : meta;
55
+ const payload = {
56
+ level,
57
+ timestamp: Date.now(),
58
+ message: msgStr, // <- garantizamos string
59
+ meta: metaVal,
60
+ };
61
+ await ds.save(payload);
62
+ };
63
+ const logger = {
64
+ trace: (m, meta) => emit(__1.LogLevel.TRACE, m, meta),
65
+ debug: (m, meta) => emit(__1.LogLevel.DEBUG, m, meta),
66
+ info: (m, meta) => emit(__1.LogLevel.INFO, m, meta),
67
+ warn: (m, meta) => emit(__1.LogLevel.WARN, m, meta),
68
+ error: (m, meta) => emit(__1.LogLevel.ERROR, m, meta),
69
+ fatal: (m, meta) => emit(__1.LogLevel.FATAL, m, meta),
70
+ flush: () => ds.flush?.() ?? Promise.resolve(),
71
+ dispose: () => ds.dispose?.() ?? Promise.resolve(),
72
+ };
73
+ return logger;
74
+ }
@@ -0,0 +1 @@
1
+ export * from "./factory";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./factory"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmlq/logger",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "author": "MLahuasi",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  ],
10
10
  "scripts": {
11
11
  "build": "tsc -p tsconfig.json",
12
- "prepublishOnly": "pnpm build"
12
+ "prepublishOnly": "npm run build"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^24.3.0",