@jmlq/logger-plugin-fs 0.1.0-alpha.1 → 0.1.0-alpha.10

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 (118) hide show
  1. package/README.md +229 -0
  2. package/architecture.md +426 -0
  3. package/dist/application/dto/index.d.ts +1 -0
  4. package/dist/application/dto/index.js +17 -0
  5. package/dist/application/dto/rotate-if-needed.request.d.ts +10 -0
  6. package/dist/application/dto/rotate-if-needed.request.js +2 -0
  7. package/dist/application/factory/create-fs-datasource.factory.d.ts +16 -0
  8. package/dist/application/factory/create-fs-datasource.factory.js +51 -0
  9. package/dist/application/factory/index.d.ts +1 -0
  10. package/dist/application/factory/index.js +17 -0
  11. package/dist/application/use-cases/append-log.use-case.d.ts +30 -0
  12. package/dist/application/use-cases/append-log.use-case.js +39 -0
  13. package/dist/application/use-cases/ensure-directory.use-case.d.ts +58 -0
  14. package/dist/application/use-cases/ensure-directory.use-case.js +64 -0
  15. package/dist/application/use-cases/find-logs-use-case.d.ts +17 -0
  16. package/dist/application/use-cases/find-logs-use-case.js +64 -0
  17. package/dist/application/use-cases/index.d.ts +5 -0
  18. package/dist/application/use-cases/index.js +21 -0
  19. package/dist/application/use-cases/persist-log.use-case.d.ts +96 -0
  20. package/dist/application/use-cases/persist-log.use-case.js +105 -0
  21. package/dist/application/use-cases/rotate-if-needed.use-case.d.ts +55 -0
  22. package/dist/application/use-cases/rotate-if-needed.use-case.js +61 -0
  23. package/dist/domain/model/index.d.ts +1 -0
  24. package/dist/domain/model/index.js +17 -0
  25. package/dist/domain/model/log-entry.model.d.ts +8 -0
  26. package/dist/domain/model/log-entry.model.js +2 -0
  27. package/dist/domain/ports/file/file-path.port.d.ts +38 -0
  28. package/dist/domain/ports/file/file-path.port.js +2 -0
  29. package/dist/domain/ports/file/file-rotator.port.d.ts +42 -0
  30. package/dist/domain/ports/file/file-rotator.port.js +2 -0
  31. package/dist/domain/ports/file/index.d.ts +2 -0
  32. package/dist/domain/ports/file/index.js +18 -0
  33. package/dist/domain/ports/file/log-stream-writer.port.d.ts +70 -0
  34. package/dist/domain/ports/file/log-stream-writer.port.js +2 -0
  35. package/dist/domain/ports/filesystem-provider.port.d.ts +61 -0
  36. package/dist/domain/ports/filesystem-provider.port.js +2 -0
  37. package/dist/domain/ports/index.d.ts +5 -0
  38. package/dist/domain/ports/index.js +21 -0
  39. package/dist/domain/ports/logs/find/index.d.ts +2 -0
  40. package/dist/domain/ports/logs/find/index.js +18 -0
  41. package/dist/domain/ports/logs/find/log-file-line-reader.port.d.ts +3 -0
  42. package/dist/domain/ports/logs/find/log-file-line-reader.port.js +2 -0
  43. package/dist/domain/ports/logs/find/log-file-numerator.port.d.ts +3 -0
  44. package/dist/domain/ports/logs/find/log-file-numerator.port.js +2 -0
  45. package/dist/domain/ports/logs/index.d.ts +2 -0
  46. package/dist/domain/ports/logs/index.js +18 -0
  47. package/dist/domain/ports/logs/log-datasource.port.d.ts +10 -0
  48. package/dist/domain/ports/logs/log-datasource.port.js +2 -0
  49. package/dist/domain/ports/system-clock.port.d.ts +8 -0
  50. package/dist/domain/ports/system-clock.port.js +2 -0
  51. package/dist/domain/request/index.d.ts +2 -0
  52. package/dist/domain/request/index.js +18 -0
  53. package/dist/domain/request/log-filter.request.d.ts +9 -0
  54. package/dist/domain/request/log-filter.request.js +2 -0
  55. package/dist/domain/request/save-log.request.d.ts +7 -0
  56. package/dist/domain/request/save-log.request.js +2 -0
  57. package/dist/domain/response/index.d.ts +1 -0
  58. package/dist/domain/response/index.js +17 -0
  59. package/dist/domain/response/log.response.d.ts +8 -0
  60. package/dist/domain/response/log.response.js +2 -0
  61. package/dist/domain/types/fs-rotation-by.type.d.ts +8 -0
  62. package/dist/domain/types/fs-rotation-by.type.js +2 -0
  63. package/dist/domain/types/index.d.ts +1 -0
  64. package/dist/domain/types/index.js +17 -0
  65. package/dist/domain/value-objects/file-name-pattern.vo.d.ts +36 -0
  66. package/dist/domain/value-objects/file-name-pattern.vo.js +53 -0
  67. package/dist/domain/value-objects/file-path.vo.d.ts +91 -0
  68. package/dist/domain/value-objects/file-path.vo.js +100 -0
  69. package/dist/domain/value-objects/file-rotation-policy.vo.d.ts +51 -0
  70. package/dist/domain/value-objects/file-rotation-policy.vo.js +76 -0
  71. package/dist/domain/value-objects/file-size.vo.d.ts +75 -0
  72. package/dist/domain/value-objects/file-size.vo.js +114 -0
  73. package/dist/domain/value-objects/index.d.ts +5 -0
  74. package/dist/domain/value-objects/index.js +21 -0
  75. package/dist/domain/value-objects/log-level.vo.d.ts +8 -0
  76. package/dist/domain/value-objects/log-level.vo.js +13 -0
  77. package/dist/index.d.ts +3 -13
  78. package/dist/index.js +10 -38
  79. package/dist/infrastructure/adapters/file-rotator.adapter.d.ts +79 -0
  80. package/dist/infrastructure/adapters/file-rotator.adapter.js +171 -0
  81. package/dist/infrastructure/adapters/fileSystem-datasource.adapter.d.ts +26 -0
  82. package/dist/infrastructure/adapters/fileSystem-datasource.adapter.js +45 -0
  83. package/dist/infrastructure/adapters/filesystem-log-file-enumerator.adapter.d.ts +6 -0
  84. package/dist/infrastructure/adapters/filesystem-log-file-enumerator.adapter.js +54 -0
  85. package/dist/infrastructure/adapters/filesystem-log-file-line-reader.adapter.d.ts +4 -0
  86. package/dist/infrastructure/adapters/filesystem-log-file-line-reader.adapter.js +53 -0
  87. package/dist/infrastructure/adapters/filesystem-provider.adapter.d.ts +122 -0
  88. package/dist/infrastructure/adapters/filesystem-provider.adapter.js +182 -0
  89. package/dist/infrastructure/adapters/index.d.ts +8 -0
  90. package/dist/infrastructure/adapters/index.js +24 -0
  91. package/dist/infrastructure/adapters/log-stream-writer.adapter.d.ts +80 -0
  92. package/dist/infrastructure/adapters/log-stream-writer.adapter.js +163 -0
  93. package/dist/infrastructure/adapters/system-clock.adapter.d.ts +25 -0
  94. package/dist/infrastructure/adapters/system-clock.adapter.js +30 -0
  95. package/dist/infrastructure/adapters/system-file-path.adapter.d.ts +47 -0
  96. package/dist/infrastructure/adapters/system-file-path.adapter.js +141 -0
  97. package/dist/infrastructure/errors/file-operation.error.d.ts +28 -0
  98. package/dist/infrastructure/errors/file-operation.error.js +54 -0
  99. package/dist/infrastructure/errors/index.d.ts +1 -0
  100. package/dist/infrastructure/errors/index.js +17 -0
  101. package/dist/infrastructure/errors/types/file-operation-error-options.type.d.ts +8 -0
  102. package/dist/infrastructure/errors/types/file-operation-error-options.type.js +2 -0
  103. package/dist/infrastructure/errors/types/file-operation.type.d.ts +1 -0
  104. package/dist/infrastructure/errors/types/file-operation.type.js +2 -0
  105. package/dist/infrastructure/errors/types/fs-error-scope.type.d.ts +1 -0
  106. package/dist/infrastructure/errors/types/fs-error-scope.type.js +2 -0
  107. package/dist/infrastructure/errors/types/index.d.ts +3 -0
  108. package/dist/infrastructure/errors/types/index.js +19 -0
  109. package/dist/infrastructure/filesystem/index.d.ts +1 -0
  110. package/dist/infrastructure/filesystem/index.js +17 -0
  111. package/dist/infrastructure/filesystem/types/filesystem-datasource-options.type.d.ts +45 -0
  112. package/dist/infrastructure/filesystem/types/filesystem-datasource-options.type.js +2 -0
  113. package/dist/infrastructure/filesystem/types/filesystem-rotation.type.d.ts +12 -0
  114. package/dist/infrastructure/filesystem/types/filesystem-rotation.type.js +2 -0
  115. package/dist/infrastructure/filesystem/types/index.d.ts +2 -0
  116. package/dist/infrastructure/filesystem/types/index.js +18 -0
  117. package/install.md +520 -0
  118. package/package.json +40 -12
@@ -0,0 +1,79 @@
1
+ import { IFileSystemProviderPort, IFilePathPort, IFileRotatorPort } from "../../domain/ports";
2
+ import { FileSize, FilePath, FileRotationPolicy, FileNamePattern } from "../../domain/value-objects";
3
+ /**
4
+ * Adapter de infraestructura que implementa IFileRotatorPort.
5
+ *
6
+ * Responsabilidad:
7
+ * - Calcular el path esperado del archivo de log en base a una fecha y un FileNamePattern.
8
+ * - Leer metadata real del filesystem (tamaño / listado de archivos).
9
+ * - Decidir si debe rotar (por día o por tamaño) apoyándose en la política del dominio.
10
+ *
11
+ * Nota:
12
+ * - Este adapter mantiene estado mínimo (`currentFilePath`) porque el Port lo requiere.
13
+ * Aun así, evitamos mutaciones ocultas: solo se actualiza desde getExpectedPathForDate().
14
+ */
15
+ export declare class FileRotatorAdapter implements IFileRotatorPort {
16
+ private readonly fsProvider;
17
+ private readonly filePathAdapter;
18
+ private readonly fileNamePattern;
19
+ private readonly basePath;
20
+ private currentFilePath;
21
+ private readonly baseDir;
22
+ private static readonly TOKEN_REGEX;
23
+ constructor(fsProvider: IFileSystemProviderPort, filePathAdapter: IFilePathPort, fileNamePattern: FileNamePattern, basePath: string);
24
+ /**
25
+ * Devuelve el último archivo considerado “actual” por el rotator.
26
+ * Puede ser null si aún no se ha calculado/solicitado un path.
27
+ */
28
+ getCurrentPath(): FilePath | null;
29
+ /**
30
+ * Calcula el path esperado para una fecha y lo “marca” como archivo actual.
31
+ * OJO: este es el único método que muta currentFilePath a propósito.
32
+ */
33
+ getExpectedPathForDate(date: Date): FilePath;
34
+ /**
35
+ * Calcula el FilePath esperado para una fecha usando el patrón.
36
+ * Método “puro”: NO muta estado.
37
+ */
38
+ private computePathForDate;
39
+ /**
40
+ * Reemplaza tokens de fecha dentro del patrón (ej. {yyyy}{MM}{dd}) por valores reales.
41
+ */
42
+ private applyDateTokens;
43
+ /**
44
+ * Obtiene el tamaño del archivo (stat) y lo devuelve como FileSize (VO).
45
+ *
46
+ * Decisión de diseño:
47
+ * - Si stat falla (archivo no existe, permisos, etc.), devolvemos tamaño 0.
48
+ * - Esto evita que el flujo de rotación “reviente” por un fallo de I/O puntual.
49
+ * - Si quieres hacer esto más estricto, aquí podrías lanzar un FileOperationError("read"/"stat", ...).
50
+ */
51
+ getFileSize(filePath: FilePath): Promise<FileSize>;
52
+ /**
53
+ * Decide si debe rotar basándose en la política del dominio y el estado actual.
54
+ *
55
+ * Nota:
56
+ * - Para rotación por día: comparamos el path actual vs el esperado para la fecha actual.
57
+ * - Para rotación por tamaño: consultamos el tamaño real y delegamos la regla al VO.
58
+ */
59
+ shouldRotate(policy: FileRotationPolicy, currentDate: Date): Promise<boolean>;
60
+ /**
61
+ * Rotación por día:
62
+ * - Si no hay currentFilePath, se considera que “debe rotar/crear” para inicializar.
63
+ * - Si el nombre esperado para la fecha actual difiere del actual, debe rotar.
64
+ *
65
+ * Importante:
66
+ * - Este método NO muta currentFilePath (solo calcula).
67
+ */
68
+ private shouldRotateByDay;
69
+ /**
70
+ * Rotación por tamaño:
71
+ * - Si no hay currentFilePath, no rotamos (no hay “archivo actual” que evaluar).
72
+ * - Si no hay maxSize en la policy, no rotamos.
73
+ * - Si se puede medir tamaño, delegamos al VO para la regla (>= maxSize).
74
+ */
75
+ private shouldRotateBySize;
76
+ }
77
+ /**
78
+ * Escapa caracteres especiales para construir RegExp seguro desde strings (basename/ext).
79
+ */
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileRotatorAdapter = void 0;
4
+ const value_objects_1 = require("../../domain/value-objects");
5
+ /**
6
+ * Adapter de infraestructura que implementa IFileRotatorPort.
7
+ *
8
+ * Responsabilidad:
9
+ * - Calcular el path esperado del archivo de log en base a una fecha y un FileNamePattern.
10
+ * - Leer metadata real del filesystem (tamaño / listado de archivos).
11
+ * - Decidir si debe rotar (por día o por tamaño) apoyándose en la política del dominio.
12
+ *
13
+ * Nota:
14
+ * - Este adapter mantiene estado mínimo (`currentFilePath`) porque el Port lo requiere.
15
+ * Aun así, evitamos mutaciones ocultas: solo se actualiza desde getExpectedPathForDate().
16
+ */
17
+ class FileRotatorAdapter {
18
+ constructor(
19
+ // Port de FS (stat, readdir, etc.)
20
+ fsProvider,
21
+ // Port de paths (normalización, join, resolve, etc.)
22
+ filePathAdapter,
23
+ // VO de dominio: patrón de nombre
24
+ fileNamePattern,
25
+ // Config cruda (normalmente string proveniente de env/config)
26
+ basePath) {
27
+ this.fsProvider = fsProvider;
28
+ this.filePathAdapter = filePathAdapter;
29
+ this.fileNamePattern = fileNamePattern;
30
+ this.basePath = basePath;
31
+ // Estado mínimo: “archivo actual” según el último cálculo/uso.
32
+ this.currentFilePath = null;
33
+ // Resolver y normalizar basePath una sola vez.
34
+ this.baseDir = this.filePathAdapter.fromRaw(this.basePath);
35
+ }
36
+ /**
37
+ * Devuelve el último archivo considerado “actual” por el rotator.
38
+ * Puede ser null si aún no se ha calculado/solicitado un path.
39
+ */
40
+ getCurrentPath() {
41
+ return this.currentFilePath;
42
+ }
43
+ /**
44
+ * Calcula el path esperado para una fecha y lo “marca” como archivo actual.
45
+ * OJO: este es el único método que muta currentFilePath a propósito.
46
+ */
47
+ getExpectedPathForDate(date) {
48
+ const expected = this.computePathForDate(date);
49
+ this.currentFilePath = expected;
50
+ return expected;
51
+ }
52
+ /**
53
+ * Calcula el FilePath esperado para una fecha usando el patrón.
54
+ * Método “puro”: NO muta estado.
55
+ */
56
+ computePathForDate(date) {
57
+ // 1) Aplicar reemplazo de tokens al patrón -> obtenemos el nombre del archivo.
58
+ const fileName = this.applyDateTokens(this.fileNamePattern.pattern, date);
59
+ // 2) Unir baseDir + fileName (sin re-resolver basePath en cada llamada).
60
+ return this.filePathAdapter.join(this.baseDir, fileName);
61
+ }
62
+ /**
63
+ * Reemplaza tokens de fecha dentro del patrón (ej. {yyyy}{MM}{dd}) por valores reales.
64
+ */
65
+ applyDateTokens(pattern, date) {
66
+ // Normalizamos valores una sola vez.
67
+ const yyyy = date.getFullYear().toString();
68
+ const MM = String(date.getMonth() + 1).padStart(2, "0");
69
+ const dd = String(date.getDate()).padStart(2, "0");
70
+ const HH = String(date.getHours()).padStart(2, "0");
71
+ const mm = String(date.getMinutes()).padStart(2, "0");
72
+ const ss = String(date.getSeconds()).padStart(2, "0");
73
+ // Reemplazamos usando regex global; más simple y eficiente que loop + new RegExp por token.
74
+ return pattern.replace(FileRotatorAdapter.TOKEN_REGEX, (_m, token) => {
75
+ switch (token) {
76
+ case "yyyy":
77
+ return yyyy;
78
+ case "MM":
79
+ return MM;
80
+ case "dd":
81
+ return dd;
82
+ case "HH":
83
+ return HH;
84
+ case "mm":
85
+ return mm;
86
+ case "ss":
87
+ return ss;
88
+ default:
89
+ // Defensive: no debería ocurrir por la regex.
90
+ return _m;
91
+ }
92
+ });
93
+ }
94
+ /**
95
+ * Obtiene el tamaño del archivo (stat) y lo devuelve como FileSize (VO).
96
+ *
97
+ * Decisión de diseño:
98
+ * - Si stat falla (archivo no existe, permisos, etc.), devolvemos tamaño 0.
99
+ * - Esto evita que el flujo de rotación “reviente” por un fallo de I/O puntual.
100
+ * - Si quieres hacer esto más estricto, aquí podrías lanzar un FileOperationError("read"/"stat", ...).
101
+ */
102
+ async getFileSize(filePath) {
103
+ try {
104
+ const stats = await this.fsProvider.stat(filePath.absolutePath);
105
+ return new value_objects_1.FileSize(stats.size);
106
+ }
107
+ catch {
108
+ return new value_objects_1.FileSize(0);
109
+ }
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Decisión de rotación
113
+ // ---------------------------------------------------------------------------
114
+ /**
115
+ * Decide si debe rotar basándose en la política del dominio y el estado actual.
116
+ *
117
+ * Nota:
118
+ * - Para rotación por día: comparamos el path actual vs el esperado para la fecha actual.
119
+ * - Para rotación por tamaño: consultamos el tamaño real y delegamos la regla al VO.
120
+ */
121
+ async shouldRotate(policy, currentDate) {
122
+ switch (policy.by) {
123
+ case "none":
124
+ return false;
125
+ case "day":
126
+ return this.shouldRotateByDay(currentDate);
127
+ case "size":
128
+ return this.shouldRotateBySize(policy);
129
+ default:
130
+ // Defensive: si el tipo llegara corrupto por casting/JS.
131
+ return false;
132
+ }
133
+ }
134
+ /**
135
+ * Rotación por día:
136
+ * - Si no hay currentFilePath, se considera que “debe rotar/crear” para inicializar.
137
+ * - Si el nombre esperado para la fecha actual difiere del actual, debe rotar.
138
+ *
139
+ * Importante:
140
+ * - Este método NO muta currentFilePath (solo calcula).
141
+ */
142
+ shouldRotateByDay(currentDate) {
143
+ if (!this.currentFilePath)
144
+ return true;
145
+ const expectedPath = this.computePathForDate(currentDate);
146
+ return !this.currentFilePath.equals(expectedPath);
147
+ }
148
+ /**
149
+ * Rotación por tamaño:
150
+ * - Si no hay currentFilePath, no rotamos (no hay “archivo actual” que evaluar).
151
+ * - Si no hay maxSize en la policy, no rotamos.
152
+ * - Si se puede medir tamaño, delegamos al VO para la regla (>= maxSize).
153
+ */
154
+ async shouldRotateBySize(policy) {
155
+ if (!this.currentFilePath)
156
+ return false;
157
+ if (!policy.maxSize)
158
+ return false;
159
+ const currentFileSize = await this.getFileSize(this.currentFilePath);
160
+ return policy.shouldRotateBySize(currentFileSize);
161
+ }
162
+ }
163
+ exports.FileRotatorAdapter = FileRotatorAdapter;
164
+ // Regex precalculada para encontrar tokens del tipo {yyyy}, {MM}, etc.
165
+ FileRotatorAdapter.TOKEN_REGEX = /\{(yyyy|MM|dd|HH|mm|ss)\}/g;
166
+ /**
167
+ * Escapa caracteres especiales para construir RegExp seguro desde strings (basename/ext).
168
+ */
169
+ // function escapeRegExp(input: string): string {
170
+ // return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
171
+ // }
@@ -0,0 +1,26 @@
1
+ import type { ILogDatasource, LogEntry as CoreLogEntry, LogSearchRequest, LogRecord } from "@jmlq/logger";
2
+ import type { FindLogsUseCase, IPersistLogUseCase } from "../../application/use-cases";
3
+ import type { ILogStreamWriterPort } from "../../domain/ports";
4
+ export declare class FsDatasourceAdapter implements ILogDatasource {
5
+ private readonly persistLogUseCase;
6
+ private readonly streamWriterPort;
7
+ private readonly findLogsUseCase?;
8
+ readonly name = "fs";
9
+ constructor(persistLogUseCase: IPersistLogUseCase, streamWriterPort: ILogStreamWriterPort, findLogsUseCase?: FindLogsUseCase | undefined);
10
+ /**
11
+ * Firma EXACTA requerida por @jmlq/logger:
12
+ * save(log: LogEntry): Promise<void>
13
+ */
14
+ save(log: CoreLogEntry): Promise<void>;
15
+ /**
16
+ * Firma típica de @jmlq/logger (si existe en tu interface):
17
+ * find?(filter?: LogFilterRequest): Promise<ILogResponse[]>
18
+ *
19
+ * Por ahora, dejamos una implementación mínima coherente:
20
+ * - Si aún no soportas lectura, devuelve [] (o lanza un error explícito).
21
+ * - Te recomiendo implementar find en serio (tail/list+parse) como acordamos.
22
+ */
23
+ find?(filter?: LogSearchRequest): Promise<LogRecord[]>;
24
+ flush(): Promise<void>;
25
+ dispose(): Promise<void>;
26
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FsDatasourceAdapter = void 0;
4
+ class FsDatasourceAdapter {
5
+ constructor(persistLogUseCase, streamWriterPort, findLogsUseCase) {
6
+ this.persistLogUseCase = persistLogUseCase;
7
+ this.streamWriterPort = streamWriterPort;
8
+ this.findLogsUseCase = findLogsUseCase;
9
+ this.name = "fs";
10
+ }
11
+ /**
12
+ * Firma EXACTA requerida por @jmlq/logger:
13
+ * save(log: LogEntry): Promise<void>
14
+ */
15
+ async save(log) {
16
+ const dto = {
17
+ log,
18
+ };
19
+ await this.persistLogUseCase.execute(dto);
20
+ }
21
+ /**
22
+ * Firma típica de @jmlq/logger (si existe en tu interface):
23
+ * find?(filter?: LogFilterRequest): Promise<ILogResponse[]>
24
+ *
25
+ * Por ahora, dejamos una implementación mínima coherente:
26
+ * - Si aún no soportas lectura, devuelve [] (o lanza un error explícito).
27
+ * - Te recomiendo implementar find en serio (tail/list+parse) como acordamos.
28
+ */
29
+ async find(filter) {
30
+ if (!this.findLogsUseCase)
31
+ return [];
32
+ // mapeo 1:1 (misma estructura)
33
+ const internalFilter = filter;
34
+ const internal = (await this.findLogsUseCase.execute(internalFilter));
35
+ // mapeo 1:1
36
+ return internal;
37
+ }
38
+ async flush() {
39
+ await this.streamWriterPort.flush();
40
+ }
41
+ async dispose() {
42
+ await this.streamWriterPort.close();
43
+ }
44
+ }
45
+ exports.FsDatasourceAdapter = FsDatasourceAdapter;
@@ -0,0 +1,6 @@
1
+ import { ILogFileEnumeratorPort } from "../../domain/ports";
2
+ export declare class FileSystemLogFileEnumeratorAdapter implements ILogFileEnumeratorPort {
3
+ private readonly basePath;
4
+ constructor(basePath: string);
5
+ listLogFiles(): Promise<string[]>;
6
+ }
@@ -0,0 +1,54 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileSystemLogFileEnumeratorAdapter = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ class FileSystemLogFileEnumeratorAdapter {
40
+ constructor(basePath) {
41
+ this.basePath = basePath;
42
+ }
43
+ async listLogFiles() {
44
+ const entries = await fs.promises.readdir(this.basePath, {
45
+ withFileTypes: true,
46
+ });
47
+ return (entries
48
+ .filter((e) => e.isFile() && e.name.endsWith(".log"))
49
+ .map((e) => path.join(this.basePath, e.name))
50
+ // opcional: ordenar por nombre para estabilidad
51
+ .sort());
52
+ }
53
+ }
54
+ exports.FileSystemLogFileEnumeratorAdapter = FileSystemLogFileEnumeratorAdapter;
@@ -0,0 +1,4 @@
1
+ import { ILogFileLineReaderPort } from "../../domain/ports";
2
+ export declare class FileSystemLogFileLineReaderAdapter implements ILogFileLineReaderPort {
3
+ readLines(filePath: string): AsyncIterable<string>;
4
+ }
@@ -0,0 +1,53 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileSystemLogFileLineReaderAdapter = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const readline = __importStar(require("readline"));
39
+ class FileSystemLogFileLineReaderAdapter {
40
+ async *readLines(filePath) {
41
+ const stream = fs.createReadStream(filePath, { encoding: "utf8" });
42
+ const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
43
+ try {
44
+ for await (const line of rl)
45
+ yield line;
46
+ }
47
+ finally {
48
+ rl.close();
49
+ stream.close();
50
+ }
51
+ }
52
+ }
53
+ exports.FileSystemLogFileLineReaderAdapter = FileSystemLogFileLineReaderAdapter;
@@ -0,0 +1,122 @@
1
+ import { IFileSystemProviderPort } from "../../domain/ports";
2
+ /**
3
+ * Adapter de infraestructura que implementa IFsProviderPort
4
+ * utilizando la API nativa de Node.js (fs/promises).
5
+ *
6
+ * Responsabilidad:
7
+ * - Encapsular el acceso al filesystem real.
8
+ * - Traducir operaciones de alto nivel del Port (exists, read, write, etc.)
9
+ * a llamadas concretas de Node.js.
10
+ *
11
+ * Beneficios:
12
+ * - Aísla el dominio y la capa de aplicación de Node.js.
13
+ * - Permite testear fácilmente mediante mocks del port.
14
+ * - Facilita reemplazar esta implementación (memfs, fs remoto, etc.).
15
+ */
16
+ export declare class FileSystemProviderAdapter implements IFileSystemProviderPort {
17
+ /**
18
+ * Verifica si una ruta existe y es accesible.
19
+ *
20
+ * Decisión de diseño:
21
+ * - Se usa fs.access() porque es una operación liviana.
22
+ * - Si ocurre cualquier error (no existe, permisos, etc.),
23
+ * se interpreta como "no accesible".
24
+ *
25
+ * Nota:
26
+ * - Esto no distingue entre "no existe" y "sin permisos".
27
+ * - Para casos más estrictos, podría usarse fs.stat() y revisar el código de error.
28
+ */
29
+ exists(path: string): Promise<boolean>;
30
+ /**
31
+ * Crea un directorio en el filesystem.
32
+ *
33
+ * @param path Ruta del directorio a crear.
34
+ * @param options Opciones de creación (por ejemplo, recursive).
35
+ *
36
+ * Decisión de diseño:
37
+ * - Se delega completamente en fs.mkdir().
38
+ * - No se captura el error: si falla, el error debe propagarse
39
+ * para que la capa superior decida cómo manejarlo.
40
+ */
41
+ mkdir(path: string, options?: {
42
+ recursive?: boolean;
43
+ }): Promise<void>;
44
+ /**
45
+ * Lee el contenido completo de un archivo.
46
+ *
47
+ * @param path Ruta absoluta del archivo.
48
+ * @returns Buffer con el contenido del archivo.
49
+ *
50
+ * Nota:
51
+ * - El adapter no interpreta el contenido (texto/binario).
52
+ * - La capa superior decide cómo procesar el Buffer.
53
+ */
54
+ readFile(path: string): Promise<Buffer>;
55
+ /**
56
+ * Escribe datos en un archivo, reemplazando su contenido si existe.
57
+ *
58
+ * @param path Ruta absoluta del archivo.
59
+ * @param data Contenido a escribir (string o Buffer).
60
+ *
61
+ * Decisión de diseño:
62
+ * - Usa fs.writeFile(), que crea el archivo si no existe.
63
+ * - Errores de escritura se propagan.
64
+ */
65
+ writeFile(path: string, data: string | Buffer): Promise<void>;
66
+ /**
67
+ * Agrega datos al final de un archivo existente.
68
+ *
69
+ * @param path Ruta absoluta del archivo.
70
+ * @param data Contenido a agregar.
71
+ *
72
+ * Nota:
73
+ * - Si el archivo no existe, fs.appendFile() lo crea.
74
+ */
75
+ appendFile(path: string, data: string | Buffer): Promise<void>;
76
+ /**
77
+ * Obtiene información básica de un archivo.
78
+ *
79
+ * @param path Ruta absoluta del archivo.
80
+ * @returns Objeto reducido con:
81
+ * - size: tamaño del archivo en bytes.
82
+ * - mtime: fecha de última modificación.
83
+ *
84
+ * Decisión de diseño:
85
+ * - Se expone solo la metadata necesaria para el dominio/app.
86
+ * - No se filtra el objeto fs.Stats completo para evitar acoplamiento.
87
+ */
88
+ stat(path: string): Promise<{
89
+ size: number;
90
+ mtime: Date;
91
+ }>;
92
+ /**
93
+ * Lista los archivos y carpetas dentro de un directorio.
94
+ *
95
+ * @param path Ruta absoluta del directorio.
96
+ * @returns Array de nombres (no rutas completas).
97
+ *
98
+ * Nota:
99
+ * - Se devuelve string[] porque el dominio no necesita fs.Dirent.
100
+ */
101
+ readdir(path: string): Promise<string[]>;
102
+ /**
103
+ * Elimina un archivo del filesystem.
104
+ *
105
+ * @param path Ruta absoluta del archivo a eliminar.
106
+ *
107
+ * Decisión de diseño:
108
+ * - No se captura el error: si falla, la capa superior debe decidir
109
+ * cómo manejar la situación (log, retry, ignore, etc.).
110
+ */
111
+ unlink(path: string): Promise<void>;
112
+ /**
113
+ * Renombra o mueve un archivo dentro del filesystem.
114
+ *
115
+ * @param oldPath Ruta actual del archivo.
116
+ * @param newPath Nueva ruta del archivo.
117
+ *
118
+ * Nota:
119
+ * - fs.rename() puede usarse tanto para renombrar como para mover archivos.
120
+ */
121
+ rename(oldPath: string, newPath: string): Promise<void>;
122
+ }