@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.
Files changed (119) hide show
  1. package/README.md +551 -0
  2. package/architecture.md +299 -0
  3. package/dist/application/dto/index.d.ts +1 -0
  4. package/dist/application/dto/index.js +17 -0
  5. package/dist/application/dto/rotate-if-needed.request.d.ts +10 -0
  6. package/dist/application/dto/rotate-if-needed.request.js +2 -0
  7. package/dist/application/factory/create-fs-datasource.factory.d.ts +16 -0
  8. package/dist/application/factory/create-fs-datasource.factory.js +51 -0
  9. package/dist/application/factory/index.d.ts +1 -0
  10. package/dist/application/factory/index.js +17 -0
  11. package/dist/application/types/filesystem-datasource-options.type.d.ts +45 -0
  12. package/dist/application/types/filesystem-datasource-options.type.js +2 -0
  13. package/dist/application/types/index.d.ts +1 -0
  14. package/dist/application/types/index.js +17 -0
  15. package/dist/application/use-cases/append-log.use-case.d.ts +30 -0
  16. package/dist/application/use-cases/append-log.use-case.js +39 -0
  17. package/dist/application/use-cases/ensure-directory.use-case.d.ts +58 -0
  18. package/dist/application/use-cases/ensure-directory.use-case.js +64 -0
  19. package/dist/application/use-cases/find-logs-use-case.d.ts +17 -0
  20. package/dist/application/use-cases/find-logs-use-case.js +64 -0
  21. package/dist/application/use-cases/index.d.ts +5 -0
  22. package/dist/application/use-cases/index.js +21 -0
  23. package/dist/application/use-cases/persist-log.use-case.d.ts +96 -0
  24. package/dist/application/use-cases/persist-log.use-case.js +105 -0
  25. package/dist/application/use-cases/rotate-if-needed.use-case.d.ts +55 -0
  26. package/dist/application/use-cases/rotate-if-needed.use-case.js +61 -0
  27. package/dist/domain/model/index.d.ts +1 -0
  28. package/dist/domain/model/index.js +17 -0
  29. package/dist/domain/model/log-entry.model.d.ts +8 -0
  30. package/dist/domain/model/log-entry.model.js +2 -0
  31. package/dist/domain/ports/file/file-path.port.d.ts +38 -0
  32. package/dist/domain/ports/file/file-path.port.js +2 -0
  33. package/dist/domain/ports/file/file-rotator.port.d.ts +42 -0
  34. package/dist/domain/ports/file/file-rotator.port.js +2 -0
  35. package/dist/domain/ports/file/index.d.ts +2 -0
  36. package/dist/domain/ports/file/index.js +18 -0
  37. package/dist/domain/ports/file/log-stream-writer.port.d.ts +70 -0
  38. package/dist/domain/ports/file/log-stream-writer.port.js +2 -0
  39. package/dist/domain/ports/filesystem-provider.port.d.ts +61 -0
  40. package/dist/domain/ports/filesystem-provider.port.js +2 -0
  41. package/dist/domain/ports/index.d.ts +5 -0
  42. package/dist/domain/ports/index.js +21 -0
  43. package/dist/domain/ports/logs/find/index.d.ts +2 -0
  44. package/dist/domain/ports/logs/find/index.js +18 -0
  45. package/dist/domain/ports/logs/find/log-file-line-reader.port.d.ts +3 -0
  46. package/dist/domain/ports/logs/find/log-file-line-reader.port.js +2 -0
  47. package/dist/domain/ports/logs/find/log-file-numerator.port.d.ts +3 -0
  48. package/dist/domain/ports/logs/find/log-file-numerator.port.js +2 -0
  49. package/dist/domain/ports/logs/index.d.ts +2 -0
  50. package/dist/domain/ports/logs/index.js +18 -0
  51. package/dist/domain/ports/logs/log-datasource.port.d.ts +10 -0
  52. package/dist/domain/ports/logs/log-datasource.port.js +2 -0
  53. package/dist/domain/ports/system-clock.port.d.ts +8 -0
  54. package/dist/domain/ports/system-clock.port.js +2 -0
  55. package/dist/domain/request/index.d.ts +2 -0
  56. package/dist/domain/request/index.js +18 -0
  57. package/dist/domain/request/log-filter.request.d.ts +9 -0
  58. package/dist/domain/request/log-filter.request.js +2 -0
  59. package/dist/domain/request/save-log.request.d.ts +7 -0
  60. package/dist/domain/request/save-log.request.js +2 -0
  61. package/dist/domain/response/index.d.ts +1 -0
  62. package/dist/domain/response/index.js +17 -0
  63. package/dist/domain/response/log.response.d.ts +8 -0
  64. package/dist/domain/response/log.response.js +2 -0
  65. package/dist/domain/types/fs-rotation-by.type.d.ts +8 -0
  66. package/dist/domain/types/fs-rotation-by.type.js +2 -0
  67. package/dist/domain/types/index.d.ts +1 -0
  68. package/dist/domain/types/index.js +17 -0
  69. package/dist/domain/value-objects/file-name-pattern.vo.d.ts +36 -0
  70. package/dist/domain/value-objects/file-name-pattern.vo.js +53 -0
  71. package/dist/domain/value-objects/file-path.vo.d.ts +91 -0
  72. package/dist/domain/value-objects/file-path.vo.js +100 -0
  73. package/dist/domain/value-objects/file-rotation-policy.vo.d.ts +51 -0
  74. package/dist/domain/value-objects/file-rotation-policy.vo.js +76 -0
  75. package/dist/domain/value-objects/file-size.vo.d.ts +75 -0
  76. package/dist/domain/value-objects/file-size.vo.js +114 -0
  77. package/dist/domain/value-objects/index.d.ts +5 -0
  78. package/dist/domain/value-objects/index.js +21 -0
  79. package/dist/domain/value-objects/log-level.vo.d.ts +8 -0
  80. package/dist/domain/value-objects/log-level.vo.js +13 -0
  81. package/dist/index.d.ts +4 -13
  82. package/dist/index.js +10 -38
  83. package/dist/infrastructure/adapters/file-rotator.adapter.d.ts +79 -0
  84. package/dist/infrastructure/adapters/file-rotator.adapter.js +171 -0
  85. package/dist/infrastructure/adapters/fileSystem-datasource.adapter.d.ts +26 -0
  86. package/dist/infrastructure/adapters/fileSystem-datasource.adapter.js +45 -0
  87. package/dist/infrastructure/adapters/filesystem-log-file-enumerator.adapter.d.ts +6 -0
  88. package/dist/infrastructure/adapters/filesystem-log-file-enumerator.adapter.js +54 -0
  89. package/dist/infrastructure/adapters/filesystem-log-file-line-reader.adapter.d.ts +4 -0
  90. package/dist/infrastructure/adapters/filesystem-log-file-line-reader.adapter.js +53 -0
  91. package/dist/infrastructure/adapters/filesystem-provider.adapter.d.ts +122 -0
  92. package/dist/infrastructure/adapters/filesystem-provider.adapter.js +182 -0
  93. package/dist/infrastructure/adapters/index.d.ts +8 -0
  94. package/dist/infrastructure/adapters/index.js +24 -0
  95. package/dist/infrastructure/adapters/log-stream-writer.adapter.d.ts +80 -0
  96. package/dist/infrastructure/adapters/log-stream-writer.adapter.js +163 -0
  97. package/dist/infrastructure/adapters/system-clock.adapter.d.ts +25 -0
  98. package/dist/infrastructure/adapters/system-clock.adapter.js +30 -0
  99. package/dist/infrastructure/adapters/system-file-path.adapter.d.ts +47 -0
  100. package/dist/infrastructure/adapters/system-file-path.adapter.js +141 -0
  101. package/dist/infrastructure/errors/file-operation.error.d.ts +28 -0
  102. package/dist/infrastructure/errors/file-operation.error.js +54 -0
  103. package/dist/infrastructure/errors/index.d.ts +1 -0
  104. package/dist/infrastructure/errors/index.js +17 -0
  105. package/dist/infrastructure/errors/types/file-operation-error-options.type.d.ts +8 -0
  106. package/dist/infrastructure/errors/types/file-operation-error-options.type.js +2 -0
  107. package/dist/infrastructure/errors/types/file-operation.type.d.ts +1 -0
  108. package/dist/infrastructure/errors/types/file-operation.type.js +2 -0
  109. package/dist/infrastructure/errors/types/fs-error-scope.type.d.ts +1 -0
  110. package/dist/infrastructure/errors/types/fs-error-scope.type.js +2 -0
  111. package/dist/infrastructure/errors/types/index.d.ts +3 -0
  112. package/dist/infrastructure/errors/types/index.js +19 -0
  113. package/dist/infrastructure/filesystem/index.d.ts +1 -0
  114. package/dist/infrastructure/filesystem/index.js +17 -0
  115. package/dist/infrastructure/filesystem/types/filesystem-rotation.type.d.ts +12 -0
  116. package/dist/infrastructure/filesystem/types/filesystem-rotation.type.js +2 -0
  117. package/dist/infrastructure/filesystem/types/index.d.ts +1 -0
  118. package/dist/infrastructure/filesystem/types/index.js +17 -0
  119. package/package.json +40 -13
@@ -0,0 +1,299 @@
1
+ # Arquitectura del Plugin - @jmlq/logger-plugin-fs
2
+
3
+ ## Visión General del Plugin
4
+
5
+ `@jmlq/logger-plugin-fs` es un plugin datasource especializado que resuelve la persistencia de logs en sistema de archivos para el ecosistema `@jmlq/logger`. Su arquitectura está diseñada bajo principios de Clean Architecture, proporcionando un sistema robusto de escritura de logs con capacidades avanzadas de rotación automática.
6
+
7
+ El plugin aporta al ecosistema `@jmlq/logger` la capacidad de persistir logs en archivos con gestión inteligente de rotación, asegurando que las aplicaciones puedan mantener historial de logs sin comprometer el rendimiento ni el espacio en disco. Su diseño modular permite diferentes estrategias de rotación y serialización según las necesidades específicas de cada aplicación.
8
+
9
+ Como datasource, el plugin implementa la interfaz `ILogDatasource` de `@jmlq/logger`, actuando como un adaptador entre el logger principal y el sistema de archivos, encapsulando toda la complejidad de gestión de archivos, rotación y escritura asíncrona.
10
+
11
+ ## Principios de Arquitectura
12
+
13
+ ### Clean Architecture
14
+
15
+ El plugin implementa Clean Architecture con separación clara de responsabilidades en capas concéntricas, donde las dependencias fluyen hacia adentro (Infrastructure → Application → Domain).
16
+
17
+ ### Ports & Adapters (Hexagonal Architecture)
18
+
19
+ Utiliza el patrón Ports & Adapters para aislar la lógica de negocio de las implementaciones concretas del filesystem, permitiendo testabilidad y flexibilidad en las implementaciones.
20
+
21
+ ### Domain Driven Design (DDD)
22
+
23
+ Implementa conceptos de DDD con Value Objects que encapsulan reglas de negocio y comportamientos específicos del dominio de logging y gestión de archivos.
24
+
25
+ ### Dependency Inversion
26
+
27
+ Las capas internas definen interfaces (ports) que las capas externas implementan, invirtiendo las dependencias y manteniendo la independencia del dominio.
28
+
29
+ ## Estructura del Proyecto
30
+
31
+ ### `/src/domain/` - Capa de Dominio
32
+
33
+ Contiene la lógica de negocio pura y las abstracciones fundamentales del plugin.
34
+
35
+ #### `/domain/model/`
36
+
37
+ - **[`log-entry.model.ts`](src/domain/model/log-entry.model.ts)**: Modelo de dominio que representa una entrada de log con sus propiedades y comportamientos
38
+
39
+ #### `/domain/value-objects/`
40
+
41
+ - **[`file-path.vo.ts`](src/domain/value-objects/file-path.vo.ts)**: Encapsula una ruta de archivo como objeto inmutable con validaciones
42
+ - **[`file-size.vo.ts`](src/domain/value-objects/file-size.vo.ts)**: Representa el tamaño de archivos con conversiones y comparaciones
43
+ - **[`file-name-pattern.vo.ts`](src/domain/value-objects/file-name-pattern.vo.ts)**: Maneja patrones de nombres con tokens de fecha para generación dinámica
44
+ - **[`file-rotation-policy.vo.ts`](src/domain/value-objects/file-rotation-policy.vo.ts)**: Define las reglas de rotación de archivos según diferentes estrategias
45
+ - **[`log-level.vo.ts`](src/domain/value-objects/log-level.vo.ts)**: Encapsula los niveles de log con lógica de comparación
46
+
47
+ #### `/domain/ports/`
48
+
49
+ - **[`filesystem-provider.port.ts`](src/domain/ports/filesystem-provider.port.ts)**: Contrato para operaciones del sistema de archivos (stat, mkdir, readdir, etc.)
50
+ - **[`system-clock.port.ts`](src/domain/ports/system-clock.port.ts)**: Abstracción del tiempo del sistema
51
+
52
+ #### `/domain/ports/file/`
53
+
54
+ - **[`file-path.port.ts`](src/domain/ports/file/file-path.port.ts)**: Contrato para manipulación y normalización de rutas
55
+ - **[`file-rotator.port.ts`](src/domain/ports/file/file-rotator.port.ts)**: Define operaciones de rotación de archivos
56
+ - **[`log-stream-writer.port.ts`](src/domain/ports/file/log-stream-writer.port.ts)**: Abstracción para escritura de streams de logs
57
+
58
+ #### `/domain/ports/logs/`
59
+
60
+ - **[`log-datasource.port.ts`](src/domain/ports/logs/log-datasource.port.ts)**: Contrato principal del datasource compatible con @jmlq/logger
61
+
62
+ #### `/domain/ports/logs/find/`
63
+
64
+ - **[`log-file-line-reader.port.ts`](src/domain/ports/logs/find/log-file-line-reader.port.ts)**: Contrato para lectura línea por línea de archivos de log
65
+ - **[`log-file-numerator.port.ts`](src/domain/ports/logs/find/log-file-numerator.port.ts)**: Abstracción para enumeración de archivos de log
66
+
67
+ #### `/domain/request/` y `/domain/response/`
68
+
69
+ - **[`save-log.request.ts`](src/domain/request/save-log.request.ts)**: DTO para solicitudes de guardado de logs
70
+ - **[`log-filter.request.ts`](src/domain/request/log-filter.request.ts)**: Parámetros para filtrado y búsqueda de logs
71
+ - **[`log.response.ts`](src/domain/response/log.response.ts)**: Estructura de respuesta para logs encontrados
72
+
73
+ #### `/domain/types/`
74
+
75
+ - **[`fs-rotation-by.type.ts`](src/domain/types/fs-rotation-by.type.ts)**: Define los tipos de rotación disponibles (`"none"`, `"day"`, `"size"`)
76
+
77
+ ### `/src/application/` - Capa de Aplicación
78
+
79
+ Orquesta los casos de uso del plugin y coordina entre dominio e infraestructura.
80
+
81
+ #### `/application/use-cases/`
82
+
83
+ - **[`persist-log.use-case.ts`](src/application/use-cases/persist-log.use-case.ts)**: Caso de uso principal que orquesta la persistencia de logs
84
+ - **[`append-log.use-case.ts`](src/application/use-cases/append-log.use-case.ts)**: Maneja la escritura física de logs al archivo actual
85
+ - **[`rotate-if-needed.use-case.ts`](src/application/use-cases/rotate-if-needed.use-case.ts)**: Evalúa y ejecuta rotación de archivos según políticas
86
+ - **[`ensure-directory.use-case.ts`](src/application/use-cases/ensure-directory.use-case.ts)**: Garantiza la existencia del directorio base
87
+ - **[`find-logs-use-case.ts`](src/application/use-cases/find-logs-use-case.ts)**: Implementa búsqueda y filtrado de logs históricos
88
+
89
+ #### `/application/factory/`
90
+
91
+ - **[`create-fs-datasource.factory.ts`](src/application/factory/create-fs-datasource.factory.ts)**: Factory principal que ensambla todos los componentes del plugin
92
+
93
+ #### `/application/dto/`
94
+
95
+ - **[`rotate-if-needed.request.ts`](src/application/dto/rotate-if-needed.request.ts)**: DTO específico para el caso de uso de rotación
96
+
97
+ #### `/application/types/`
98
+
99
+ - **[`filesystem-datasource-options.type.ts`](src/application/types/filesystem-datasource-options.type.ts)**: Opciones de configuración del datasource de filesystem
100
+
101
+ ### `/src/infrastructure/` - Capa de Infraestructura
102
+
103
+ Implementaciones concretas de los ports del dominio usando tecnologías específicas de Node.js.
104
+
105
+ #### `/infrastructure/adapters/`
106
+
107
+ - **[`system-file-path.adapter.ts`](src/infrastructure/adapters/system-file-path.adapter.ts)**: Implementación usando el módulo `path` de Node.js
108
+ - **[`filesystem-provider.adapter.ts`](src/infrastructure/adapters/filesystem-provider.adapter.ts)**: Adaptador del módulo `fs` de Node.js
109
+ - **[`system-clock.adapter.ts`](src/infrastructure/adapters/system-clock.adapter.ts)**: Proporciona acceso al tiempo del sistema
110
+ - **[`log-stream-writer.adapter.ts`](src/infrastructure/adapters/log-stream-writer.adapter.ts)**: Maneja WriteStream de Node.js con control de backpressure
111
+ - **[`file-rotator.adapter.ts`](src/infrastructure/adapters/file-rotator.adapter.ts)**: Implementa lógica de rotación y generación de paths basados en fechas
112
+ - **[`fileSystem-datasource.adapter.ts`](src/infrastructure/adapters/fileSystem-datasource.adapter.ts)**: Implementación principal de ILogDatasource
113
+ - **[`filesystem-log-file-enumerator.adapter.ts`](src/infrastructure/adapters/filesystem-log-file-enumerator.adapter.ts)**: Enumera archivos .log en directorios del filesystem
114
+ - **[`filesystem-log-file-line-reader.adapter.ts`](src/infrastructure/adapters/filesystem-log-file-line-reader.adapter.ts)**: Lee archivos línea por línea usando streams
115
+
116
+ #### `/infrastructure/errors/`
117
+
118
+ - **[`file-operation.error.ts`](src/infrastructure/errors/file-operation.error.ts)**: Jerarquía de errores específicos para operaciones de filesystem
119
+
120
+ #### `/infrastructure/errors/types/`
121
+
122
+ - **[`file-operation-error-options.type.ts`](src/infrastructure/errors/types/file-operation-error-options.type.ts)**: Opciones para errores de operaciones de archivo
123
+ - **[`file-operation.type.ts`](src/infrastructure/errors/types/file-operation.type.ts)**: Tipos de operaciones de archivo para categorización de errores
124
+ - **[`fs-error-scope.type.ts`](src/infrastructure/errors/types/fs-error-scope.type.ts)**: Ámbitos de errores del filesystem
125
+
126
+ #### `/infrastructure/filesystem/types/`
127
+
128
+ - **[`filesystem-rotation.type.ts`](src/infrastructure/filesystem/types/filesystem-rotation.type.ts)**: Configuración específica de rotación para filesystem
129
+
130
+ ### `/src/index.ts`
131
+
132
+ **[`index.ts`](src/index.ts)**: Punto de entrada principal del plugin que expone la API pública
133
+
134
+ ## Política de Rotación
135
+
136
+ ### Estrategias Implementadas
137
+
138
+ #### Rotación por Día (`"day"`)
139
+
140
+ La rotación se basa en cambios de fecha. El sistema evalúa si el archivo actual corresponde a la fecha presente y rota cuando detecta un cambio de día.
141
+
142
+ #### Rotación por Tamaño (`"size"`)
143
+
144
+ La rotación se activa cuando el archivo supera el límite configurado en `maxSizeMB`. Se puede configurar un límite de archivos rotados con `maxFiles`.
145
+
146
+ #### Sin Rotación (`"none"`)
147
+
148
+ El archivo crece indefinidamente sin intervención del sistema de rotación.
149
+
150
+ ### Generación de Nombres de Archivos
151
+
152
+ El sistema utiliza [`FileNamePattern`](src/domain/value-objects/file-name-pattern.vo.ts) para generar nombres dinámicamente con placeholders:
153
+
154
+ - `{yyyy}` - Año de 4 dígitos
155
+ - `{MM}` - Mes con padding (01-12)
156
+ - `{dd}` - Día con padding (01-31)
157
+
158
+ ## Flujo Principal del Datasource
159
+
160
+ ### Pipeline de Escritura
161
+
162
+ 1. **Recepción**: [`FsDatasourceAdapter.save()`](src/infrastructure/adapters/fileSystem-datasource.adapter.ts) recibe el log desde @jmlq/logger
163
+
164
+ 2. **Orquestación**: [`PersistLogUseCase`](src/application/use-cases/persist-log.use-case.ts) coordina el proceso:
165
+
166
+ - Asegura directorio base ([`EnsureDirectoryUseCase`](src/application/use-cases/ensure-directory.use-case.ts))
167
+ - Evalúa rotación ([`RotateIfNeededUseCase`](src/application/use-cases/rotate-if-needed.use-case.ts))
168
+ - Ejecuta escritura ([`AppendLogUseCase`](src/application/use-cases/append-log.use-case.ts))
169
+
170
+ 3. **Escritura**: [`LogStreamWriterAdapter`](src/infrastructure/adapters/log-stream-writer.adapter.ts) maneja la escritura física usando Node.js WriteStream
171
+
172
+ ### Pipeline de Búsqueda
173
+
174
+ 1. **Enumeración**: [`FileSystemLogFileEnumeratorAdapter`](src/infrastructure/adapters/filesystem-log-file-enumerator.adapter.ts) encuentra archivos .log
175
+
176
+ 2. **Lectura**: [`FileSystemLogFileLineReaderAdapter`](src/infrastructure/adapters/filesystem-log-file-line-reader.adapter.ts) lee líneas de archivos
177
+
178
+ 3. **Filtrado**: [`FindLogsUseCase`](src/application/use-cases/find-logs-use-case.ts) aplica filtros de nivel, fecha y contenido
179
+
180
+ ## Dependencias entre Capas
181
+
182
+ ```
183
+ ┌─────────────────────────────────────────────────────┐
184
+ │ Infrastructure │
185
+ │ ┌─────────────────┐ ┌─────────────────────────┐ │
186
+ │ │ Adapters │ │ Filesystem │ │
187
+ │ │ - FsWriter │ │ - RotationPolicy │ │
188
+ │ │ - FileRotator │ │ - FileNamePattern │ │
189
+ │ │ - FsProvider │ │ - Types & Ports │ │
190
+ │ │ - NodeClock │ │ │ │
191
+ │ │ - NodeFilePath │ │ │ │
192
+ │ └─────────────────┘ └─────────────────────────┘ │
193
+ └─────────────────────────────────────────────────────┘
194
+
195
+
196
+ ┌─────────────────────────────────────────────────────┐
197
+ │ Application │
198
+ │ ┌─────────────────┐ ┌─────────────────────────┐ │
199
+ │ │ Use Cases │ │ Factory & Service │ │
200
+ │ │ - PersistLog │ │ - createFsDatasource │ │
201
+ │ │ - AppendLog │ │ - FsDatasourceService │ │
202
+ │ │ - RotateIfNeed │ │ │ │
203
+ │ │ - EnsureDir │ │ │ │
204
+ │ └─────────────────┘ └─────────────────────────┘ │
205
+ └─────────────────────────────────────────────────────┘
206
+
207
+
208
+ ┌─────────────────────────────────────────────────────┐
209
+ │ Domain │
210
+ │ ┌─────────────────┐ ┌─────────────────────────┐ │
211
+ │ │ Value Objects │ │ Ports │ │
212
+ │ │ - FilePath │ │ - IStreamWriterPort │ │
213
+ │ │ - FileSize │ │ - IFileRotatorPort │ │
214
+ │ │ │ │ - IFsProviderPort │ │
215
+ │ │ │ │ - IClockPort │ │
216
+ │ └─────────────────┘ └─────────────────────────┘ │
217
+ └─────────────────────────────────────────────────────┘
218
+ ```
219
+
220
+ ## Patrones Técnicos Implementados
221
+
222
+ ### Adapter Pattern
223
+
224
+ Los adaptadores (`FsWriterAdapter`, `FileRotatorAdapter`, etc.) implementan interfaces del dominio, adaptando APIs externas (Node.js filesystem) a las necesidades del plugin.
225
+
226
+ ### Strategy Pattern
227
+
228
+ `RotationPolicy` encapsula diferentes estrategias de rotación (`"day"`, `"size"`, `"none"`) que pueden intercambiarse dinámicamente.
229
+
230
+ ### Value Object Pattern
231
+
232
+ `FilePath`, `FileSize`, `FileNamePattern` y `RotationPolicy` implementan el patrón Value Object, proporcionando inmutabilidad y encapsulación de conceptos del dominio.
233
+
234
+ ### Factory Pattern
235
+
236
+ `createFsDatasource` implementa el patrón Factory, encapsulando la complejidad de creación y configuración de dependencias.
237
+
238
+ ### Port & Adapter Pattern
239
+
240
+ Las interfaces en `/domain/ports/` definen contratos que las implementaciones en `/infrastructure/adapters/` cumplen, permitiendo inversión de dependencias.
241
+
242
+ ## Interacción con @jmlq/logger
243
+
244
+ ### Integración como Datasource
245
+
246
+ El plugin se integra implementando `ILogDatasource`:
247
+
248
+ ```typescript
249
+ import { createFsDatasource } from "@jmlq/logger-plugin-fs";
250
+ import { LoggerFactory } from "@jmlq/logger";
251
+
252
+ const fsDatasource = createFsDatasource({
253
+ basePath: "./logs",
254
+ rotation: { by: "day" },
255
+ });
256
+
257
+ const logger = LoggerFactory.create({
258
+ datasources: [fsDatasource],
259
+ });
260
+ ```
261
+
262
+ ### Flujo de Integración
263
+
264
+ 1. `LoggerFactory` registra el datasource creado por `createFsDatasource`
265
+ 2. Cuando se ejecuta `logger.info()`, `logger.error()`, etc., el logger principal invoca `FsDatasourceService.save()`
266
+ 3. El plugin procesa el log a través de su arquitectura interna
267
+ 4. El resultado se persiste en el filesystem según la configuración
268
+
269
+ ### Lifecycle Management
270
+
271
+ ```typescript
272
+ // Flush de buffers pendientes
273
+ await fsDatasource.flush();
274
+
275
+ // Cierre limpio de recursos
276
+ await fsDatasource.dispose();
277
+ ```
278
+
279
+ ## Consideraciones Técnicas
280
+
281
+ ### Gestión de Concurrencia
282
+
283
+ El plugin maneja escritura asíncrona usando WriteStream de Node.js con control de backpressure a través de eventos `drain` y gestión de estado interno.
284
+
285
+ ### Manejo de Errores
286
+
287
+ La arquitectura proporciona tres niveles de error:
288
+
289
+ - `FsPluginError`: Errores generales del plugin
290
+ - `FileOperationError`: Errores específicos de operaciones de archivo
291
+ - `RotationError`: Errores relacionados con rotación de archivos
292
+
293
+ ### Limitaciones Arquitecturales
294
+
295
+ 1. **Dependencia de Node.js**: El plugin está acoplado al runtime de Node.js para operaciones de filesystem
296
+ 2. **Rotación Síncrona por Fecha**: La evaluación de rotación por día se basa en comparación de paths, no en metadata de archivos
297
+ 3. **Sin Compresión**: No incluye compresión automática de archivos rotados
298
+ 4. **Estrategias de Rotación Limitadas**: Solo soporta rotación por día, tamaño o ninguna
299
+ 5. **Sin Limpieza Automática**: La limpieza de archivos antiguos debe implementarse externamente usando `maxFiles` como referencia
@@ -0,0 +1 @@
1
+ export * from "./rotate-if-needed.request";
@@ -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("./rotate-if-needed.request"), exports);
@@ -0,0 +1,10 @@
1
+ import { FileRotationPolicy } from "../../domain/value-objects";
2
+ /**
3
+ * DTO de entrada para el caso de uso RotateIfNeeded.
4
+ * - currentDate: fecha/hora actual (inyectada desde un clock para tests deterministas)
5
+ * - rotationPolicy: política de rotación (VO de dominio)
6
+ */
7
+ export interface RotateIfNeededRequest {
8
+ currentDate: Date;
9
+ rotationPolicy: FileRotationPolicy;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,16 @@
1
+ import { ILogDatasource } from "../../domain/ports";
2
+ import { IFilesystemDatasourceOptions } from "../types";
3
+ /**
4
+ * createFsDatasource
5
+ * -----------------------------------------------------------------------------
6
+ * Composition Root del plugin FS.
7
+ *
8
+ * Responsabilidad:
9
+ * - Construir el grafo de dependencias (VOs, adapters, use-cases)
10
+ * - Exponer un ILogDatasource compatible con @jmlq/logger
11
+ *
12
+ * Clean Architecture:
13
+ * - Vive en infraestructura porque integra con el core externo (@jmlq/logger)
14
+ * - Application/domain permanecen sin dependencias externas.
15
+ */
16
+ export declare function createFsDatasource(options: IFilesystemDatasourceOptions): ILogDatasource;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ // import type { ILogDatasource } from "@jmlq/logger";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.createFsDatasource = createFsDatasource;
5
+ const adapters_1 = require("../../infrastructure/adapters");
6
+ const use_cases_1 = require("../../application/use-cases");
7
+ const value_objects_1 = require("../../domain/value-objects");
8
+ /**
9
+ * createFsDatasource
10
+ * -----------------------------------------------------------------------------
11
+ * Composition Root del plugin FS.
12
+ *
13
+ * Responsabilidad:
14
+ * - Construir el grafo de dependencias (VOs, adapters, use-cases)
15
+ * - Exponer un ILogDatasource compatible con @jmlq/logger
16
+ *
17
+ * Clean Architecture:
18
+ * - Vive en infraestructura porque integra con el core externo (@jmlq/logger)
19
+ * - Application/domain permanecen sin dependencias externas.
20
+ */
21
+ function createFsDatasource(options) {
22
+ // ---------------------------------------------------------------------------
23
+ // 1) Value Objects (dominio)
24
+ // ---------------------------------------------------------------------------
25
+ const fileNamePattern = new value_objects_1.FileNamePattern(options.fileNamePattern || "app-{yyyy}{MM}{dd}.log");
26
+ const rotationPolicy = new value_objects_1.FileRotationPolicy(options.rotation?.by || "day", options.rotation?.maxSizeMB, options.rotation?.maxFiles);
27
+ // ---------------------------------------------------------------------------
28
+ // 2) Adapters (infraestructura)
29
+ // ---------------------------------------------------------------------------
30
+ const clock = new adapters_1.SystemClockAdapter();
31
+ const filePath = new adapters_1.SystemFilePathAdapter();
32
+ const fsProvider = new adapters_1.FileSystemProviderAdapter();
33
+ const writer = new adapters_1.LogStreamWriterAdapter();
34
+ const fileRotator = new adapters_1.FileRotatorAdapter(fsProvider, filePath, fileNamePattern, options.basePath);
35
+ // ---------------------------------------------------------------------------
36
+ // 3) Use-cases (application)
37
+ // Nota: no se "exponen" fuera; PersistLog orquesta todo.
38
+ // ---------------------------------------------------------------------------
39
+ const ensureDirectoryUseCase = new use_cases_1.EnsureDirectoryUseCase(fsProvider, options.basePath, options.mkdir);
40
+ const appendLogUseCase = new use_cases_1.AppendLogUseCase(writer);
41
+ const rotateIfNeededUseCase = new use_cases_1.RotateIfNeededUseCase(fileRotator, writer, options.onRotate);
42
+ const persistLogUseCase = new use_cases_1.PersistLogUseCase(clock, fileRotator, writer, rotationPolicy, rotateIfNeededUseCase, appendLogUseCase, ensureDirectoryUseCase, options.onError);
43
+ // 3.x) FindLogs use-case + IO
44
+ const fileEnumerator = new adapters_1.FileSystemLogFileEnumeratorAdapter(options.basePath);
45
+ const lineReader = new adapters_1.FileSystemLogFileLineReaderAdapter();
46
+ const findLogsUseCase = new use_cases_1.FindLogsUseCase({ fileEnumerator, lineReader });
47
+ // ---------------------------------------------------------------------------
48
+ // 4) Adapter que expone el contrato del core (@jmlq/logger)
49
+ // ---------------------------------------------------------------------------
50
+ return new adapters_1.FsDatasourceAdapter(persistLogUseCase, writer, findLogsUseCase);
51
+ }
@@ -0,0 +1 @@
1
+ export * from "./create-fs-datasource.factory";
@@ -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("./create-fs-datasource.factory"), exports);
@@ -0,0 +1,45 @@
1
+ import { FilePath } from "../../domain/value-objects";
2
+ import { IFileSystemRotationConfig } from "../../infrastructure/filesystem/types";
3
+ /**
4
+ * Opciones de configuración para la fuente de datos del sistema de archivos.
5
+ * Es el objeto de configuración para un datasource de logs basado en filesystem
6
+ */
7
+ export interface IFilesystemDatasourceOptions {
8
+ /**
9
+ * Directorio base donde se van a crear/leer los archivos de log.
10
+ */
11
+ basePath: string;
12
+ /**
13
+ * Indica si se deben crear los directorios padres si no existen
14
+ * Si es true, la implementación puede crear el basePath (con fs.mkdir({ recursive: true })) si no existe.
15
+ * Si es false o undefined, o no lo crea o depende de tu implementación.
16
+ */
17
+ mkdir?: boolean;
18
+ /**
19
+ * Patrón de nombre del archivo de log
20
+ */
21
+ fileNamePattern?: string;
22
+ /**
23
+ * Conecta con la política de rotación: "none" | "day" | "size", tamaño máximo, número de archivos, etc
24
+ */
25
+ rotation?: IFileSystemRotationConfig;
26
+ /**
27
+ * Hook que se dispara cuando se rota un archivo de log. Útil para:
28
+ * - Notificar a otro sistema.
29
+ * - Enviar el archivo rotado a S3.
30
+ * - Hacer limpieza adicional.
31
+ * @param oldPath Ruta del archivo de log antes de la rotación
32
+ * @param newPath Ruta del archivo de log después de la rotación
33
+ * @returns
34
+ */
35
+ onRotate?: (oldPath: FilePath, newPath: FilePath) => void | Promise<void>;
36
+ /**
37
+ * Hook centralizado para manejar errores del datasource:
38
+ * - Loggear en otro canal.
39
+ * - Enviar métricas / alertas.
40
+ * - Evitar que el logger “reviente” la app.
41
+ * @param error Error ocurrido en el datasource
42
+ * @returns
43
+ */
44
+ onError?: (error: Error) => void | Promise<void>;
45
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from "./filesystem-datasource-options.type";
@@ -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("./filesystem-datasource-options.type"), exports);
@@ -0,0 +1,30 @@
1
+ import { SaveLogRequest } from "../../domain/request";
2
+ import { ILogStreamWriterPort } from "../../domain/ports";
3
+ /**
4
+ * Contrato del caso de uso: permite ejecutar la operación "append log".
5
+ * Útil si quieres mockearlo en tests o exponerlo vía facade.
6
+ */
7
+ export interface IAppendLogUseCase {
8
+ execute(dto: SaveLogRequest): Promise<void>;
9
+ }
10
+ /**
11
+ * AppendLogUseCase
12
+ * -----------------------------------------------------------------------------
13
+ * Caso de uso de Application que:
14
+ * - toma un log (request model),
15
+ * - lo serializa (serializer opcional o JSON por defecto),
16
+ * - asegura salto de línea,
17
+ * - lo escribe a través de un port (writer por stream).
18
+ *
19
+ * Clean Architecture:
20
+ * - depende de puertos del dominio (ILogStreamWriterPort)
21
+ * - NO depende de infraestructura directamente.
22
+ */
23
+ export declare class AppendLogUseCase implements IAppendLogUseCase {
24
+ private readonly logStreamWriter;
25
+ constructor(logStreamWriter: ILogStreamWriterPort);
26
+ /**
27
+ * Ejecuta el caso de uso "append log".
28
+ */
29
+ execute(dto: SaveLogRequest): Promise<void>;
30
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppendLogUseCase = void 0;
4
+ /**
5
+ * AppendLogUseCase
6
+ * -----------------------------------------------------------------------------
7
+ * Caso de uso de Application que:
8
+ * - toma un log (request model),
9
+ * - lo serializa (serializer opcional o JSON por defecto),
10
+ * - asegura salto de línea,
11
+ * - lo escribe a través de un port (writer por stream).
12
+ *
13
+ * Clean Architecture:
14
+ * - depende de puertos del dominio (ILogStreamWriterPort)
15
+ * - NO depende de infraestructura directamente.
16
+ */
17
+ class AppendLogUseCase {
18
+ constructor(
19
+ // Outbound port hacia infraestructura (writer de stream)
20
+ logStreamWriter) {
21
+ this.logStreamWriter = logStreamWriter;
22
+ }
23
+ /**
24
+ * Ejecuta el caso de uso "append log".
25
+ */
26
+ async execute(dto) {
27
+ const { log } = dto;
28
+ // 1) Serializar el log
29
+ let serializedData;
30
+ serializedData = JSON.stringify(log);
31
+ // 2) Asegurar salto de línea para escritura line-by-line
32
+ if (!serializedData.endsWith("\n")) {
33
+ serializedData += "\n";
34
+ }
35
+ // 3) Escribir al stream mediante el port (IO en infraestructura)
36
+ await this.logStreamWriter.write(serializedData);
37
+ }
38
+ }
39
+ exports.AppendLogUseCase = AppendLogUseCase;
@@ -0,0 +1,58 @@
1
+ import { IFileSystemProviderPort } from "../../domain/ports";
2
+ /**
3
+ * Contrato del caso de uso: asegura la existencia del directorio base.
4
+ */
5
+ export interface IEnsureDirectoryUseCase {
6
+ execute(): Promise<void>;
7
+ }
8
+ /**
9
+ * EnsureDirectoryUseCase
10
+ * -----------------------------------------------------------------------------
11
+ * Caso de uso de Application que garantiza que exista un directorio.
12
+ *
13
+ * Clean Architecture:
14
+ * - Depende de un port de dominio (IFileSystemProviderPort) para IO.
15
+ * - No depende de infraestructura directamente.
16
+ *
17
+ * Comportamiento:
18
+ * - Si createIfNotExists es false, no hace nada (no side-effects).
19
+ * - Si el directorio no existe, lo crea (recursive).
20
+ */
21
+ export declare class EnsureDirectoryUseCase implements IEnsureDirectoryUseCase {
22
+ /**
23
+ * Port de filesystem
24
+ */
25
+ private readonly fileSystemProvider;
26
+ /**
27
+ * Ruta del directorio base a asegurar.
28
+ * Nota: aquí se usa string; idealmente podría ser un VO (FilePath/DirectoryPath)
29
+ * si tu dominio ya lo maneja.
30
+ */
31
+ private readonly basePath;
32
+ /**
33
+ * Flag de configuración: si es false, el caso de uso es no-op.
34
+ */
35
+ private readonly createIfNotExists;
36
+ constructor(
37
+ /**
38
+ * Port de filesystem
39
+ */
40
+ fileSystemProvider: IFileSystemProviderPort,
41
+ /**
42
+ * Ruta del directorio base a asegurar.
43
+ * Nota: aquí se usa string; idealmente podría ser un VO (FilePath/DirectoryPath)
44
+ * si tu dominio ya lo maneja.
45
+ */
46
+ basePath: string,
47
+ /**
48
+ * Flag de configuración: si es false, el caso de uso es no-op.
49
+ */
50
+ createIfNotExists?: boolean);
51
+ /**
52
+ * Ejecuta la operación:
53
+ * - verifica existencia
54
+ * - crea el directorio si no existe
55
+ * - encapsula errores en un error de dominio
56
+ */
57
+ execute(): Promise<void>;
58
+ }