@jmlq/logger-plugin-fs 0.1.0-alpha.1 → 0.1.0-alpha.11
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/README.md +551 -0
- package/architecture.md +299 -0
- package/dist/application/dto/index.d.ts +1 -0
- package/dist/application/dto/index.js +17 -0
- package/dist/application/dto/rotate-if-needed.request.d.ts +10 -0
- package/dist/application/dto/rotate-if-needed.request.js +2 -0
- package/dist/application/factory/create-fs-datasource.factory.d.ts +16 -0
- package/dist/application/factory/create-fs-datasource.factory.js +51 -0
- package/dist/application/factory/index.d.ts +1 -0
- package/dist/application/factory/index.js +17 -0
- package/dist/application/types/filesystem-datasource-options.type.d.ts +45 -0
- package/dist/application/types/filesystem-datasource-options.type.js +2 -0
- package/dist/application/types/index.d.ts +1 -0
- package/dist/application/types/index.js +17 -0
- package/dist/application/use-cases/append-log.use-case.d.ts +30 -0
- package/dist/application/use-cases/append-log.use-case.js +39 -0
- package/dist/application/use-cases/ensure-directory.use-case.d.ts +58 -0
- package/dist/application/use-cases/ensure-directory.use-case.js +64 -0
- package/dist/application/use-cases/find-logs-use-case.d.ts +17 -0
- package/dist/application/use-cases/find-logs-use-case.js +64 -0
- package/dist/application/use-cases/index.d.ts +5 -0
- package/dist/application/use-cases/index.js +21 -0
- package/dist/application/use-cases/persist-log.use-case.d.ts +96 -0
- package/dist/application/use-cases/persist-log.use-case.js +105 -0
- package/dist/application/use-cases/rotate-if-needed.use-case.d.ts +55 -0
- package/dist/application/use-cases/rotate-if-needed.use-case.js +61 -0
- package/dist/domain/model/index.d.ts +1 -0
- package/dist/domain/model/index.js +17 -0
- package/dist/domain/model/log-entry.model.d.ts +8 -0
- package/dist/domain/model/log-entry.model.js +2 -0
- package/dist/domain/ports/file/file-path.port.d.ts +38 -0
- package/dist/domain/ports/file/file-path.port.js +2 -0
- package/dist/domain/ports/file/file-rotator.port.d.ts +42 -0
- package/dist/domain/ports/file/file-rotator.port.js +2 -0
- package/dist/domain/ports/file/index.d.ts +2 -0
- package/dist/domain/ports/file/index.js +18 -0
- package/dist/domain/ports/file/log-stream-writer.port.d.ts +70 -0
- package/dist/domain/ports/file/log-stream-writer.port.js +2 -0
- package/dist/domain/ports/filesystem-provider.port.d.ts +61 -0
- package/dist/domain/ports/filesystem-provider.port.js +2 -0
- package/dist/domain/ports/index.d.ts +5 -0
- package/dist/domain/ports/index.js +21 -0
- package/dist/domain/ports/logs/find/index.d.ts +2 -0
- package/dist/domain/ports/logs/find/index.js +18 -0
- package/dist/domain/ports/logs/find/log-file-line-reader.port.d.ts +3 -0
- package/dist/domain/ports/logs/find/log-file-line-reader.port.js +2 -0
- package/dist/domain/ports/logs/find/log-file-numerator.port.d.ts +3 -0
- package/dist/domain/ports/logs/find/log-file-numerator.port.js +2 -0
- package/dist/domain/ports/logs/index.d.ts +2 -0
- package/dist/domain/ports/logs/index.js +18 -0
- package/dist/domain/ports/logs/log-datasource.port.d.ts +10 -0
- package/dist/domain/ports/logs/log-datasource.port.js +2 -0
- package/dist/domain/ports/system-clock.port.d.ts +8 -0
- package/dist/domain/ports/system-clock.port.js +2 -0
- package/dist/domain/request/index.d.ts +2 -0
- package/dist/domain/request/index.js +18 -0
- package/dist/domain/request/log-filter.request.d.ts +9 -0
- package/dist/domain/request/log-filter.request.js +2 -0
- package/dist/domain/request/save-log.request.d.ts +7 -0
- package/dist/domain/request/save-log.request.js +2 -0
- package/dist/domain/response/index.d.ts +1 -0
- package/dist/domain/response/index.js +17 -0
- package/dist/domain/response/log.response.d.ts +8 -0
- package/dist/domain/response/log.response.js +2 -0
- package/dist/domain/types/fs-rotation-by.type.d.ts +8 -0
- package/dist/domain/types/fs-rotation-by.type.js +2 -0
- package/dist/domain/types/index.d.ts +1 -0
- package/dist/domain/types/index.js +17 -0
- package/dist/domain/value-objects/file-name-pattern.vo.d.ts +36 -0
- package/dist/domain/value-objects/file-name-pattern.vo.js +53 -0
- package/dist/domain/value-objects/file-path.vo.d.ts +91 -0
- package/dist/domain/value-objects/file-path.vo.js +100 -0
- package/dist/domain/value-objects/file-rotation-policy.vo.d.ts +51 -0
- package/dist/domain/value-objects/file-rotation-policy.vo.js +76 -0
- package/dist/domain/value-objects/file-size.vo.d.ts +75 -0
- package/dist/domain/value-objects/file-size.vo.js +114 -0
- package/dist/domain/value-objects/index.d.ts +5 -0
- package/dist/domain/value-objects/index.js +21 -0
- package/dist/domain/value-objects/log-level.vo.d.ts +8 -0
- package/dist/domain/value-objects/log-level.vo.js +13 -0
- package/dist/index.d.ts +4 -13
- package/dist/index.js +10 -38
- package/dist/infrastructure/adapters/file-rotator.adapter.d.ts +79 -0
- package/dist/infrastructure/adapters/file-rotator.adapter.js +171 -0
- package/dist/infrastructure/adapters/fileSystem-datasource.adapter.d.ts +26 -0
- package/dist/infrastructure/adapters/fileSystem-datasource.adapter.js +45 -0
- package/dist/infrastructure/adapters/filesystem-log-file-enumerator.adapter.d.ts +6 -0
- package/dist/infrastructure/adapters/filesystem-log-file-enumerator.adapter.js +54 -0
- package/dist/infrastructure/adapters/filesystem-log-file-line-reader.adapter.d.ts +4 -0
- package/dist/infrastructure/adapters/filesystem-log-file-line-reader.adapter.js +53 -0
- package/dist/infrastructure/adapters/filesystem-provider.adapter.d.ts +122 -0
- package/dist/infrastructure/adapters/filesystem-provider.adapter.js +182 -0
- package/dist/infrastructure/adapters/index.d.ts +8 -0
- package/dist/infrastructure/adapters/index.js +24 -0
- package/dist/infrastructure/adapters/log-stream-writer.adapter.d.ts +80 -0
- package/dist/infrastructure/adapters/log-stream-writer.adapter.js +163 -0
- package/dist/infrastructure/adapters/system-clock.adapter.d.ts +25 -0
- package/dist/infrastructure/adapters/system-clock.adapter.js +30 -0
- package/dist/infrastructure/adapters/system-file-path.adapter.d.ts +47 -0
- package/dist/infrastructure/adapters/system-file-path.adapter.js +141 -0
- package/dist/infrastructure/errors/file-operation.error.d.ts +28 -0
- package/dist/infrastructure/errors/file-operation.error.js +54 -0
- package/dist/infrastructure/errors/index.d.ts +1 -0
- package/dist/infrastructure/errors/index.js +17 -0
- package/dist/infrastructure/errors/types/file-operation-error-options.type.d.ts +8 -0
- package/dist/infrastructure/errors/types/file-operation-error-options.type.js +2 -0
- package/dist/infrastructure/errors/types/file-operation.type.d.ts +1 -0
- package/dist/infrastructure/errors/types/file-operation.type.js +2 -0
- package/dist/infrastructure/errors/types/fs-error-scope.type.d.ts +1 -0
- package/dist/infrastructure/errors/types/fs-error-scope.type.js +2 -0
- package/dist/infrastructure/errors/types/index.d.ts +3 -0
- package/dist/infrastructure/errors/types/index.js +19 -0
- package/dist/infrastructure/filesystem/index.d.ts +1 -0
- package/dist/infrastructure/filesystem/index.js +17 -0
- package/dist/infrastructure/filesystem/types/filesystem-rotation.type.d.ts +12 -0
- package/dist/infrastructure/filesystem/types/filesystem-rotation.type.js +2 -0
- package/dist/infrastructure/filesystem/types/index.d.ts +1 -0
- package/dist/infrastructure/filesystem/types/index.js +17 -0
- package/package.json +40 -13
|
@@ -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,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,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
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
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.FileSystemProviderAdapter = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
/**
|
|
39
|
+
* Adapter de infraestructura que implementa IFsProviderPort
|
|
40
|
+
* utilizando la API nativa de Node.js (fs/promises).
|
|
41
|
+
*
|
|
42
|
+
* Responsabilidad:
|
|
43
|
+
* - Encapsular el acceso al filesystem real.
|
|
44
|
+
* - Traducir operaciones de alto nivel del Port (exists, read, write, etc.)
|
|
45
|
+
* a llamadas concretas de Node.js.
|
|
46
|
+
*
|
|
47
|
+
* Beneficios:
|
|
48
|
+
* - Aísla el dominio y la capa de aplicación de Node.js.
|
|
49
|
+
* - Permite testear fácilmente mediante mocks del port.
|
|
50
|
+
* - Facilita reemplazar esta implementación (memfs, fs remoto, etc.).
|
|
51
|
+
*/
|
|
52
|
+
class FileSystemProviderAdapter {
|
|
53
|
+
/**
|
|
54
|
+
* Verifica si una ruta existe y es accesible.
|
|
55
|
+
*
|
|
56
|
+
* Decisión de diseño:
|
|
57
|
+
* - Se usa fs.access() porque es una operación liviana.
|
|
58
|
+
* - Si ocurre cualquier error (no existe, permisos, etc.),
|
|
59
|
+
* se interpreta como "no accesible".
|
|
60
|
+
*
|
|
61
|
+
* Nota:
|
|
62
|
+
* - Esto no distingue entre "no existe" y "sin permisos".
|
|
63
|
+
* - Para casos más estrictos, podría usarse fs.stat() y revisar el código de error.
|
|
64
|
+
*/
|
|
65
|
+
async exists(path) {
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(path);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Crea un directorio en el filesystem.
|
|
76
|
+
*
|
|
77
|
+
* @param path Ruta del directorio a crear.
|
|
78
|
+
* @param options Opciones de creación (por ejemplo, recursive).
|
|
79
|
+
*
|
|
80
|
+
* Decisión de diseño:
|
|
81
|
+
* - Se delega completamente en fs.mkdir().
|
|
82
|
+
* - No se captura el error: si falla, el error debe propagarse
|
|
83
|
+
* para que la capa superior decida cómo manejarlo.
|
|
84
|
+
*/
|
|
85
|
+
async mkdir(path, options) {
|
|
86
|
+
await fs.mkdir(path, options);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Lee el contenido completo de un archivo.
|
|
90
|
+
*
|
|
91
|
+
* @param path Ruta absoluta del archivo.
|
|
92
|
+
* @returns Buffer con el contenido del archivo.
|
|
93
|
+
*
|
|
94
|
+
* Nota:
|
|
95
|
+
* - El adapter no interpreta el contenido (texto/binario).
|
|
96
|
+
* - La capa superior decide cómo procesar el Buffer.
|
|
97
|
+
*/
|
|
98
|
+
async readFile(path) {
|
|
99
|
+
return fs.readFile(path);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Escribe datos en un archivo, reemplazando su contenido si existe.
|
|
103
|
+
*
|
|
104
|
+
* @param path Ruta absoluta del archivo.
|
|
105
|
+
* @param data Contenido a escribir (string o Buffer).
|
|
106
|
+
*
|
|
107
|
+
* Decisión de diseño:
|
|
108
|
+
* - Usa fs.writeFile(), que crea el archivo si no existe.
|
|
109
|
+
* - Errores de escritura se propagan.
|
|
110
|
+
*/
|
|
111
|
+
async writeFile(path, data) {
|
|
112
|
+
await fs.writeFile(path, data);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Agrega datos al final de un archivo existente.
|
|
116
|
+
*
|
|
117
|
+
* @param path Ruta absoluta del archivo.
|
|
118
|
+
* @param data Contenido a agregar.
|
|
119
|
+
*
|
|
120
|
+
* Nota:
|
|
121
|
+
* - Si el archivo no existe, fs.appendFile() lo crea.
|
|
122
|
+
*/
|
|
123
|
+
async appendFile(path, data) {
|
|
124
|
+
await fs.appendFile(path, data);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Obtiene información básica de un archivo.
|
|
128
|
+
*
|
|
129
|
+
* @param path Ruta absoluta del archivo.
|
|
130
|
+
* @returns Objeto reducido con:
|
|
131
|
+
* - size: tamaño del archivo en bytes.
|
|
132
|
+
* - mtime: fecha de última modificación.
|
|
133
|
+
*
|
|
134
|
+
* Decisión de diseño:
|
|
135
|
+
* - Se expone solo la metadata necesaria para el dominio/app.
|
|
136
|
+
* - No se filtra el objeto fs.Stats completo para evitar acoplamiento.
|
|
137
|
+
*/
|
|
138
|
+
async stat(path) {
|
|
139
|
+
const stats = await fs.stat(path);
|
|
140
|
+
return {
|
|
141
|
+
size: stats.size,
|
|
142
|
+
mtime: stats.mtime,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Lista los archivos y carpetas dentro de un directorio.
|
|
147
|
+
*
|
|
148
|
+
* @param path Ruta absoluta del directorio.
|
|
149
|
+
* @returns Array de nombres (no rutas completas).
|
|
150
|
+
*
|
|
151
|
+
* Nota:
|
|
152
|
+
* - Se devuelve string[] porque el dominio no necesita fs.Dirent.
|
|
153
|
+
*/
|
|
154
|
+
async readdir(path) {
|
|
155
|
+
return fs.readdir(path);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Elimina un archivo del filesystem.
|
|
159
|
+
*
|
|
160
|
+
* @param path Ruta absoluta del archivo a eliminar.
|
|
161
|
+
*
|
|
162
|
+
* Decisión de diseño:
|
|
163
|
+
* - No se captura el error: si falla, la capa superior debe decidir
|
|
164
|
+
* cómo manejar la situación (log, retry, ignore, etc.).
|
|
165
|
+
*/
|
|
166
|
+
async unlink(path) {
|
|
167
|
+
await fs.unlink(path);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Renombra o mueve un archivo dentro del filesystem.
|
|
171
|
+
*
|
|
172
|
+
* @param oldPath Ruta actual del archivo.
|
|
173
|
+
* @param newPath Nueva ruta del archivo.
|
|
174
|
+
*
|
|
175
|
+
* Nota:
|
|
176
|
+
* - fs.rename() puede usarse tanto para renombrar como para mover archivos.
|
|
177
|
+
*/
|
|
178
|
+
async rename(oldPath, newPath) {
|
|
179
|
+
await fs.rename(oldPath, newPath);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.FileSystemProviderAdapter = FileSystemProviderAdapter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./file-rotator.adapter";
|
|
2
|
+
export * from "./fileSystem-datasource.adapter";
|
|
3
|
+
export * from "./filesystem-log-file-enumerator.adapter";
|
|
4
|
+
export * from "./filesystem-log-file-line-reader.adapter";
|
|
5
|
+
export * from "./filesystem-provider.adapter";
|
|
6
|
+
export * from "./log-stream-writer.adapter";
|
|
7
|
+
export * from "./system-clock.adapter";
|
|
8
|
+
export * from "./system-file-path.adapter";
|