@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.
- package/README.md +229 -0
- package/architecture.md +426 -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/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 +3 -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-datasource-options.type.d.ts +45 -0
- package/dist/infrastructure/filesystem/types/filesystem-datasource-options.type.js +2 -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 +2 -0
- package/dist/infrastructure/filesystem/types/index.js +18 -0
- package/install.md +520 -0
- package/package.json +40 -12
|
@@ -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";
|
|
@@ -0,0 +1,24 @@
|
|
|
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("./file-rotator.adapter"), exports);
|
|
18
|
+
__exportStar(require("./fileSystem-datasource.adapter"), exports);
|
|
19
|
+
__exportStar(require("./filesystem-log-file-enumerator.adapter"), exports);
|
|
20
|
+
__exportStar(require("./filesystem-log-file-line-reader.adapter"), exports);
|
|
21
|
+
__exportStar(require("./filesystem-provider.adapter"), exports);
|
|
22
|
+
__exportStar(require("./log-stream-writer.adapter"), exports);
|
|
23
|
+
__exportStar(require("./system-clock.adapter"), exports);
|
|
24
|
+
__exportStar(require("./system-file-path.adapter"), exports);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ILogStreamWriterPort } from "../../domain/ports";
|
|
2
|
+
import { FilePath } from "../../domain/value-objects";
|
|
3
|
+
/**
|
|
4
|
+
* Adapter de infraestructura que implementa IStreamWriterPort
|
|
5
|
+
* usando streams de escritura de Node.js.
|
|
6
|
+
*
|
|
7
|
+
* Responsabilidad:
|
|
8
|
+
* - Gestionar el ciclo de vida de un WriteStream (open → write → flush → close).
|
|
9
|
+
* - Manejar backpressure correctamente usando el evento "drain".
|
|
10
|
+
* - Encapsular errores de I/O en FileOperationError.
|
|
11
|
+
*
|
|
12
|
+
* Este adapter NO:
|
|
13
|
+
* - Decide cuándo rotar.
|
|
14
|
+
* - Decide qué escribir.
|
|
15
|
+
* - Conoce reglas de negocio.
|
|
16
|
+
*
|
|
17
|
+
* Solo escribe datos de forma eficiente.
|
|
18
|
+
*/
|
|
19
|
+
export declare class LogStreamWriterAdapter implements ILogStreamWriterPort {
|
|
20
|
+
/**
|
|
21
|
+
* Stream activo de escritura.
|
|
22
|
+
* Es null cuando no hay archivo abierto.
|
|
23
|
+
*/
|
|
24
|
+
private stream;
|
|
25
|
+
/**
|
|
26
|
+
* Ruta del archivo actualmente abierto.
|
|
27
|
+
* Se mantiene para:
|
|
28
|
+
* - diagnóstico
|
|
29
|
+
* - errores más informativos
|
|
30
|
+
*/
|
|
31
|
+
private currentPath;
|
|
32
|
+
/**
|
|
33
|
+
* Escribe datos en el stream actual.
|
|
34
|
+
*
|
|
35
|
+
* Maneja backpressure:
|
|
36
|
+
* - Si write() devuelve false, espera al evento "drain".
|
|
37
|
+
*
|
|
38
|
+
* @param data Cadena a escribir (ya serializada).
|
|
39
|
+
* @returns true cuando la escritura fue aceptada por el stream.
|
|
40
|
+
*/
|
|
41
|
+
write(data: string): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Abre un archivo para escritura en modo append.
|
|
44
|
+
*
|
|
45
|
+
* Si ya existe un stream abierto:
|
|
46
|
+
* - Se cierra primero de forma segura.
|
|
47
|
+
*
|
|
48
|
+
* @param path FilePath del archivo a abrir.
|
|
49
|
+
*/
|
|
50
|
+
open(path: FilePath): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Cierra el stream actual de forma ordenada.
|
|
53
|
+
*
|
|
54
|
+
* - Llama a stream.end().
|
|
55
|
+
* - Limpia el estado interno.
|
|
56
|
+
*
|
|
57
|
+
* Es seguro llamar varias veces.
|
|
58
|
+
*/
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Alias semántico de close().
|
|
62
|
+
* Útil cuando el writer se usa como “sink” final.
|
|
63
|
+
*/
|
|
64
|
+
end(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Espera a que el buffer interno del stream se vacíe.
|
|
67
|
+
*
|
|
68
|
+
* No fuerza fsync; solo asegura que el buffer JS esté drenado.
|
|
69
|
+
*/
|
|
70
|
+
flush(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Indica si hay un stream abierto actualmente.
|
|
73
|
+
*/
|
|
74
|
+
isOpen(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Devuelve la ruta del archivo actualmente abierto.
|
|
77
|
+
* Puede ser null si no hay stream activo.
|
|
78
|
+
*/
|
|
79
|
+
getCurrentPath(): FilePath | null;
|
|
80
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LogStreamWriterAdapter = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
/**
|
|
7
|
+
* Adapter de infraestructura que implementa IStreamWriterPort
|
|
8
|
+
* usando streams de escritura de Node.js.
|
|
9
|
+
*
|
|
10
|
+
* Responsabilidad:
|
|
11
|
+
* - Gestionar el ciclo de vida de un WriteStream (open → write → flush → close).
|
|
12
|
+
* - Manejar backpressure correctamente usando el evento "drain".
|
|
13
|
+
* - Encapsular errores de I/O en FileOperationError.
|
|
14
|
+
*
|
|
15
|
+
* Este adapter NO:
|
|
16
|
+
* - Decide cuándo rotar.
|
|
17
|
+
* - Decide qué escribir.
|
|
18
|
+
* - Conoce reglas de negocio.
|
|
19
|
+
*
|
|
20
|
+
* Solo escribe datos de forma eficiente.
|
|
21
|
+
*/
|
|
22
|
+
class LogStreamWriterAdapter {
|
|
23
|
+
constructor() {
|
|
24
|
+
/**
|
|
25
|
+
* Stream activo de escritura.
|
|
26
|
+
* Es null cuando no hay archivo abierto.
|
|
27
|
+
*/
|
|
28
|
+
this.stream = null;
|
|
29
|
+
/**
|
|
30
|
+
* Ruta del archivo actualmente abierto.
|
|
31
|
+
* Se mantiene para:
|
|
32
|
+
* - diagnóstico
|
|
33
|
+
* - errores más informativos
|
|
34
|
+
*/
|
|
35
|
+
this.currentPath = null;
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Escritura
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/**
|
|
41
|
+
* Escribe datos en el stream actual.
|
|
42
|
+
*
|
|
43
|
+
* Maneja backpressure:
|
|
44
|
+
* - Si write() devuelve false, espera al evento "drain".
|
|
45
|
+
*
|
|
46
|
+
* @param data Cadena a escribir (ya serializada).
|
|
47
|
+
* @returns true cuando la escritura fue aceptada por el stream.
|
|
48
|
+
*/
|
|
49
|
+
async write(data) {
|
|
50
|
+
// Protección: no se puede escribir sin haber abierto un stream
|
|
51
|
+
if (!this.stream) {
|
|
52
|
+
throw errors_1.FileOperationError.fs("write", this.currentPath?.absolutePath, "Stream not opened");
|
|
53
|
+
}
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
// write() devuelve false si el buffer interno está lleno
|
|
56
|
+
const accepted = this.stream.write(data);
|
|
57
|
+
if (!accepted) {
|
|
58
|
+
// Esperar a que el buffer se drene
|
|
59
|
+
this.stream.once("drain", () => resolve(true));
|
|
60
|
+
// Error del stream (I/O real)
|
|
61
|
+
this.stream.once("error", reject);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
resolve(true);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Apertura
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Abre un archivo para escritura en modo append.
|
|
73
|
+
*
|
|
74
|
+
* Si ya existe un stream abierto:
|
|
75
|
+
* - Se cierra primero de forma segura.
|
|
76
|
+
*
|
|
77
|
+
* @param path FilePath del archivo a abrir.
|
|
78
|
+
*/
|
|
79
|
+
async open(path) {
|
|
80
|
+
// Si hay un stream previo, cerrarlo primero
|
|
81
|
+
if (this.stream) {
|
|
82
|
+
await this.close();
|
|
83
|
+
}
|
|
84
|
+
const pathString = path.absolutePath;
|
|
85
|
+
// Creamos el stream, pero NO lo asignamos todavía
|
|
86
|
+
const stream = (0, fs_1.createWriteStream)(pathString, { flags: "a" });
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
// El evento "open" garantiza que el archivo está listo
|
|
89
|
+
stream.once("open", () => {
|
|
90
|
+
this.stream = stream;
|
|
91
|
+
this.currentPath = path;
|
|
92
|
+
resolve();
|
|
93
|
+
});
|
|
94
|
+
// Error durante apertura (permisos, path inválido, etc.)
|
|
95
|
+
stream.once("error", (err) => {
|
|
96
|
+
reject(errors_1.FileOperationError.fs("write", pathString, err));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Cierre
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Cierra el stream actual de forma ordenada.
|
|
105
|
+
*
|
|
106
|
+
* - Llama a stream.end().
|
|
107
|
+
* - Limpia el estado interno.
|
|
108
|
+
*
|
|
109
|
+
* Es seguro llamar varias veces.
|
|
110
|
+
*/
|
|
111
|
+
async close() {
|
|
112
|
+
if (!this.stream)
|
|
113
|
+
return;
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
this.stream.end(() => {
|
|
116
|
+
this.stream = null;
|
|
117
|
+
this.currentPath = null;
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Alias semántico de close().
|
|
124
|
+
* Útil cuando el writer se usa como “sink” final.
|
|
125
|
+
*/
|
|
126
|
+
async end() {
|
|
127
|
+
return this.close();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Espera a que el buffer interno del stream se vacíe.
|
|
131
|
+
*
|
|
132
|
+
* No fuerza fsync; solo asegura que el buffer JS esté drenado.
|
|
133
|
+
*/
|
|
134
|
+
async flush() {
|
|
135
|
+
if (!this.stream)
|
|
136
|
+
return;
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
if (this.stream.writableNeedDrain) {
|
|
139
|
+
this.stream.once("drain", () => resolve());
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
resolve();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Estado
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
/**
|
|
150
|
+
* Indica si hay un stream abierto actualmente.
|
|
151
|
+
*/
|
|
152
|
+
isOpen() {
|
|
153
|
+
return this.stream !== null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Devuelve la ruta del archivo actualmente abierto.
|
|
157
|
+
* Puede ser null si no hay stream activo.
|
|
158
|
+
*/
|
|
159
|
+
getCurrentPath() {
|
|
160
|
+
return this.currentPath;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.LogStreamWriterAdapter = LogStreamWriterAdapter;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ISystemClockPort } from "../../domain/ports";
|
|
2
|
+
/**
|
|
3
|
+
* Adapter de infraestructura que implementa IClockPort
|
|
4
|
+
* utilizando el reloj real del sistema.
|
|
5
|
+
*
|
|
6
|
+
* Responsabilidad:
|
|
7
|
+
* - Proveer la fecha y hora actual al sistema.
|
|
8
|
+
*
|
|
9
|
+
* Propósito arquitectónico:
|
|
10
|
+
* - Evitar el uso directo de `new Date()` en dominio y aplicación.
|
|
11
|
+
* - Permitir tests deterministas mediante clocks falsos (FixedClock, MockClock).
|
|
12
|
+
* - Centralizar la noción de "tiempo actual" del sistema.
|
|
13
|
+
*
|
|
14
|
+
* Nota:
|
|
15
|
+
* - No depende de APIs específicas de Node.js.
|
|
16
|
+
* - Funciona en cualquier entorno JavaScript.
|
|
17
|
+
*/
|
|
18
|
+
export declare class SystemClockAdapter implements ISystemClockPort {
|
|
19
|
+
/**
|
|
20
|
+
* Retorna la fecha y hora actual del sistema.
|
|
21
|
+
*
|
|
22
|
+
* @returns Date actual (timestamp UTC con representación local).
|
|
23
|
+
*/
|
|
24
|
+
now(): Date;
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SystemClockAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Adapter de infraestructura que implementa IClockPort
|
|
6
|
+
* utilizando el reloj real del sistema.
|
|
7
|
+
*
|
|
8
|
+
* Responsabilidad:
|
|
9
|
+
* - Proveer la fecha y hora actual al sistema.
|
|
10
|
+
*
|
|
11
|
+
* Propósito arquitectónico:
|
|
12
|
+
* - Evitar el uso directo de `new Date()` en dominio y aplicación.
|
|
13
|
+
* - Permitir tests deterministas mediante clocks falsos (FixedClock, MockClock).
|
|
14
|
+
* - Centralizar la noción de "tiempo actual" del sistema.
|
|
15
|
+
*
|
|
16
|
+
* Nota:
|
|
17
|
+
* - No depende de APIs específicas de Node.js.
|
|
18
|
+
* - Funciona en cualquier entorno JavaScript.
|
|
19
|
+
*/
|
|
20
|
+
class SystemClockAdapter {
|
|
21
|
+
/**
|
|
22
|
+
* Retorna la fecha y hora actual del sistema.
|
|
23
|
+
*
|
|
24
|
+
* @returns Date actual (timestamp UTC con representación local).
|
|
25
|
+
*/
|
|
26
|
+
now() {
|
|
27
|
+
return new Date();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.SystemClockAdapter = SystemClockAdapter;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { FilePath } from "../../domain/value-objects";
|
|
2
|
+
import { IFilePathPort } from "../../domain/ports";
|
|
3
|
+
/**
|
|
4
|
+
* SystemFilePathAdapter
|
|
5
|
+
* --------------------
|
|
6
|
+
* Adapter de infraestructura que implementa IFilePathPort usando el módulo `path`
|
|
7
|
+
* del entorno de ejecución (típicamente Node.js).
|
|
8
|
+
*
|
|
9
|
+
* Responsabilidad:
|
|
10
|
+
* - Aceptar rutas crudas (relativas o absolutas).
|
|
11
|
+
* - Normalizar y resolver siempre a una ruta absoluta.
|
|
12
|
+
* - Construir y transformar el Value Object FilePath de forma consistente.
|
|
13
|
+
*
|
|
14
|
+
* Nota de diseño:
|
|
15
|
+
* - Este adapter centraliza TODA la lógica "dependiente del sistema" (path/OS).
|
|
16
|
+
* - El dominio no debería usar `path.*` directamente.
|
|
17
|
+
*/
|
|
18
|
+
export declare class SystemFilePathAdapter implements IFilePathPort {
|
|
19
|
+
/**
|
|
20
|
+
* Crea un FilePath desde una ruta cruda (relativa o absoluta).
|
|
21
|
+
* La implementación:
|
|
22
|
+
* - valida input
|
|
23
|
+
* - normaliza
|
|
24
|
+
* - resuelve a absoluta (path.resolve)
|
|
25
|
+
*/
|
|
26
|
+
fromRaw(inputPath: string): FilePath;
|
|
27
|
+
/**
|
|
28
|
+
* Une segmentos a la ruta base.
|
|
29
|
+
*
|
|
30
|
+
* Nota importante:
|
|
31
|
+
* - Aquí es clave definir semántica. Para evitar el caso raro:
|
|
32
|
+
* "/logs/app.log" + "x" -> "/logs/app.log/x"
|
|
33
|
+
* unimos los segmentos al DIRECTORIO del FilePath.
|
|
34
|
+
*
|
|
35
|
+
* Si tu intención era tratar FilePath como "basePath" que puede ser directorio,
|
|
36
|
+
* entonces tu VO debería representar directorios explícitamente.
|
|
37
|
+
*/
|
|
38
|
+
join(filePath: FilePath, ...segments: string[]): FilePath;
|
|
39
|
+
/**
|
|
40
|
+
* Construye un FilePath resolviendo y normalizando en UN SOLO lugar.
|
|
41
|
+
*
|
|
42
|
+
* Redundancia corregida:
|
|
43
|
+
* - Antes se hacía path.resolve() en métodos públicos y también en el builder.
|
|
44
|
+
* - Ahora todos pasan por este helper y aquí se aplica resolve/normalize.
|
|
45
|
+
*/
|
|
46
|
+
private buildFromAnyPath;
|
|
47
|
+
}
|