@jmlq/logger 0.1.0-alpha.7 → 0.1.0-alpha.8

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.
Files changed (145) hide show
  1. package/README.md +225 -733
  2. package/dist/examples/data-source-service.example.d.ts +3 -0
  3. package/dist/examples/data-source-service.example.js +174 -0
  4. package/dist/examples/flush-buffers-use-case.example.d.ts +3 -0
  5. package/dist/examples/flush-buffers-use-case.example.js +60 -0
  6. package/dist/examples/get-logs-use-case.example.d.ts +3 -0
  7. package/dist/examples/get-logs-use-case.example.js +110 -0
  8. package/dist/examples/index.example.d.ts +8 -0
  9. package/dist/examples/index.example.js +116 -0
  10. package/dist/examples/logger-factory.example.d.ts +39 -0
  11. package/dist/examples/logger-factory.example.js +158 -0
  12. package/dist/examples/normalize-message.example.d.ts +3 -0
  13. package/dist/examples/normalize-message.example.js +80 -0
  14. package/dist/examples/pii-redactor.example.d.ts +3 -0
  15. package/dist/examples/pii-redactor.example.js +129 -0
  16. package/dist/examples/save-log-use-case.example.d.ts +3 -0
  17. package/dist/examples/save-log-use-case.example.js +150 -0
  18. package/dist/examples/to-log-level.example.d.ts +3 -0
  19. package/dist/examples/to-log-level.example.js +49 -0
  20. package/dist/examples/to-pii-regex.example.d.ts +3 -0
  21. package/dist/examples/to-pii-regex.example.js +75 -0
  22. package/dist/{presentation → src/application}/factory/create-logger.d.ts +1 -1
  23. package/dist/{presentation → src/application}/factory/create-logger.js +3 -3
  24. package/dist/src/application/factory/index.d.ts +1 -0
  25. package/dist/{presentation → src/application}/factory/index.js +1 -1
  26. package/dist/src/application/factory/logger.factory.d.ts +12 -0
  27. package/dist/src/application/factory/logger.factory.js +74 -0
  28. package/dist/src/application/index.d.ts +2 -0
  29. package/dist/src/application/index.js +18 -0
  30. package/dist/src/application/use-cases/flush-buffers.use-case.d.ts +6 -0
  31. package/dist/{application/use-cases/flush-buffers.js → src/application/use-cases/flush-buffers.use-case.js} +3 -3
  32. package/dist/src/application/use-cases/get-logs.use-case.d.ts +8 -0
  33. package/dist/{application/use-cases/get-logs.js → src/application/use-cases/get-logs.use-case.js} +3 -3
  34. package/dist/src/application/use-cases/index.d.ts +3 -0
  35. package/dist/{application → src/application}/use-cases/index.js +3 -3
  36. package/dist/src/application/use-cases/save-log.use-case.d.ts +8 -0
  37. package/dist/src/application/use-cases/save-log.use-case.js +26 -0
  38. package/dist/src/domain/index.d.ts +6 -0
  39. package/dist/src/domain/index.js +22 -0
  40. package/dist/src/domain/ports/create-logger-options.port.d.ts +7 -0
  41. package/dist/src/domain/ports/index.d.ts +6 -0
  42. package/dist/src/domain/ports/index.js +22 -0
  43. package/dist/src/domain/ports/log-datasource.port.d.ts +9 -0
  44. package/dist/src/domain/ports/logger-factory-config.port.d.ts +28 -0
  45. package/dist/src/domain/ports/logger-service.port.d.ts +19 -0
  46. package/dist/{domain/contracts/logger.d.ts → src/domain/ports/logger.port.d.ts} +0 -9
  47. package/dist/src/domain/ports/logger.port.js +2 -0
  48. package/dist/src/domain/ports/pii-redactor.port.d.ts +5 -0
  49. package/dist/src/domain/ports/pii-redactor.port.js +2 -0
  50. package/dist/src/domain/request/get-logs-filter.props.d.ts +9 -0
  51. package/dist/src/domain/request/get-logs-filter.props.js +2 -0
  52. package/dist/src/domain/request/index.d.ts +5 -0
  53. package/dist/src/domain/request/index.js +21 -0
  54. package/dist/src/domain/request/log.props.d.ts +7 -0
  55. package/dist/src/domain/request/log.props.js +2 -0
  56. package/dist/src/domain/request/pii-options.props.d.ts +8 -0
  57. package/dist/src/domain/request/pii-options.props.js +2 -0
  58. package/dist/src/domain/request/pii-replacement.props.d.ts +5 -0
  59. package/dist/src/domain/request/pii-replacement.props.js +2 -0
  60. package/dist/src/domain/request/save-log.props.d.ts +7 -0
  61. package/dist/src/domain/request/save-log.props.js +2 -0
  62. package/dist/src/domain/response/index.d.ts +1 -0
  63. package/dist/{domain/value-objects → src/domain/response}/index.js +1 -1
  64. package/dist/src/domain/response/log.response.d.ts +7 -0
  65. package/dist/src/domain/response/log.response.js +2 -0
  66. package/dist/src/domain/services/index.d.ts +4 -0
  67. package/dist/{domain/contracts → src/domain/services}/index.js +4 -3
  68. package/dist/src/domain/services/log-level.service.d.ts +2 -0
  69. package/dist/src/domain/services/log-level.service.js +27 -0
  70. package/dist/src/domain/services/message-normalizer.service.d.ts +3 -0
  71. package/dist/src/domain/services/message-normalizer.service.js +8 -0
  72. package/dist/src/domain/services/pii-pattern.service.d.ts +2 -0
  73. package/dist/src/domain/services/pii-pattern.service.js +13 -0
  74. package/dist/src/domain/services/pii-redactor.d.ts +10 -0
  75. package/dist/{domain → src/domain}/services/pii-redactor.js +8 -17
  76. package/dist/src/domain/services/pii-redactor.service.d.ts +10 -0
  77. package/dist/src/domain/services/pii-redactor.service.js +68 -0
  78. package/dist/src/domain/types/index.d.ts +1 -0
  79. package/dist/src/domain/types/index.js +17 -0
  80. package/dist/src/domain/types/log-message.type.d.ts +1 -0
  81. package/dist/src/domain/types/log-message.type.js +2 -0
  82. package/dist/src/domain/value-objects/index.d.ts +1 -0
  83. package/dist/{domain/services → src/domain/value-objects}/index.js +1 -1
  84. package/dist/{domain/value-objects/log-level.d.ts → src/domain/value-objects/log-level.vo.d.ts} +0 -1
  85. package/dist/src/domain/value-objects/log-level.vo.js +13 -0
  86. package/dist/src/index.d.ts +6 -0
  87. package/dist/src/index.js +22 -0
  88. package/dist/src/infrastructure/datasources/in-memory-log.datasource.d.ts +1 -0
  89. package/dist/src/infrastructure/datasources/in-memory-log.datasource.js +2 -0
  90. package/dist/src/infrastructure/datasources/index.d.ts +1 -0
  91. package/dist/{infrastructure/adapters → src/infrastructure/datasources}/index.js +1 -1
  92. package/dist/src/infrastructure/index.d.ts +1 -0
  93. package/dist/{domain/types → src/infrastructure}/index.js +1 -1
  94. package/dist/src/infrastructure/services/data-source-error-handler.type.d.ts +5 -0
  95. package/dist/src/infrastructure/services/data-source-error-handler.type.js +2 -0
  96. package/dist/src/infrastructure/services/datasource.service.d.ts +15 -0
  97. package/dist/src/infrastructure/services/datasource.service.js +63 -0
  98. package/dist/src/infrastructure/services/index.d.ts +2 -0
  99. package/dist/src/infrastructure/services/index.js +18 -0
  100. package/dist/tests/application/factory/logger-factory.spec.d.ts +1 -0
  101. package/dist/tests/application/factory/logger-factory.spec.js +161 -0
  102. package/dist/tests/application/use-cases/flush-buffers.use-case.spec.d.ts +1 -0
  103. package/dist/tests/application/use-cases/flush-buffers.use-case.spec.js +38 -0
  104. package/dist/tests/application/use-cases/get-logs.use-case.spec.d.ts +1 -0
  105. package/dist/tests/application/use-cases/get-logs.use-case.spec.js +114 -0
  106. package/dist/tests/application/use-cases/save-log.use-case.spec.d.ts +1 -0
  107. package/dist/tests/application/use-cases/save-log.use-case.spec.js +138 -0
  108. package/dist/tests/domain/services/log-level.service.spec.d.ts +1 -0
  109. package/dist/tests/domain/services/log-level.service.spec.js +68 -0
  110. package/dist/tests/domain/services/normalize-message.spec.d.ts +1 -0
  111. package/dist/tests/domain/services/normalize-message.spec.js +83 -0
  112. package/dist/tests/domain/services/pii-redactor.spec.d.ts +1 -0
  113. package/dist/tests/domain/services/pii-redactor.spec.js +170 -0
  114. package/dist/tests/domain/services/to-pii-regex.spec.d.ts +1 -0
  115. package/dist/tests/domain/services/to-pii-regex.spec.js +82 -0
  116. package/dist/tests/infrastructure/services/datasource.service.spec.d.ts +1 -0
  117. package/dist/tests/infrastructure/services/datasource.service.spec.js +128 -0
  118. package/dist/tests/test-utils/create-pii-redactor-mock.d.ts +5 -0
  119. package/dist/tests/test-utils/create-pii-redactor-mock.js +10 -0
  120. package/package.json +27 -19
  121. package/LICENSE +0 -21
  122. package/dist/application/use-cases/flush-buffers.d.ts +0 -6
  123. package/dist/application/use-cases/get-logs.d.ts +0 -7
  124. package/dist/application/use-cases/index.d.ts +0 -3
  125. package/dist/application/use-cases/save-log.d.ts +0 -13
  126. package/dist/application/use-cases/save-log.js +0 -30
  127. package/dist/domain/contracts/index.d.ts +0 -3
  128. package/dist/domain/contracts/log.datasource.d.ts +0 -8
  129. package/dist/domain/contracts/pii.d.ts +0 -5
  130. package/dist/domain/services/index.d.ts +0 -1
  131. package/dist/domain/services/pii-redactor.d.ts +0 -10
  132. package/dist/domain/types/index.d.ts +0 -1
  133. package/dist/domain/types/log.types.d.ts +0 -28
  134. package/dist/domain/value-objects/index.d.ts +0 -1
  135. package/dist/domain/value-objects/log-level.js +0 -37
  136. package/dist/index.d.ts +0 -5
  137. package/dist/index.js +0 -10
  138. package/dist/infrastructure/adapters/composite.datasource.d.ts +0 -11
  139. package/dist/infrastructure/adapters/composite.datasource.js +0 -46
  140. package/dist/infrastructure/adapters/index.d.ts +0 -1
  141. package/dist/presentation/factory/index.d.ts +0 -1
  142. /package/dist/{domain/contracts/log.datasource.js → src/domain/ports/create-logger-options.port.js} +0 -0
  143. /package/dist/{domain/contracts/logger.js → src/domain/ports/log-datasource.port.js} +0 -0
  144. /package/dist/{domain/contracts/pii.js → src/domain/ports/logger-factory-config.port.js} +0 -0
  145. /package/dist/{domain/types/log.types.js → src/domain/ports/logger-service.port.js} +0 -0
@@ -0,0 +1,10 @@
1
+ import { IPiiRedactor } from "../ports";
2
+ import { IPiiOptionsProps } from "../request";
3
+ export declare class PiiRedactor implements IPiiRedactor {
4
+ private props;
5
+ constructor(props?: IPiiOptionsProps);
6
+ updateOptions(opts: IPiiOptionsProps): void;
7
+ redact<T = unknown>(value: T): T;
8
+ private applyPatterns;
9
+ private redactDeep;
10
+ }
@@ -1,26 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PiiRedactor = void 0;
4
- // Utilidad local: crea RegExp segura desde {pattern, flags}
5
- function toRegex(p) {
6
- // new RegExp lanza si el patrón es inválido → try/catch para robustez
7
- try {
8
- return new RegExp(p.pattern, p.flags ?? "g");
9
- }
10
- catch {
11
- return /$a/; /* patrón imposible */
12
- }
13
- }
4
+ const pii_pattern_service_1 = require("./pii-pattern.service");
14
5
  class PiiRedactor {
15
- constructor(options = {}) {
16
- this.options = options;
6
+ constructor(props = {}) {
7
+ this.props = props;
17
8
  }
18
9
  updateOptions(opts) {
19
10
  // merge superficial de opciones; no muta referencias externas
20
- this.options = { ...this.options, ...opts };
11
+ this.props = { ...this.props, ...opts };
21
12
  }
22
13
  redact(value) {
23
- if (!this.options.enabled)
14
+ if (!this.props.enabled)
24
15
  return value; // early exit si PII apagado
25
16
  if (value == null)
26
17
  return value;
@@ -36,10 +27,10 @@ class PiiRedactor {
36
27
  return value;
37
28
  }
38
29
  applyPatterns(input) {
39
- const patterns = this.options.patterns ?? [];
30
+ const patterns = this.props.patterns ?? [];
40
31
  let out = input;
41
32
  for (const p of patterns) {
42
- const rx = toRegex(p);
33
+ const rx = (0, pii_pattern_service_1.toPiiRegex)(p);
43
34
  // replace con patrón RegExp
44
35
  out = out.replace(rx, p.replaceWith);
45
36
  }
@@ -50,7 +41,7 @@ class PiiRedactor {
50
41
  const clone = Array.isArray(obj)
51
42
  ? [...obj]
52
43
  : { ...obj };
53
- const { whitelistKeys = [], blacklistKeys = [], deep = true, } = this.options;
44
+ const { whitelistKeys = [], blacklistKeys = [], deep = true } = this.props;
54
45
  for (const key of Object.keys(clone)) {
55
46
  const val = clone[key];
56
47
  // Si está en whitelist, se respeta siempre
@@ -0,0 +1,10 @@
1
+ import { IPiiRedactor } from "../ports";
2
+ import { IPiiOptionsProps } from "../request";
3
+ export declare class PiiRedactor implements IPiiRedactor {
4
+ private props;
5
+ constructor(props?: IPiiOptionsProps);
6
+ updateOptions(opts: IPiiOptionsProps): void;
7
+ redact<T = unknown>(value: T): T;
8
+ private applyPatterns;
9
+ private redactDeep;
10
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PiiRedactor = void 0;
4
+ const pii_pattern_service_1 = require("./pii-pattern.service");
5
+ class PiiRedactor {
6
+ constructor(props = {}) {
7
+ this.props = props;
8
+ }
9
+ updateOptions(opts) {
10
+ // merge superficial de opciones; no muta referencias externas
11
+ this.props = { ...this.props, ...opts };
12
+ }
13
+ redact(value) {
14
+ if (!this.props.enabled)
15
+ return value; // early exit si PII apagado
16
+ if (value == null)
17
+ return value;
18
+ // Si es string → aplicar patrones
19
+ if (typeof value === "string") {
20
+ return this.applyPatterns(value);
21
+ }
22
+ // Si es objeto/array → redacción profunda si corresponde
23
+ if (typeof value === "object") {
24
+ return this.redactDeep(value);
25
+ }
26
+ // Otros tipos (number, boolean, function) no se redactan
27
+ return value;
28
+ }
29
+ applyPatterns(input) {
30
+ const patterns = this.props.patterns ?? [];
31
+ let out = input;
32
+ for (const p of patterns) {
33
+ const rx = (0, pii_pattern_service_1.toPiiRegex)(p);
34
+ // replace con patrón RegExp
35
+ out = out.replace(rx, p.replaceWith);
36
+ }
37
+ return out;
38
+ }
39
+ redactDeep(obj) {
40
+ // Evita mutar el objeto original → copia superficial
41
+ const clone = Array.isArray(obj)
42
+ ? [...obj]
43
+ : { ...obj };
44
+ const { whitelistKeys = [], blacklistKeys = [], deep = true } = this.props;
45
+ for (const key of Object.keys(clone)) {
46
+ const val = clone[key];
47
+ // Si está en whitelist, se respeta siempre
48
+ if (whitelistKeys.includes(key))
49
+ continue;
50
+ // Si está en blacklist y es string → reemplazar completamente
51
+ if (blacklistKeys.includes(key)) {
52
+ clone[key] = "[REDACTED]"; // fuerza redacción total de claves sensibles
53
+ continue;
54
+ }
55
+ // Para strings comunes → aplicar patrones
56
+ if (typeof val === "string") {
57
+ clone[key] = this.applyPatterns(val);
58
+ continue;
59
+ }
60
+ // Para objetos/arrays y deep===true → recursión
61
+ if (deep && val && typeof val === "object") {
62
+ clone[key] = this.redactDeep(val);
63
+ }
64
+ }
65
+ return clone;
66
+ }
67
+ }
68
+ exports.PiiRedactor = PiiRedactor;
@@ -0,0 +1 @@
1
+ export * from "./log-message.type";
@@ -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("./log-message.type"), exports);
@@ -0,0 +1 @@
1
+ export type LogMessage = string | Record<string, unknown> | (() => string | Record<string, unknown>);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from "./log-level.vo";
@@ -14,4 +14,4 @@ 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("./pii-redactor"), exports);
17
+ __exportStar(require("./log-level.vo"), exports);
@@ -6,4 +6,3 @@ export declare enum LogLevel {
6
6
  ERROR = 50,
7
7
  FATAL = 60
8
8
  }
9
- export declare function toLogLevel(level: string | number | undefined, def?: LogLevel): LogLevel;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogLevel = void 0;
4
+ var LogLevel;
5
+ (function (LogLevel) {
6
+ // Orden creciente permite comparar por severidad (trace < debug < ...)
7
+ LogLevel[LogLevel["TRACE"] = 10] = "TRACE";
8
+ LogLevel[LogLevel["DEBUG"] = 20] = "DEBUG";
9
+ LogLevel[LogLevel["INFO"] = 30] = "INFO";
10
+ LogLevel[LogLevel["WARN"] = 40] = "WARN";
11
+ LogLevel[LogLevel["ERROR"] = 50] = "ERROR";
12
+ LogLevel[LogLevel["FATAL"] = 60] = "FATAL";
13
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
@@ -0,0 +1,6 @@
1
+ export * from "./application/factory/logger.factory";
2
+ export type { ILoggerService, ILoggerFactoryConfig } from "./domain/ports";
3
+ export type { LogMessage } from "./domain/types";
4
+ export type { IGetLogsFilterProps } from "./domain/request";
5
+ export type { ILogResponse } from "./domain/response";
6
+ export { LogLevel } from "./domain/value-objects";
@@ -0,0 +1,22 @@
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
+ exports.LogLevel = void 0;
18
+ // API principal de @jmlq/logger
19
+ __exportStar(require("./application/factory/logger.factory"), exports);
20
+ // Enums
21
+ var value_objects_1 = require("./domain/value-objects");
22
+ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return value_objects_1.LogLevel; } });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from "./in-memory-log.datasource";
@@ -14,4 +14,4 @@ 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("./composite.datasource"), exports);
17
+ __exportStar(require("./in-memory-log.datasource"), exports);
@@ -0,0 +1 @@
1
+ export * from "./services";
@@ -14,4 +14,4 @@ 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("./log.types"), exports);
17
+ __exportStar(require("./services"), exports);
@@ -0,0 +1,5 @@
1
+ export type DataSourceErrorHandler = (info: {
2
+ operation: "save" | "find" | "flush" | "dispose";
3
+ datasourceName: string;
4
+ reason: any;
5
+ }) => void;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,15 @@
1
+ import { DataSourceErrorHandler } from ".";
2
+ import { ILogDatasource } from "../../domain/ports";
3
+ import { IGetLogsFilterProps, ILogProps } from "../../domain/request";
4
+ import { ILogResponse } from "../../domain/response";
5
+ export declare class DataSourceService implements ILogDatasource {
6
+ private readonly targets;
7
+ private readonly onError?;
8
+ readonly name = "composite";
9
+ constructor(targets: ILogDatasource[], onError?: DataSourceErrorHandler | undefined);
10
+ private handleError;
11
+ save(log: ILogProps): Promise<void>;
12
+ find(filter?: IGetLogsFilterProps): Promise<ILogResponse[]>;
13
+ flush(): Promise<void>;
14
+ dispose(): Promise<void>;
15
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DataSourceService = void 0;
4
+ class DataSourceService {
5
+ constructor(targets, onError) {
6
+ this.targets = targets;
7
+ this.onError = onError;
8
+ this.name = "composite";
9
+ this.targets = (targets ?? []).filter(Boolean);
10
+ }
11
+ handleError(op, i, reason) {
12
+ const datasourceName = this.targets[i]?.name ?? "unknown";
13
+ if (this.onError) {
14
+ this.onError({
15
+ operation: op,
16
+ datasourceName,
17
+ reason,
18
+ });
19
+ return;
20
+ }
21
+ // fallback por simplicidad
22
+ console.warn(`[DataSourceService] ${op} error in ds#${i} (${datasourceName})`, reason);
23
+ }
24
+ async save(log) {
25
+ const results = await Promise.allSettled(this.targets.map((ds) => ds.save(log)));
26
+ results.forEach((r, i) => {
27
+ if (r.status === "rejected") {
28
+ this.handleError("save", i, r.reason);
29
+ }
30
+ });
31
+ }
32
+ async find(filter) {
33
+ const results = await Promise.allSettled(this.targets.map((ds) => ds.find?.(filter)));
34
+ const logs = [];
35
+ results.forEach((r, i) => {
36
+ if (r.status === "fulfilled" && Array.isArray(r.value)) {
37
+ logs.push(...r.value);
38
+ }
39
+ else if (r.status === "rejected") {
40
+ this.handleError("find", i, r.reason);
41
+ }
42
+ });
43
+ logs.sort((a, b) => b.timestamp - a.timestamp);
44
+ return logs;
45
+ }
46
+ async flush() {
47
+ const results = await Promise.allSettled(this.targets.map((ds) => ds.flush?.()));
48
+ results.forEach((r, i) => {
49
+ if (r.status === "rejected") {
50
+ this.handleError("flush", i, r.reason);
51
+ }
52
+ });
53
+ }
54
+ async dispose() {
55
+ const results = await Promise.allSettled(this.targets.map((ds) => ds.dispose?.()));
56
+ results.forEach((r, i) => {
57
+ if (r.status === "rejected") {
58
+ this.handleError("dispose", i, r.reason);
59
+ }
60
+ });
61
+ }
62
+ }
63
+ exports.DataSourceService = DataSourceService;
@@ -0,0 +1,2 @@
1
+ export * from "./datasource.service";
2
+ export * from "./data-source-error-handler.type";
@@ -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("./datasource.service"), exports);
18
+ __exportStar(require("./data-source-error-handler.type"), exports);
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const logger_factory_1 = require("../../../src/application/factory/logger.factory");
4
+ const value_objects_1 = require("../../../src/domain/value-objects");
5
+ describe("LoggerFactory (behavioral)", () => {
6
+ it("debe respetar el minLevel por defecto (INFO): no guardar DEBUG, sí INFO y ERROR", async () => {
7
+ const savedLogs = [];
8
+ const ds = {
9
+ name: "single-ds",
10
+ save: jest.fn(async (log) => {
11
+ savedLogs.push(log);
12
+ }),
13
+ find: jest.fn(),
14
+ flush: jest.fn(),
15
+ dispose: jest.fn(),
16
+ };
17
+ // No pasamos minLevel → debe usar LogLevel.INFO
18
+ const logger = logger_factory_1.LoggerFactory.create({
19
+ datasources: ds,
20
+ // sin redactor ni opciones, usa PiiRedactor por defecto
21
+ });
22
+ await logger.debug("debug ignorado");
23
+ await logger.info("info guardado");
24
+ await logger.error("error guardado");
25
+ // Se debe haber llamado save solo 2 veces (INFO y ERROR)
26
+ expect(ds.save.mock.calls.length).toBe(2);
27
+ expect(savedLogs[0].level).toBe(value_objects_1.LogLevel.INFO);
28
+ expect(savedLogs[0].message).toBe("info guardado");
29
+ expect(savedLogs[1].level).toBe(value_objects_1.LogLevel.ERROR);
30
+ expect(savedLogs[1].message).toBe("error guardado");
31
+ });
32
+ it("debe respetar un minLevel personalizado (por ejemplo ERROR)", async () => {
33
+ const savedLogs = [];
34
+ const ds = {
35
+ name: "single-ds",
36
+ save: jest.fn(async (log) => {
37
+ savedLogs.push(log);
38
+ }),
39
+ find: jest.fn(),
40
+ flush: jest.fn(),
41
+ dispose: jest.fn(),
42
+ };
43
+ const logger = logger_factory_1.LoggerFactory.create({
44
+ datasources: ds,
45
+ minLevel: value_objects_1.LogLevel.ERROR,
46
+ });
47
+ await logger.info("esto NO debería loggearse");
48
+ await logger.error("esto SÍ debería loggearse");
49
+ expect(ds.save.mock.calls.length).toBe(1);
50
+ expect(savedLogs[0].level).toBe(value_objects_1.LogLevel.ERROR);
51
+ expect(savedLogs[0].message).toBe("esto SÍ debería loggearse");
52
+ });
53
+ it("debe componer múltiples datasources (fan-out) y enviar el log a todos", async () => {
54
+ const dsA = {
55
+ name: "A",
56
+ save: jest.fn(),
57
+ find: jest.fn(),
58
+ flush: jest.fn(),
59
+ dispose: jest.fn(),
60
+ };
61
+ const dsB = {
62
+ name: "B",
63
+ save: jest.fn(),
64
+ find: jest.fn(),
65
+ flush: jest.fn(),
66
+ dispose: jest.fn(),
67
+ };
68
+ const logger = logger_factory_1.LoggerFactory.create({
69
+ datasources: [dsA, dsB],
70
+ minLevel: value_objects_1.LogLevel.TRACE, // para que no filtre nada
71
+ });
72
+ await logger.info("mensaje fan-out");
73
+ expect(dsA.save).toHaveBeenCalledTimes(1);
74
+ expect(dsB.save).toHaveBeenCalledTimes(1);
75
+ const logA = dsA.save.mock.calls[0][0];
76
+ const logB = dsB.save.mock.calls[0][0];
77
+ expect(logA.message).toBe("mensaje fan-out");
78
+ expect(logB.message).toBe("mensaje fan-out");
79
+ });
80
+ it("debe usar el redactor proporcionado en config para procesar meta", async () => {
81
+ const savedLogs = [];
82
+ const ds = {
83
+ name: "ds",
84
+ save: jest.fn(async (log) => {
85
+ savedLogs.push(log);
86
+ }),
87
+ find: jest.fn(),
88
+ flush: jest.fn(),
89
+ dispose: jest.fn(),
90
+ };
91
+ // Redactor custom que marca la meta
92
+ const redactor = {
93
+ redact: jest.fn((value) => {
94
+ if (value && typeof value === "object") {
95
+ return {
96
+ ...value,
97
+ __redacted: true,
98
+ };
99
+ }
100
+ return value;
101
+ }),
102
+ updateOptions: jest.fn(),
103
+ };
104
+ const logger = logger_factory_1.LoggerFactory.create({
105
+ datasources: ds,
106
+ minLevel: value_objects_1.LogLevel.INFO,
107
+ redactor,
108
+ });
109
+ const metaOriginal = { userId: "123" };
110
+ await logger.info("mensaje con meta", metaOriginal);
111
+ expect(redactor.redact).toHaveBeenCalledWith(metaOriginal);
112
+ const logGuardado = savedLogs[0];
113
+ expect(logGuardado.meta).toEqual({
114
+ userId: "123",
115
+ __redacted: true,
116
+ });
117
+ });
118
+ it("getLogs debe delegar a find del datasource con el filtro adecuado y retornar el resultado", async () => {
119
+ const logsSimulados = [
120
+ {
121
+ level: value_objects_1.LogLevel.ERROR,
122
+ message: "falló algo",
123
+ timestamp: Date.now(),
124
+ },
125
+ ];
126
+ const ds = {
127
+ name: "ds",
128
+ save: jest.fn(),
129
+ find: jest.fn().mockResolvedValue(logsSimulados),
130
+ flush: jest.fn(),
131
+ dispose: jest.fn(),
132
+ };
133
+ const logger = logger_factory_1.LoggerFactory.create({
134
+ datasources: ds,
135
+ minLevel: value_objects_1.LogLevel.INFO,
136
+ });
137
+ const filter = {
138
+ levelMin: value_objects_1.LogLevel.ERROR,
139
+ query: "falló",
140
+ };
141
+ const result = await logger.getLogs(filter);
142
+ expect(ds.find).toHaveBeenCalledTimes(1);
143
+ expect(ds.find).toHaveBeenCalledWith(filter);
144
+ expect(result).toBe(logsSimulados);
145
+ });
146
+ it("flush debe delegar al flush del datasource (o composite) si existe", async () => {
147
+ const ds = {
148
+ name: "ds",
149
+ save: jest.fn(),
150
+ find: jest.fn(),
151
+ flush: jest.fn(),
152
+ dispose: jest.fn(),
153
+ };
154
+ const logger = logger_factory_1.LoggerFactory.create({
155
+ datasources: ds,
156
+ minLevel: value_objects_1.LogLevel.INFO,
157
+ });
158
+ await logger.flush();
159
+ expect(ds.flush).toHaveBeenCalledTimes(1);
160
+ });
161
+ });
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const use_cases_1 = require("../../../src/application/use-cases");
4
+ describe("FlushBuffersUseCase", () => {
5
+ it("debe llamar a ds.flush cuando está implementado", async () => {
6
+ const flushMock = jest.fn().mockResolvedValue(undefined);
7
+ const ds = {
8
+ name: "memory",
9
+ save: jest.fn().mockResolvedValue(undefined),
10
+ flush: flushMock,
11
+ };
12
+ const useCase = new use_cases_1.FlushBuffersUseCase(ds);
13
+ await useCase.execute();
14
+ expect(flushMock).toHaveBeenCalledTimes(1);
15
+ expect(flushMock).toHaveBeenCalledWith(); // sin argumentos
16
+ });
17
+ it("no debe fallar cuando el datasource no implementa flush", async () => {
18
+ const ds = {
19
+ name: "memory",
20
+ save: jest.fn().mockResolvedValue(undefined),
21
+ // flush NO definido
22
+ };
23
+ const useCase = new use_cases_1.FlushBuffersUseCase(ds);
24
+ // execute simplemente debería resolverse sin lanzar error
25
+ await expect(useCase.execute()).resolves.toBeUndefined();
26
+ });
27
+ it("debe propagar el error si ds.flush rechaza", async () => {
28
+ const error = new Error("flush failed");
29
+ const flushMock = jest.fn().mockRejectedValue(error);
30
+ const ds = {
31
+ name: "memory",
32
+ save: jest.fn().mockResolvedValue(undefined),
33
+ flush: flushMock,
34
+ };
35
+ const useCase = new use_cases_1.FlushBuffersUseCase(ds);
36
+ await expect(useCase.execute()).rejects.toBe(error);
37
+ });
38
+ });