@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,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
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
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.SystemFilePathAdapter = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const value_objects_1 = require("../../domain/value-objects");
|
|
39
|
+
/**
|
|
40
|
+
* SystemFilePathAdapter
|
|
41
|
+
* --------------------
|
|
42
|
+
* Adapter de infraestructura que implementa IFilePathPort usando el módulo `path`
|
|
43
|
+
* del entorno de ejecución (típicamente Node.js).
|
|
44
|
+
*
|
|
45
|
+
* Responsabilidad:
|
|
46
|
+
* - Aceptar rutas crudas (relativas o absolutas).
|
|
47
|
+
* - Normalizar y resolver siempre a una ruta absoluta.
|
|
48
|
+
* - Construir y transformar el Value Object FilePath de forma consistente.
|
|
49
|
+
*
|
|
50
|
+
* Nota de diseño:
|
|
51
|
+
* - Este adapter centraliza TODA la lógica "dependiente del sistema" (path/OS).
|
|
52
|
+
* - El dominio no debería usar `path.*` directamente.
|
|
53
|
+
*/
|
|
54
|
+
class SystemFilePathAdapter {
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Creación: desde input crudo
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
/**
|
|
59
|
+
* Crea un FilePath desde una ruta cruda (relativa o absoluta).
|
|
60
|
+
* La implementación:
|
|
61
|
+
* - valida input
|
|
62
|
+
* - normaliza
|
|
63
|
+
* - resuelve a absoluta (path.resolve)
|
|
64
|
+
*/
|
|
65
|
+
fromRaw(inputPath) {
|
|
66
|
+
// Validación de entrada: evita valores nulos, no-string o vacíos.
|
|
67
|
+
if (typeof inputPath !== "string") {
|
|
68
|
+
throw new Error("SystemFilePathAdapter.fromRaw: inputPath must be a string");
|
|
69
|
+
}
|
|
70
|
+
const trimmed = inputPath.trim();
|
|
71
|
+
if (!trimmed) {
|
|
72
|
+
throw new Error("SystemFilePathAdapter.fromRaw: inputPath cannot be empty or whitespace");
|
|
73
|
+
}
|
|
74
|
+
// Importante: NO resolvemos aquí y también en buildFromAnyPath.
|
|
75
|
+
// Reducimos redundancia: delegamos la normalización/resolución en un único helper.
|
|
76
|
+
return this.buildFromAnyPath(trimmed);
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Transformaciones: derivar nuevas rutas desde un FilePath existente
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
/**
|
|
82
|
+
* Une segmentos a la ruta base.
|
|
83
|
+
*
|
|
84
|
+
* Nota importante:
|
|
85
|
+
* - Aquí es clave definir semántica. Para evitar el caso raro:
|
|
86
|
+
* "/logs/app.log" + "x" -> "/logs/app.log/x"
|
|
87
|
+
* unimos los segmentos al DIRECTORIO del FilePath.
|
|
88
|
+
*
|
|
89
|
+
* Si tu intención era tratar FilePath como "basePath" que puede ser directorio,
|
|
90
|
+
* entonces tu VO debería representar directorios explícitamente.
|
|
91
|
+
*/
|
|
92
|
+
join(filePath, ...segments) {
|
|
93
|
+
// Unimos respecto al directorio del archivo (semántica segura).
|
|
94
|
+
const fullPath = path.join(filePath.directory, ...segments);
|
|
95
|
+
return this.buildFromAnyPath(fullPath);
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Helper privado: construcción consistente de IFilePathProps
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Construye un FilePath resolviendo y normalizando en UN SOLO lugar.
|
|
102
|
+
*
|
|
103
|
+
* Redundancia corregida:
|
|
104
|
+
* - Antes se hacía path.resolve() en métodos públicos y también en el builder.
|
|
105
|
+
* - Ahora todos pasan por este helper y aquí se aplica resolve/normalize.
|
|
106
|
+
*/
|
|
107
|
+
buildFromAnyPath(anyPath) {
|
|
108
|
+
const raw = anyPath.trim();
|
|
109
|
+
const isDirHint = /[\\\/]$/.test(raw);
|
|
110
|
+
// Resolver a absoluto y normalizar (maneja ., .., separadores OS, etc.)
|
|
111
|
+
const normalized = path.resolve(anyPath);
|
|
112
|
+
if (isDirHint) {
|
|
113
|
+
// Representa directorio
|
|
114
|
+
return new value_objects_1.FilePath({
|
|
115
|
+
kind: "dir",
|
|
116
|
+
absolutePath: normalized,
|
|
117
|
+
directory: normalized, // para dirs, directory = sí mismo
|
|
118
|
+
filename: null,
|
|
119
|
+
extension: null,
|
|
120
|
+
basename: null,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Derivar partes a partir de la ruta normalizada.
|
|
124
|
+
const directory = path.dirname(normalized);
|
|
125
|
+
const filename = path.basename(normalized);
|
|
126
|
+
const extension = path.extname(normalized);
|
|
127
|
+
const basename = path.basename(normalized, extension);
|
|
128
|
+
// Props canónicas del VO.
|
|
129
|
+
const props = {
|
|
130
|
+
kind: "file",
|
|
131
|
+
absolutePath: normalized,
|
|
132
|
+
directory,
|
|
133
|
+
filename,
|
|
134
|
+
extension,
|
|
135
|
+
basename,
|
|
136
|
+
};
|
|
137
|
+
// Crear VO: aquí el dominio puede validar invariantes adicionales si lo deseas.
|
|
138
|
+
return new value_objects_1.FilePath(props);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.SystemFilePathAdapter = SystemFilePathAdapter;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FileOperation, FileOperationErrorOptions, FsErrorScope } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Error especializado para fallos de operaciones de filesystem.
|
|
4
|
+
*
|
|
5
|
+
* Encapsula errores de I/O (read, write, delete, mkdir, move) agregando
|
|
6
|
+
* contexto técnico relevante:
|
|
7
|
+
* - operation: operación de filesystem que falló.
|
|
8
|
+
* - filePath: ruta afectada (si aplica).
|
|
9
|
+
* - cause: error original que provocó el fallo.
|
|
10
|
+
* - scope: origen del error (por ejemplo, "fs-plugin").
|
|
11
|
+
*
|
|
12
|
+
* Su propósito es:
|
|
13
|
+
* - Normalizar errores de filesystem dentro del plugin FS.
|
|
14
|
+
* - Evitar el uso de Error genérico sin contexto.
|
|
15
|
+
* - Facilitar logging, debugging y traducción a errores de capas superiores.
|
|
16
|
+
*
|
|
17
|
+
* No representa errores de dominio ni de configuración; solo fallos de I/O.
|
|
18
|
+
*/
|
|
19
|
+
export declare class FileOperationError extends Error {
|
|
20
|
+
readonly name = "FileOperationError";
|
|
21
|
+
readonly scope?: FsErrorScope;
|
|
22
|
+
readonly operation: FileOperation;
|
|
23
|
+
readonly filePath?: string;
|
|
24
|
+
readonly cause?: unknown;
|
|
25
|
+
constructor(opts: FileOperationErrorOptions);
|
|
26
|
+
static create(operation: FileOperation, filePath?: string, cause?: unknown): FileOperationError;
|
|
27
|
+
static fs(operation: FileOperation, filePath?: string, cause?: unknown): FileOperationError;
|
|
28
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileOperationError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Error especializado para fallos de operaciones de filesystem.
|
|
6
|
+
*
|
|
7
|
+
* Encapsula errores de I/O (read, write, delete, mkdir, move) agregando
|
|
8
|
+
* contexto técnico relevante:
|
|
9
|
+
* - operation: operación de filesystem que falló.
|
|
10
|
+
* - filePath: ruta afectada (si aplica).
|
|
11
|
+
* - cause: error original que provocó el fallo.
|
|
12
|
+
* - scope: origen del error (por ejemplo, "fs-plugin").
|
|
13
|
+
*
|
|
14
|
+
* Su propósito es:
|
|
15
|
+
* - Normalizar errores de filesystem dentro del plugin FS.
|
|
16
|
+
* - Evitar el uso de Error genérico sin contexto.
|
|
17
|
+
* - Facilitar logging, debugging y traducción a errores de capas superiores.
|
|
18
|
+
*
|
|
19
|
+
* No representa errores de dominio ni de configuración; solo fallos de I/O.
|
|
20
|
+
*/
|
|
21
|
+
class FileOperationError extends Error {
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
const message = opts.message ??
|
|
24
|
+
buildDefaultMessage({
|
|
25
|
+
operation: opts.operation,
|
|
26
|
+
filePath: opts.filePath,
|
|
27
|
+
});
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "FileOperationError";
|
|
30
|
+
this.scope = opts.scope;
|
|
31
|
+
this.operation = opts.operation;
|
|
32
|
+
this.filePath = opts.filePath;
|
|
33
|
+
this.cause = opts.cause;
|
|
34
|
+
}
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Factories: I/O
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
static create(operation, filePath, cause) {
|
|
39
|
+
return new FileOperationError({ operation, filePath, cause });
|
|
40
|
+
}
|
|
41
|
+
static fs(operation, filePath, cause) {
|
|
42
|
+
return new FileOperationError({
|
|
43
|
+
scope: "fs-plugin",
|
|
44
|
+
operation,
|
|
45
|
+
filePath,
|
|
46
|
+
cause,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.FileOperationError = FileOperationError;
|
|
51
|
+
function buildDefaultMessage(opts) {
|
|
52
|
+
// I/O normal
|
|
53
|
+
return `Failed to ${opts.operation} file: ${opts.filePath ?? "(unknown)"}`;
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./file-operation.error";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./file-operation.error"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type FileOperation = "read" | "write" | "delete" | "mkdir" | "move";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type FsErrorScope = "fs-plugin";
|
|
@@ -0,0 +1,19 @@
|
|
|
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-operation.type"), exports);
|
|
18
|
+
__exportStar(require("./fs-error-scope.type"), exports);
|
|
19
|
+
__exportStar(require("./file-operation-error-options.type"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types";
|