@jmlq/logger-plugin-fs 0.1.0-alpha.6 → 0.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +165 -117
  2. package/architecture.md +426 -0
  3. package/dist/application/dto/index.d.ts +2 -0
  4. package/dist/application/dto/index.js +2 -0
  5. package/dist/application/dto/rotation-check.dto.d.ts +5 -0
  6. package/dist/application/dto/save-log.dto.d.ts +3 -3
  7. package/dist/application/dto/write-operation.dto.d.ts +5 -0
  8. package/dist/application/factory/create-fs-datasource.factory.d.ts +3 -0
  9. package/dist/application/factory/create-fs-datasource.factory.js +26 -0
  10. package/dist/application/factory/index.d.ts +1 -0
  11. package/dist/{presentation → application}/factory/index.js +1 -1
  12. package/dist/application/services/fs-datasource.service.d.ts +7 -5
  13. package/dist/application/services/fs-datasource.service.js +6 -9
  14. package/dist/application/use-cases/append-log.use-case.d.ts +12 -0
  15. package/dist/application/use-cases/append-log.use-case.js +26 -0
  16. package/dist/application/use-cases/ensure-directory.use-case.d.ts +11 -0
  17. package/dist/application/use-cases/ensure-directory.use-case.js +27 -0
  18. package/dist/application/use-cases/index.d.ts +4 -3
  19. package/dist/application/use-cases/index.js +4 -3
  20. package/dist/application/use-cases/persist-log.use-case.d.ts +19 -0
  21. package/dist/application/use-cases/persist-log.use-case.js +47 -0
  22. package/dist/application/use-cases/rotate-if-needed.use-case.d.ts +17 -0
  23. package/dist/application/use-cases/rotate-if-needed.use-case.js +28 -0
  24. package/dist/domain/ports/clock.port.d.ts +8 -0
  25. package/dist/domain/ports/file-path-adapter.port.d.ts +33 -0
  26. package/dist/domain/ports/fs-provider.port.d.ts +61 -0
  27. package/dist/domain/ports/fs-provider.port.js +2 -0
  28. package/dist/domain/ports/index.d.ts +4 -0
  29. package/dist/domain/{contracts → ports}/index.js +3 -3
  30. package/dist/domain/ports/stream-writer.port.d.ts +42 -0
  31. package/dist/domain/ports/stream-writer.port.js +2 -0
  32. package/dist/domain/value-objects/file-path.vo.d.ts +27 -0
  33. package/dist/domain/value-objects/file-path.vo.js +59 -0
  34. package/dist/domain/value-objects/file-size.vo.d.ts +14 -0
  35. package/dist/domain/value-objects/file-size.vo.js +57 -0
  36. package/dist/domain/value-objects/index.d.ts +2 -2
  37. package/dist/domain/value-objects/index.js +2 -2
  38. package/dist/index.d.ts +4 -3
  39. package/dist/index.js +14 -5
  40. package/dist/infrastructure/adapters/file-rotator.adapter.d.ts +20 -0
  41. package/dist/infrastructure/adapters/file-rotator.adapter.js +105 -0
  42. package/dist/infrastructure/adapters/fs-provider.adapter.d.ts +17 -0
  43. package/dist/infrastructure/{fs/fs-provider.js → adapters/fs-provider.adapter.js} +41 -19
  44. package/dist/infrastructure/adapters/fs-writer.adapter.d.ts +13 -0
  45. package/dist/infrastructure/adapters/fs-writer.adapter.js +71 -0
  46. package/dist/infrastructure/{fs → adapters}/index.d.ts +2 -2
  47. package/dist/infrastructure/{fs → adapters}/index.js +2 -2
  48. package/dist/infrastructure/adapters/node-clock.adapter.d.ts +4 -0
  49. package/dist/infrastructure/{fs → adapters}/node-clock.adapter.js +0 -1
  50. package/dist/infrastructure/adapters/node-file-path.adapter.d.ts +16 -0
  51. package/dist/infrastructure/adapters/node-file-path.adapter.js +117 -0
  52. package/dist/infrastructure/filesystem/index.d.ts +4 -0
  53. package/dist/infrastructure/filesystem/index.js +20 -0
  54. package/dist/infrastructure/filesystem/polices/index.d.ts +1 -0
  55. package/dist/infrastructure/filesystem/polices/index.js +5 -0
  56. package/dist/infrastructure/filesystem/polices/rotation-policy.d.ts +29 -0
  57. package/dist/infrastructure/filesystem/polices/rotation-policy.js +55 -0
  58. package/dist/infrastructure/filesystem/ports/file-rotator.port.d.ts +32 -0
  59. package/dist/infrastructure/filesystem/ports/file-rotator.port.js +2 -0
  60. package/dist/infrastructure/filesystem/ports/index.d.ts +1 -0
  61. package/dist/{domain/types → infrastructure/filesystem/ports}/index.js +1 -1
  62. package/dist/infrastructure/filesystem/types/filesystem-datasource-options.type.d.ts +49 -0
  63. package/dist/infrastructure/filesystem/types/filesystem-datasource-options.type.js +2 -0
  64. package/dist/infrastructure/filesystem/types/filesystem-rotation.type.d.ts +19 -0
  65. package/dist/infrastructure/filesystem/types/filesystem-rotation.type.js +2 -0
  66. package/dist/infrastructure/filesystem/types/filesystem-serializer.type.d.ts +10 -0
  67. package/dist/infrastructure/filesystem/types/filesystem-serializer.type.js +2 -0
  68. package/dist/infrastructure/filesystem/types/index.d.ts +3 -0
  69. package/dist/infrastructure/filesystem/types/index.js +19 -0
  70. package/dist/infrastructure/filesystem/value-objects/file-name-pattern.vo.d.ts +22 -0
  71. package/dist/infrastructure/filesystem/value-objects/file-name-pattern.vo.js +37 -0
  72. package/dist/infrastructure/filesystem/value-objects/index.d.ts +1 -0
  73. package/dist/infrastructure/{datasources → filesystem/value-objects}/index.js +1 -1
  74. package/dist/shared/errors/file-operation.error.d.ts +12 -0
  75. package/dist/shared/errors/file-operation.error.js +32 -0
  76. package/dist/shared/errors/fs-plugin.error.d.ts +4 -0
  77. package/dist/shared/errors/fs-plugin.error.js +11 -0
  78. package/dist/shared/errors/index.d.ts +3 -0
  79. package/dist/shared/errors/index.js +19 -0
  80. package/dist/shared/errors/rotation.error.d.ts +10 -0
  81. package/dist/shared/errors/rotation.error.js +25 -0
  82. package/install.md +520 -0
  83. package/package.json +39 -22
  84. package/LICENSE +0 -21
  85. package/dist/application/use-cases/append-log.usecase.d.ts +0 -7
  86. package/dist/application/use-cases/append-log.usecase.js +0 -19
  87. package/dist/application/use-cases/persist-log.usecase.d.ts +0 -23
  88. package/dist/application/use-cases/persist-log.usecase.js +0 -74
  89. package/dist/application/use-cases/rotate-if-needed.usecase.d.ts +0 -10
  90. package/dist/application/use-cases/rotate-if-needed.usecase.js +0 -39
  91. package/dist/domain/contracts/clock.contract.d.ts +0 -3
  92. package/dist/domain/contracts/file-rotator.contract.d.ts +0 -7
  93. package/dist/domain/contracts/index.d.ts +0 -4
  94. package/dist/domain/contracts/serializer.contract.d.ts +0 -3
  95. package/dist/domain/contracts/stream-writer.port.d.ts +0 -6
  96. package/dist/domain/types/index.d.ts +0 -1
  97. package/dist/domain/types/options.type.d.ts +0 -11
  98. package/dist/domain/types/options.type.js +0 -5
  99. package/dist/domain/value-objects/file-name-pattern.vo.d.ts +0 -4
  100. package/dist/domain/value-objects/file-name-pattern.vo.js +0 -14
  101. package/dist/domain/value-objects/rotation-policy.vo.d.ts +0 -7
  102. package/dist/domain/value-objects/rotation-policy.vo.js +0 -20
  103. package/dist/infrastructure/datasources/fs.datasource.d.ts +0 -17
  104. package/dist/infrastructure/datasources/fs.datasource.js +0 -84
  105. package/dist/infrastructure/datasources/index.d.ts +0 -1
  106. package/dist/infrastructure/fs/file-rotator.adapter.d.ts +0 -22
  107. package/dist/infrastructure/fs/file-rotator.adapter.js +0 -51
  108. package/dist/infrastructure/fs/fs-provider.d.ts +0 -15
  109. package/dist/infrastructure/fs/fs-writer.adapter.d.ts +0 -10
  110. package/dist/infrastructure/fs/fs-writer.adapter.js +0 -26
  111. package/dist/infrastructure/fs/node-clock.adapter.d.ts +0 -4
  112. package/dist/infrastructure/fs/path-utils.d.ts +0 -6
  113. package/dist/infrastructure/fs/path-utils.js +0 -26
  114. package/dist/presentation/factory/create-fs-datasource.d.ts +0 -15
  115. package/dist/presentation/factory/create-fs-datasource.js +0 -39
  116. package/dist/presentation/factory/index.d.ts +0 -1
  117. /package/dist/{domain/contracts/clock.contract.js → application/dto/rotation-check.dto.js} +0 -0
  118. /package/dist/{domain/contracts/file-rotator.contract.js → application/dto/write-operation.dto.js} +0 -0
  119. /package/dist/domain/{contracts/serializer.contract.js → ports/clock.port.js} +0 -0
  120. /package/dist/domain/{contracts/stream-writer.port.js → ports/file-path-adapter.port.js} +0 -0
@@ -0,0 +1,19 @@
1
+ import { IClockPort, IStreamWriterPort } from "../../domain/ports";
2
+ import { ISaveLogDto } from "../dto";
3
+ import { IAppendLogUseCase, IEnsureDirectoryUseCase, IRotateIfNeededUseCase } from ".";
4
+ import { IFileRotatorPort, RotationPolicy } from "../../infrastructure/filesystem";
5
+ export interface IPersistLogUseCase {
6
+ execute(dto: ISaveLogDto): Promise<void>;
7
+ }
8
+ export declare class PersistLogUseCase implements IPersistLogUseCase {
9
+ private readonly clockPort;
10
+ private readonly fileRotatorPort;
11
+ private readonly streamWriterPort;
12
+ private readonly rotationPolicy;
13
+ private readonly rotateIfNeededUseCase;
14
+ private readonly appendLogUseCase;
15
+ private readonly ensureDirectoryUseCase;
16
+ private readonly onError?;
17
+ constructor(clockPort: IClockPort, fileRotatorPort: IFileRotatorPort, streamWriterPort: IStreamWriterPort, rotationPolicy: RotationPolicy, rotateIfNeededUseCase: IRotateIfNeededUseCase, appendLogUseCase: IAppendLogUseCase, ensureDirectoryUseCase: IEnsureDirectoryUseCase, onError?: ((error: Error) => void | Promise<void>) | undefined);
18
+ execute(dto: ISaveLogDto): Promise<void>;
19
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PersistLogUseCase = void 0;
4
+ class PersistLogUseCase {
5
+ constructor(clockPort, fileRotatorPort, streamWriterPort, rotationPolicy, rotateIfNeededUseCase, appendLogUseCase, ensureDirectoryUseCase, onError) {
6
+ this.clockPort = clockPort;
7
+ this.fileRotatorPort = fileRotatorPort;
8
+ this.streamWriterPort = streamWriterPort;
9
+ this.rotationPolicy = rotationPolicy;
10
+ this.rotateIfNeededUseCase = rotateIfNeededUseCase;
11
+ this.appendLogUseCase = appendLogUseCase;
12
+ this.ensureDirectoryUseCase = ensureDirectoryUseCase;
13
+ this.onError = onError;
14
+ }
15
+ async execute(dto) {
16
+ try {
17
+ // Asegurar que el directorio existe
18
+ await this.ensureDirectoryUseCase.execute();
19
+ const currentDate = this.clockPort.now();
20
+ // Verificar si necesitamos rotar
21
+ await this.rotateIfNeededUseCase.execute({
22
+ currentDate,
23
+ rotationPolicy: this.rotationPolicy,
24
+ });
25
+ // Asegurar que el stream está abierto en la ruta correcta
26
+ const expectedPath = this.fileRotatorPort.getExpectedPathForDate(currentDate);
27
+ const currentPath = this.streamWriterPort.getCurrentPath();
28
+ if (!this.streamWriterPort.isOpen() ||
29
+ !currentPath ||
30
+ !currentPath.equals(expectedPath)) {
31
+ if (this.streamWriterPort.isOpen()) {
32
+ await this.streamWriterPort.close();
33
+ }
34
+ await this.streamWriterPort.open(expectedPath);
35
+ }
36
+ // Escribir el log
37
+ await this.appendLogUseCase.execute(dto);
38
+ }
39
+ catch (error) {
40
+ if (this.onError) {
41
+ await this.onError(error);
42
+ }
43
+ throw error;
44
+ }
45
+ }
46
+ }
47
+ exports.PersistLogUseCase = PersistLogUseCase;
@@ -0,0 +1,17 @@
1
+ import { IFileRotatorPort, RotationPolicy } from "../../infrastructure/filesystem";
2
+ import { IStreamWriterPort } from "../../domain/ports";
3
+ import { FilePath } from "../../domain/value-objects";
4
+ export interface IRotateIfNeededDto {
5
+ currentDate: Date;
6
+ rotationPolicy: RotationPolicy;
7
+ }
8
+ export interface IRotateIfNeededUseCase {
9
+ execute(dto: IRotateIfNeededDto): Promise<FilePath | null>;
10
+ }
11
+ export declare class RotateIfNeededUseCase implements IRotateIfNeededUseCase {
12
+ private readonly fileRotatorPort;
13
+ private readonly streamWriterPort;
14
+ private readonly onRotate?;
15
+ constructor(fileRotatorPort: IFileRotatorPort, streamWriterPort: IStreamWriterPort, onRotate?: ((oldPath: FilePath, newPath: FilePath) => void | Promise<void>) | undefined);
16
+ execute(dto: IRotateIfNeededDto): Promise<FilePath | null>;
17
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RotateIfNeededUseCase = void 0;
4
+ class RotateIfNeededUseCase {
5
+ constructor(fileRotatorPort, streamWriterPort, onRotate) {
6
+ this.fileRotatorPort = fileRotatorPort;
7
+ this.streamWriterPort = streamWriterPort;
8
+ this.onRotate = onRotate;
9
+ }
10
+ async execute(dto) {
11
+ const { currentDate, rotationPolicy } = dto;
12
+ const shouldRotate = await this.fileRotatorPort.shouldRotate(rotationPolicy, currentDate);
13
+ if (!shouldRotate) {
14
+ return null;
15
+ }
16
+ const oldPath = this.fileRotatorPort.getCurrentPath();
17
+ const newPath = this.fileRotatorPort.getExpectedPathForDate(currentDate);
18
+ if (oldPath && !oldPath.equals(newPath)) {
19
+ await this.streamWriterPort.close();
20
+ await this.streamWriterPort.open(newPath);
21
+ if (this.onRotate) {
22
+ await this.onRotate(oldPath, newPath);
23
+ }
24
+ }
25
+ return newPath;
26
+ }
27
+ }
28
+ exports.RotateIfNeededUseCase = RotateIfNeededUseCase;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Representa una abstracción del reloj del sistema dentro de la capa domain.
3
+ * Su único propósito es permitir que el dominio pueda obtener la hora actual
4
+ * sin depender directamente de new Date() ni de Date.now()
5
+ */
6
+ export interface IClockPort {
7
+ now(): Date;
8
+ }
@@ -0,0 +1,33 @@
1
+ import { FilePath } from "../value-objects";
2
+ export interface IFilePathAdapterPort {
3
+ /**
4
+ * Crea un FilePath a partir de una ruta cruda (relativa o absoluta).
5
+ * La implementación se encarga de normalizar y resolver a absoluta.
6
+ */
7
+ fromRaw(inputPath: string): FilePath;
8
+ /**
9
+ * Crea un FilePath a partir de (directorio, basename, extensión).
10
+ */
11
+ fromBasename(directory: string, basename: string, extension: string): FilePath;
12
+ /**
13
+ * Devuelve un nuevo FilePath con la misma ruta pero otra extensión.
14
+ */
15
+ withExtension(filePath: FilePath, newExtension: string): FilePath;
16
+ /**
17
+ * Devuelve un nuevo FilePath con el mismo directorio y extensión,
18
+ * pero con otro basename.
19
+ */
20
+ withBasename(filePath: FilePath, newBasename: string): FilePath;
21
+ /**
22
+ * Une segmentos a la ruta actual.
23
+ */
24
+ join(filePath: FilePath, ...segments: string[]): FilePath;
25
+ /**
26
+ * Resuelve segmentos relativos respecto al FilePath actual.
27
+ */
28
+ resolve(filePath: FilePath, ...segments: string[]): FilePath;
29
+ /**
30
+ * Obtiene la ruta relativa desde basePath hacia el FilePath.
31
+ */
32
+ relativeTo(basePath: string, filePath: FilePath): string;
33
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Es una abstracción del sistema de archivos (fs) que la capa de dominio puede solicitar sin depender directamente del módulo fs de Node
3
+ */
4
+ export interface IFsProviderPort {
5
+ /**
6
+ * Permite saber si un archivo o carpeta existe
7
+ * @param path Ruta del archivo o directorio
8
+ */
9
+ exists(path: string): Promise<boolean>;
10
+ /**
11
+ * Permite crear directorios
12
+ * @param path Ruta del directorio
13
+ * @param options Opciones de creación. Actualmente solo soporta 'recursive' (Indica si se deben crear directorios padres si no existen)
14
+ */
15
+ mkdir(path: string, options?: {
16
+ recursive?: boolean;
17
+ }): Promise<void>;
18
+ /**
19
+ * Se usa para leer registros existentes
20
+ * @param path Ruta del archivo
21
+ */
22
+ readFile(path: string): Promise<Buffer>;
23
+ /**
24
+ * Se usa para escribir registros nuevos
25
+ * @param path Ruta del archivo
26
+ * @param data Datos a escribir
27
+ */
28
+ writeFile(path: string, data: string | Buffer): Promise<void>;
29
+ /**
30
+ * Se usa para agregar registros nuevos al final del archivo
31
+ * @param path Ruta del archivo
32
+ * @param data Datos a agregar
33
+ */
34
+ appendFile(path: string, data: string | Buffer): Promise<void>;
35
+ /**
36
+ * Permite obtener metadata del archivo o directorio
37
+ * - Ver tamaño actual → política de RotationPolicy.
38
+ * - Ver fecha de modificación → rotación diaria/semanal.
39
+ * @param path Ruta del archivo o directorio
40
+ */
41
+ stat(path: string): Promise<{
42
+ size: number;
43
+ mtime: Date;
44
+ }>;
45
+ /**
46
+ * Necesario para rotación por índice y descubrimiento de archivos app.log.1, app.log.2, etc.
47
+ * @param path Ruta del directorio
48
+ */
49
+ readdir(path: string): Promise<string[]>;
50
+ /**
51
+ * Eliminar archivos antiguos.
52
+ * @param path Ruta del archivo
53
+ */
54
+ unlink(path: string): Promise<void>;
55
+ /**
56
+ * Clave para renombrar archivos cuando rotan.
57
+ * @param oldPath Ruta actual del archivo o directorio
58
+ * @param newPath Nueva ruta del archivo o directorio
59
+ */
60
+ rename(oldPath: string, newPath: string): Promise<void>;
61
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ export * from "./clock.port";
2
+ export * from "./fs-provider.port";
3
+ export * from "./stream-writer.port";
4
+ export * from "./file-path-adapter.port";
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./clock.contract"), exports);
18
- __exportStar(require("./serializer.contract"), exports);
19
- __exportStar(require("./file-rotator.contract"), exports);
17
+ __exportStar(require("./clock.port"), exports);
18
+ __exportStar(require("./fs-provider.port"), exports);
20
19
  __exportStar(require("./stream-writer.port"), exports);
20
+ __exportStar(require("./file-path-adapter.port"), exports);
@@ -0,0 +1,42 @@
1
+ import { FilePath } from "../value-objects";
2
+ /**
3
+ * Es una abstracción de un writer basado en stream, típicamente usado para escribir logs de forma eficiente sin reabrir archivos por cada línea.
4
+ * Es un Outbound Port del dominio hacia una implementación de infraestructura
5
+ */
6
+ export interface IStreamWriterPort {
7
+ /**
8
+ * Escritura directa de datos al stream
9
+ * @param data Datos a escribir
10
+ * @return Retorna true si la escritura fue exitosa, false si el buffer está lleno y se debe esperar a que se vacíe antes de escribir más
11
+ */
12
+ write(data: string): Promise<boolean>;
13
+ /**
14
+ * Abre un stream para un archivo específico.
15
+ * @param filePath Ruta del archivo a abrir para escritura como FilePath
16
+ */
17
+ open(filePath: FilePath): Promise<void>;
18
+ /**
19
+ * Cierra el stream sin finalizar el archivo, dejando posibilidad de reabrirlo o rotarlo
20
+ * @return Promise que se resuelve cuando el stream ha sido cerrado
21
+ */
22
+ close(): Promise<void>;
23
+ /**
24
+ * Buffer pendiente (si la implementación usa buffering).
25
+ * Asegura que todos los datos en el buffer hayan sido escritos al archivo
26
+ */
27
+ flush(): Promise<void>;
28
+ /**
29
+ * Permite saber si el stream está listo para aceptar escritura
30
+ * @return true si el stream está abierto y listo, false si está cerrado o no disponible
31
+ */
32
+ isOpen(): boolean;
33
+ /**
34
+ * Permite al dominio saber qué archivo está actualmente siendo usado para escribir
35
+ * @return FilePath del archivo actualmente abierto, o null si no hay ninguno abierto
36
+ */
37
+ getCurrentPath(): FilePath | null;
38
+ /**
39
+ * Finaliza el stream
40
+ */
41
+ end(): Promise<void>;
42
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,27 @@
1
+ export interface IFilePathProps {
2
+ absolutePath: string;
3
+ directory: string;
4
+ filename: string;
5
+ extension: string;
6
+ basename: string;
7
+ }
8
+ /**
9
+ * Value Object que representa una ruta de archivo ya normalizada.
10
+ * No depende de Node ni del módulo `path`. Solo encapsula datos inmutables.
11
+ */
12
+ export declare class FilePath {
13
+ private readonly _absolutePath;
14
+ private readonly _directory;
15
+ private readonly _filename;
16
+ private readonly _extension;
17
+ private readonly _basename;
18
+ constructor(props: IFilePathProps);
19
+ get value(): string;
20
+ get absolutePath(): string;
21
+ get directory(): string;
22
+ get filename(): string;
23
+ get extension(): string;
24
+ get basename(): string;
25
+ equals(other: FilePath): boolean;
26
+ toString(): string;
27
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FilePath = void 0;
4
+ /**
5
+ * Value Object que representa una ruta de archivo ya normalizada.
6
+ * No depende de Node ni del módulo `path`. Solo encapsula datos inmutables.
7
+ */
8
+ class FilePath {
9
+ constructor(props) {
10
+ const { absolutePath, directory, filename, extension, basename } = props;
11
+ if (!absolutePath ||
12
+ typeof absolutePath !== "string" ||
13
+ !absolutePath.trim()) {
14
+ throw new Error("FilePath: absolutePath must be a non-empty string");
15
+ }
16
+ if (!directory || typeof directory !== "string" || !directory.trim()) {
17
+ throw new Error("FilePath: directory must be a non-empty string");
18
+ }
19
+ if (!filename || typeof filename !== "string" || !filename.trim()) {
20
+ throw new Error("FilePath: filename must be a non-empty string");
21
+ }
22
+ if (typeof extension !== "string") {
23
+ throw new Error("FilePath: extension must be a string");
24
+ }
25
+ if (!basename || typeof basename !== "string" || !basename.trim()) {
26
+ throw new Error("FilePath: basename must be a non-empty string");
27
+ }
28
+ this._absolutePath = absolutePath;
29
+ this._directory = directory;
30
+ this._filename = filename;
31
+ this._extension = extension;
32
+ this._basename = basename;
33
+ }
34
+ get value() {
35
+ return this._absolutePath;
36
+ }
37
+ get absolutePath() {
38
+ return this._absolutePath;
39
+ }
40
+ get directory() {
41
+ return this._directory;
42
+ }
43
+ get filename() {
44
+ return this._filename;
45
+ }
46
+ get extension() {
47
+ return this._extension;
48
+ }
49
+ get basename() {
50
+ return this._basename;
51
+ }
52
+ equals(other) {
53
+ return this._absolutePath === other._absolutePath;
54
+ }
55
+ toString() {
56
+ return this._absolutePath;
57
+ }
58
+ }
59
+ exports.FilePath = FilePath;
@@ -0,0 +1,14 @@
1
+ export declare class FileSize {
2
+ private readonly _bytes;
3
+ constructor(bytes: number);
4
+ get bytes(): number;
5
+ get kilobytes(): number;
6
+ get megabytes(): number;
7
+ get gigabytes(): number;
8
+ static fromMegabytes(mb: number): FileSize;
9
+ static fromKilobytes(kb: number): FileSize;
10
+ isGreaterThan(other: FileSize): boolean;
11
+ isLessThan(other: FileSize): boolean;
12
+ equals(other: FileSize): boolean;
13
+ toString(): string;
14
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileSize = void 0;
4
+ class FileSize {
5
+ constructor(bytes) {
6
+ if (bytes < 0 || !Number.isInteger(bytes)) {
7
+ throw new Error("FileSize: bytes must be a non-negative integer");
8
+ }
9
+ this._bytes = bytes;
10
+ }
11
+ get bytes() {
12
+ return this._bytes;
13
+ }
14
+ get kilobytes() {
15
+ return this._bytes / 1024;
16
+ }
17
+ get megabytes() {
18
+ return this._bytes / (1024 * 1024);
19
+ }
20
+ get gigabytes() {
21
+ return this._bytes / (1024 * 1024 * 1024);
22
+ }
23
+ static fromMegabytes(mb) {
24
+ if (mb < 0) {
25
+ throw new Error("FileSize: megabytes must be non-negative");
26
+ }
27
+ return new FileSize(Math.floor(mb * 1024 * 1024));
28
+ }
29
+ static fromKilobytes(kb) {
30
+ if (kb < 0) {
31
+ throw new Error("FileSize: kilobytes must be non-negative");
32
+ }
33
+ return new FileSize(Math.floor(kb * 1024));
34
+ }
35
+ isGreaterThan(other) {
36
+ return this._bytes > other._bytes;
37
+ }
38
+ isLessThan(other) {
39
+ return this._bytes < other._bytes;
40
+ }
41
+ equals(other) {
42
+ return this._bytes === other._bytes;
43
+ }
44
+ toString() {
45
+ if (this._bytes >= 1024 * 1024 * 1024) {
46
+ return `${(this._bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
47
+ }
48
+ if (this._bytes >= 1024 * 1024) {
49
+ return `${(this._bytes / (1024 * 1024)).toFixed(2)} MB`;
50
+ }
51
+ if (this._bytes >= 1024) {
52
+ return `${(this._bytes / 1024).toFixed(2)} KB`;
53
+ }
54
+ return `${this._bytes} bytes`;
55
+ }
56
+ }
57
+ exports.FileSize = FileSize;
@@ -1,2 +1,2 @@
1
- export * from "./file-name-pattern.vo";
2
- export * from "./rotation-policy.vo";
1
+ export * from "./file-path.vo";
2
+ export * from "./file-size.vo";
@@ -14,5 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./file-name-pattern.vo"), exports);
18
- __exportStar(require("./rotation-policy.vo"), exports);
17
+ __exportStar(require("./file-path.vo"), exports);
18
+ __exportStar(require("./file-size.vo"), exports);
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export { RotationPolicy, type RotationBy } from "./domain/value-objects";
2
- export { createFsDatasource } from "./presentation/factory";
3
- export { FileNamePattern } from "./domain/value-objects";
1
+ export { FilePath, FileSize } from "./domain/value-objects";
2
+ export { createFsDatasource } from "./application/factory";
3
+ export { FsPluginError, FileOperationError, RotationError, } from "./shared/errors";
4
+ export { IFilesystemDatasourceOptions, FsRotationBy, IFileRotationConfig, IFsSerializer, FileNamePattern, RotationPolicy, } from "./infrastructure/filesystem";
package/dist/index.js CHANGED
@@ -1,9 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FileNamePattern = exports.createFsDatasource = exports.RotationPolicy = void 0;
3
+ exports.RotationPolicy = exports.FileNamePattern = exports.RotationError = exports.FileOperationError = exports.FsPluginError = exports.createFsDatasource = exports.FileSize = exports.FilePath = void 0;
4
+ // Value Objects
4
5
  var value_objects_1 = require("./domain/value-objects");
5
- Object.defineProperty(exports, "RotationPolicy", { enumerable: true, get: function () { return value_objects_1.RotationPolicy; } });
6
- var factory_1 = require("./presentation/factory");
6
+ Object.defineProperty(exports, "FilePath", { enumerable: true, get: function () { return value_objects_1.FilePath; } });
7
+ Object.defineProperty(exports, "FileSize", { enumerable: true, get: function () { return value_objects_1.FileSize; } });
8
+ // Factory
9
+ var factory_1 = require("./application/factory");
7
10
  Object.defineProperty(exports, "createFsDatasource", { enumerable: true, get: function () { return factory_1.createFsDatasource; } });
8
- var value_objects_2 = require("./domain/value-objects");
9
- Object.defineProperty(exports, "FileNamePattern", { enumerable: true, get: function () { return value_objects_2.FileNamePattern; } });
11
+ // Errors
12
+ var errors_1 = require("./shared/errors");
13
+ Object.defineProperty(exports, "FsPluginError", { enumerable: true, get: function () { return errors_1.FsPluginError; } });
14
+ Object.defineProperty(exports, "FileOperationError", { enumerable: true, get: function () { return errors_1.FileOperationError; } });
15
+ Object.defineProperty(exports, "RotationError", { enumerable: true, get: function () { return errors_1.RotationError; } });
16
+ var filesystem_1 = require("./infrastructure/filesystem");
17
+ Object.defineProperty(exports, "FileNamePattern", { enumerable: true, get: function () { return filesystem_1.FileNamePattern; } });
18
+ Object.defineProperty(exports, "RotationPolicy", { enumerable: true, get: function () { return filesystem_1.RotationPolicy; } });
@@ -0,0 +1,20 @@
1
+ import { IFsProviderPort, IFilePathAdapterPort } from "../../domain/ports";
2
+ import { FileSize, FilePath } from "../../domain/value-objects";
3
+ import { FileNamePattern, IFileRotatorPort, RotationPolicy } from "../filesystem";
4
+ export declare class FileRotatorAdapter implements IFileRotatorPort {
5
+ private readonly fsProvider;
6
+ private readonly filePathAdapter;
7
+ private readonly fileNamePattern;
8
+ private readonly basePath;
9
+ private currentFilePath;
10
+ constructor(fsProvider: IFsProviderPort, filePathAdapter: IFilePathAdapterPort, fileNamePattern: FileNamePattern, basePath: string);
11
+ getCurrentPath(): FilePath | null;
12
+ getExpectedPathForDate(date: Date): FilePath;
13
+ private buildPathForDate;
14
+ getFileSize(filePath: FilePath): Promise<FileSize>;
15
+ getFileSizeBytes(path: string): Promise<number>;
16
+ getNextRotationIndex(baseFilePath: FilePath): Promise<number>;
17
+ shouldRotate(policy: RotationPolicy, currentDate: Date): Promise<boolean>;
18
+ private shouldRotateByDay;
19
+ private shouldRotateBySize;
20
+ }
@@ -0,0 +1,105 @@
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
+ class FileRotatorAdapter {
6
+ constructor(fsProvider, filePathAdapter, fileNamePattern, basePath) {
7
+ this.fsProvider = fsProvider;
8
+ this.filePathAdapter = filePathAdapter;
9
+ this.fileNamePattern = fileNamePattern;
10
+ this.basePath = basePath;
11
+ this.currentFilePath = null;
12
+ }
13
+ getCurrentPath() {
14
+ return this.currentFilePath;
15
+ }
16
+ getExpectedPathForDate(date) {
17
+ const fullFilePath = this.buildPathForDate(date);
18
+ this.currentFilePath = fullFilePath;
19
+ return fullFilePath;
20
+ }
21
+ buildPathForDate(date) {
22
+ const pattern = this.fileNamePattern.pattern;
23
+ const replacements = {
24
+ "{yyyy}": date.getFullYear().toString(),
25
+ "{MM}": (date.getMonth() + 1).toString().padStart(2, "0"),
26
+ "{dd}": date.getDate().toString().padStart(2, "0"),
27
+ "{HH}": date.getHours().toString().padStart(2, "0"),
28
+ "{mm}": date.getMinutes().toString().padStart(2, "0"),
29
+ "{ss}": date.getSeconds().toString().padStart(2, "0"),
30
+ };
31
+ let fileName = pattern;
32
+ for (const [token, value] of Object.entries(replacements)) {
33
+ fileName = fileName.replace(new RegExp(token.replace(/[{}]/g, "\\$&"), "g"), value);
34
+ }
35
+ return this.filePathAdapter.join(this.filePathAdapter.fromRaw(this.basePath), fileName);
36
+ }
37
+ async getFileSize(filePath) {
38
+ try {
39
+ const stats = await this.fsProvider.stat(filePath.absolutePath);
40
+ return new value_objects_1.FileSize(stats.size);
41
+ }
42
+ catch {
43
+ return new value_objects_1.FileSize(0);
44
+ }
45
+ }
46
+ // Método de compatibilidad - deprecado, usar getFileSize
47
+ async getFileSizeBytes(path) {
48
+ const filePath = this.filePathAdapter.fromRaw(path);
49
+ const fileSize = await this.getFileSize(filePath);
50
+ return fileSize.bytes;
51
+ }
52
+ async getNextRotationIndex(baseFilePath) {
53
+ try {
54
+ const files = await this.fsProvider.readdir(baseFilePath.directory);
55
+ const pattern = new RegExp(`^${baseFilePath.basename}\\.(\\d+)${baseFilePath.extension.replace(".", "\\.")}$`);
56
+ let maxIndex = 0;
57
+ for (const file of files) {
58
+ const match = file.match(pattern);
59
+ if (match) {
60
+ const index = parseInt(match[1], 10);
61
+ if (index > maxIndex) {
62
+ maxIndex = index;
63
+ }
64
+ }
65
+ }
66
+ return maxIndex + 1;
67
+ }
68
+ catch {
69
+ return 1;
70
+ }
71
+ }
72
+ async shouldRotate(policy, currentDate) {
73
+ if (policy.by === "none") {
74
+ return false;
75
+ }
76
+ if (policy.by === "day") {
77
+ return this.shouldRotateByDay(currentDate);
78
+ }
79
+ if (policy.by === "size") {
80
+ return this.shouldRotateBySize(policy);
81
+ }
82
+ return false;
83
+ }
84
+ shouldRotateByDay(currentDate) {
85
+ if (!this.currentFilePath) {
86
+ return true; // No hay archivo actual, necesitamos crear uno
87
+ }
88
+ // Ahora solo calculamos, NO mutamos currentFilePath
89
+ const expectedPath = this.buildPathForDate(currentDate);
90
+ return !this.currentFilePath.equals(expectedPath);
91
+ }
92
+ async shouldRotateBySize(policy) {
93
+ if (!this.currentFilePath || !policy.maxSize) {
94
+ return false;
95
+ }
96
+ try {
97
+ const currentFileSize = await this.getFileSize(this.currentFilePath);
98
+ return policy.shouldRotateBySize(currentFileSize);
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ }
105
+ exports.FileRotatorAdapter = FileRotatorAdapter;
@@ -0,0 +1,17 @@
1
+ import { IFsProviderPort } from "../../domain/ports";
2
+ export declare class FsProviderAdapter implements IFsProviderPort {
3
+ exists(path: string): Promise<boolean>;
4
+ mkdir(path: string, options?: {
5
+ recursive?: boolean;
6
+ }): Promise<void>;
7
+ readFile(path: string): Promise<Buffer>;
8
+ writeFile(path: string, data: string | Buffer): Promise<void>;
9
+ appendFile(path: string, data: string | Buffer): Promise<void>;
10
+ stat(path: string): Promise<{
11
+ size: number;
12
+ mtime: Date;
13
+ }>;
14
+ readdir(path: string): Promise<string[]>;
15
+ unlink(path: string): Promise<void>;
16
+ rename(oldPath: string, newPath: string): Promise<void>;
17
+ }