@bgord/bun 1.9.0 → 1.9.2

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 (546) hide show
  1. package/dist/basic-auth-password.vo.d.ts +1 -1
  2. package/dist/basic-auth-password.vo.d.ts.map +1 -1
  3. package/dist/basic-auth-password.vo.js +1 -1
  4. package/dist/basic-auth-password.vo.js.map +1 -1
  5. package/dist/basic-auth-username.vo.d.ts +1 -1
  6. package/dist/basic-auth-username.vo.d.ts.map +1 -1
  7. package/dist/basic-auth-username.vo.js +1 -1
  8. package/dist/basic-auth-username.vo.js.map +1 -1
  9. package/dist/binary.vo.d.ts +1 -1
  10. package/dist/binary.vo.d.ts.map +1 -1
  11. package/dist/binary.vo.js +1 -1
  12. package/dist/binary.vo.js.map +1 -1
  13. package/dist/cache-subject-segment-env.strategy.d.ts +3 -3
  14. package/dist/client-ip.vo.d.ts +1 -1
  15. package/dist/client-ip.vo.d.ts.map +1 -1
  16. package/dist/client-ip.vo.js +1 -1
  17. package/dist/client-ip.vo.js.map +1 -1
  18. package/dist/client-user-agent.vo.d.ts +1 -1
  19. package/dist/client-user-agent.vo.d.ts.map +1 -1
  20. package/dist/client-user-agent.vo.js +1 -1
  21. package/dist/client-user-agent.vo.js.map +1 -1
  22. package/dist/command.types.d.ts +1 -1
  23. package/dist/command.types.d.ts.map +1 -1
  24. package/dist/commit-sha-value.vo.d.ts +1 -1
  25. package/dist/commit-sha-value.vo.d.ts.map +1 -1
  26. package/dist/commit-sha-value.vo.js +1 -1
  27. package/dist/commit-sha-value.vo.js.map +1 -1
  28. package/dist/correlation-id.vo.d.ts +1 -1
  29. package/dist/correlation-id.vo.d.ts.map +1 -1
  30. package/dist/dispatching-event-store.d.ts +1 -1
  31. package/dist/dispatching-event-store.d.ts.map +1 -1
  32. package/dist/encryption-key-value.vo.d.ts +1 -1
  33. package/dist/encryption-key-value.vo.d.ts.map +1 -1
  34. package/dist/encryption-key-value.vo.js +1 -1
  35. package/dist/encryption-key-value.vo.js.map +1 -1
  36. package/dist/environment-loader-encrypted.adapter.d.ts +4 -4
  37. package/dist/environment-loader-encrypted.adapter.d.ts.map +1 -1
  38. package/dist/environment-loader-noop.adapter.d.ts +3 -3
  39. package/dist/environment-loader-noop.adapter.d.ts.map +1 -1
  40. package/dist/environment-loader-process-safe.adapter.d.ts +4 -4
  41. package/dist/environment-loader-process-safe.adapter.d.ts.map +1 -1
  42. package/dist/environment-loader-process.adapter.d.ts +4 -4
  43. package/dist/environment-loader-process.adapter.d.ts.map +1 -1
  44. package/dist/environment-loader.port.d.ts +3 -3
  45. package/dist/environment-loader.port.d.ts.map +1 -1
  46. package/dist/event-envelope.d.ts +1 -1
  47. package/dist/event-envelope.d.ts.map +1 -1
  48. package/dist/event-envelope.js +1 -1
  49. package/dist/event-envelope.js.map +1 -1
  50. package/dist/event-handler-bare.strategy.d.ts +1 -1
  51. package/dist/event-handler-bare.strategy.d.ts.map +1 -1
  52. package/dist/event-handler-noop.strategy.d.ts +1 -1
  53. package/dist/event-handler-noop.strategy.d.ts.map +1 -1
  54. package/dist/event-handler-with-logger.strategy.d.ts +1 -1
  55. package/dist/event-handler-with-logger.strategy.d.ts.map +1 -1
  56. package/dist/event-handler.strategy.d.ts +1 -1
  57. package/dist/event-handler.strategy.d.ts.map +1 -1
  58. package/dist/event-store.d.ts +1 -1
  59. package/dist/event-store.d.ts.map +1 -1
  60. package/dist/event-stream.vo.d.ts +1 -1
  61. package/dist/event-stream.vo.d.ts.map +1 -1
  62. package/dist/event-stream.vo.js +1 -1
  63. package/dist/event-stream.vo.js.map +1 -1
  64. package/dist/event.types.d.ts +1 -1
  65. package/dist/event.types.d.ts.map +1 -1
  66. package/dist/hash-value.vo.d.ts +1 -1
  67. package/dist/hash-value.vo.d.ts.map +1 -1
  68. package/dist/hash-value.vo.js +1 -1
  69. package/dist/hash-value.vo.js.map +1 -1
  70. package/dist/hcaptcha-secret-key.vo.d.ts +1 -1
  71. package/dist/hcaptcha-secret-key.vo.d.ts.map +1 -1
  72. package/dist/hcaptcha-secret-key.vo.js +1 -1
  73. package/dist/hcaptcha-secret-key.vo.js.map +1 -1
  74. package/dist/hcaptcha-site-key.vo.d.ts +1 -1
  75. package/dist/hcaptcha-site-key.vo.d.ts.map +1 -1
  76. package/dist/hcaptcha-site-key.vo.js +1 -1
  77. package/dist/hcaptcha-site-key.vo.js.map +1 -1
  78. package/dist/healthcheck.service.d.ts +3 -3
  79. package/dist/logger-winston-local.adapter.d.ts.map +1 -1
  80. package/dist/logger-winston-local.adapter.js +2 -1
  81. package/dist/logger-winston-local.adapter.js.map +1 -1
  82. package/dist/logger-winston-production.adapter.d.ts.map +1 -1
  83. package/dist/logger-winston-production.adapter.js +3 -2
  84. package/dist/logger-winston-production.adapter.js.map +1 -1
  85. package/dist/logger-winston.adapter.d.ts +2 -2
  86. package/dist/logger.port.d.ts +3 -3
  87. package/dist/logger.port.d.ts.map +1 -1
  88. package/dist/logger.port.js +1 -1
  89. package/dist/logger.port.js.map +1 -1
  90. package/dist/mailer-content-html.vo.d.ts +1 -1
  91. package/dist/mailer-content-html.vo.d.ts.map +1 -1
  92. package/dist/mailer-content-html.vo.js +1 -1
  93. package/dist/mailer-content-html.vo.js.map +1 -1
  94. package/dist/mailer-subject.vo.d.ts +1 -1
  95. package/dist/mailer-subject.vo.d.ts.map +1 -1
  96. package/dist/mailer-subject.vo.js +1 -1
  97. package/dist/mailer-subject.vo.js.map +1 -1
  98. package/dist/modules/history/events/HISTORY_CLEARED_EVENT.d.ts +1 -1
  99. package/dist/modules/history/events/HISTORY_CLEARED_EVENT.d.ts.map +1 -1
  100. package/dist/modules/history/events/HISTORY_CLEARED_EVENT.js +1 -1
  101. package/dist/modules/history/events/HISTORY_CLEARED_EVENT.js.map +1 -1
  102. package/dist/modules/history/events/HISTORY_POPULATED_EVENT.d.ts +1 -1
  103. package/dist/modules/history/events/HISTORY_POPULATED_EVENT.d.ts.map +1 -1
  104. package/dist/modules/history/events/HISTORY_POPULATED_EVENT.js +1 -1
  105. package/dist/modules/history/events/HISTORY_POPULATED_EVENT.js.map +1 -1
  106. package/dist/modules/history/value-objects/history-created-at.d.ts +1 -1
  107. package/dist/modules/history/value-objects/history-created-at.d.ts.map +1 -1
  108. package/dist/modules/history/value-objects/history-operation.d.ts +1 -1
  109. package/dist/modules/history/value-objects/history-operation.d.ts.map +1 -1
  110. package/dist/modules/history/value-objects/history-operation.js +1 -1
  111. package/dist/modules/history/value-objects/history-operation.js.map +1 -1
  112. package/dist/modules/history/value-objects/history-payload.d.ts +1 -1
  113. package/dist/modules/history/value-objects/history-payload.d.ts.map +1 -1
  114. package/dist/modules/history/value-objects/history-payload.js +1 -1
  115. package/dist/modules/history/value-objects/history-payload.js.map +1 -1
  116. package/dist/modules/history/value-objects/history-subject.d.ts +1 -1
  117. package/dist/modules/history/value-objects/history-subject.d.ts.map +1 -1
  118. package/dist/modules/history/value-objects/history-subject.js +1 -1
  119. package/dist/modules/history/value-objects/history-subject.js.map +1 -1
  120. package/dist/modules/history/value-objects/history.d.ts +1 -1
  121. package/dist/modules/history/value-objects/history.d.ts.map +1 -1
  122. package/dist/modules/history/value-objects/history.js +1 -1
  123. package/dist/modules/history/value-objects/history.js.map +1 -1
  124. package/dist/modules/preferences/commands/SET_USER_LANGUAGE_COMMAND.d.ts +1 -1
  125. package/dist/modules/preferences/commands/SET_USER_LANGUAGE_COMMAND.d.ts.map +1 -1
  126. package/dist/modules/preferences/commands/SET_USER_LANGUAGE_COMMAND.js +1 -1
  127. package/dist/modules/preferences/commands/SET_USER_LANGUAGE_COMMAND.js.map +1 -1
  128. package/dist/modules/preferences/events/USER_LANGUAGE_SET_EVENT.d.ts +1 -1
  129. package/dist/modules/preferences/events/USER_LANGUAGE_SET_EVENT.d.ts.map +1 -1
  130. package/dist/modules/preferences/events/USER_LANGUAGE_SET_EVENT.js +1 -1
  131. package/dist/modules/preferences/events/USER_LANGUAGE_SET_EVENT.js.map +1 -1
  132. package/dist/modules/system/events/HOUR_HAS_PASSED_EVENT.d.ts +1 -1
  133. package/dist/modules/system/events/HOUR_HAS_PASSED_EVENT.d.ts.map +1 -1
  134. package/dist/modules/system/events/HOUR_HAS_PASSED_EVENT.js +1 -1
  135. package/dist/modules/system/events/HOUR_HAS_PASSED_EVENT.js.map +1 -1
  136. package/dist/modules/system/events/MINUTE_HAS_PASSED_EVENT.d.ts +1 -1
  137. package/dist/modules/system/events/MINUTE_HAS_PASSED_EVENT.d.ts.map +1 -1
  138. package/dist/modules/system/events/MINUTE_HAS_PASSED_EVENT.js +1 -1
  139. package/dist/modules/system/events/MINUTE_HAS_PASSED_EVENT.js.map +1 -1
  140. package/dist/modules/system/events/SECURITY_VIOLATION_DETECTED_EVENT.d.ts +1 -1
  141. package/dist/modules/system/events/SECURITY_VIOLATION_DETECTED_EVENT.d.ts.map +1 -1
  142. package/dist/modules/system/events/SECURITY_VIOLATION_DETECTED_EVENT.js +1 -1
  143. package/dist/modules/system/events/SECURITY_VIOLATION_DETECTED_EVENT.js.map +1 -1
  144. package/dist/node-env.vo.d.ts +11 -9
  145. package/dist/node-env.vo.d.ts.map +1 -1
  146. package/dist/node-env.vo.js +10 -3
  147. package/dist/node-env.vo.js.map +1 -1
  148. package/dist/nonce-value.vo.d.ts +1 -1
  149. package/dist/nonce-value.vo.d.ts.map +1 -1
  150. package/dist/nonce-value.vo.js +1 -1
  151. package/dist/nonce-value.vo.js.map +1 -1
  152. package/dist/port.vo.d.ts +1 -1
  153. package/dist/port.vo.d.ts.map +1 -1
  154. package/dist/port.vo.js +1 -1
  155. package/dist/port.vo.js.map +1 -1
  156. package/dist/prerequisite-verifier-timezone-utc.adapter.d.ts +1 -1
  157. package/dist/prerequisite-verifier-timezone-utc.adapter.d.ts.map +1 -1
  158. package/dist/prerequisite-verifier-timezone-utc.adapter.js +1 -1
  159. package/dist/prerequisite-verifier-timezone-utc.adapter.js.map +1 -1
  160. package/dist/recaptcha-secret-key.vo.d.ts +1 -1
  161. package/dist/recaptcha-secret-key.vo.d.ts.map +1 -1
  162. package/dist/recaptcha-secret-key.vo.js +1 -1
  163. package/dist/recaptcha-secret-key.vo.js.map +1 -1
  164. package/dist/recaptcha-site-key.vo.d.ts +1 -1
  165. package/dist/recaptcha-site-key.vo.d.ts.map +1 -1
  166. package/dist/recaptcha-site-key.vo.js +1 -1
  167. package/dist/recaptcha-site-key.vo.js.map +1 -1
  168. package/dist/security-countermeasure-name.vo.d.ts +1 -1
  169. package/dist/security-countermeasure-name.vo.d.ts.map +1 -1
  170. package/dist/security-countermeasure-name.vo.js +1 -1
  171. package/dist/security-countermeasure-name.vo.js.map +1 -1
  172. package/dist/security-rule-name.vo.d.ts +1 -1
  173. package/dist/security-rule-name.vo.d.ts.map +1 -1
  174. package/dist/security-rule-name.vo.js +1 -1
  175. package/dist/security-rule-name.vo.js.map +1 -1
  176. package/dist/smtp-host.vo.d.ts +1 -1
  177. package/dist/smtp-host.vo.d.ts.map +1 -1
  178. package/dist/smtp-host.vo.js +1 -1
  179. package/dist/smtp-host.vo.js.map +1 -1
  180. package/dist/smtp-pass.vo.d.ts +1 -1
  181. package/dist/smtp-pass.vo.d.ts.map +1 -1
  182. package/dist/smtp-pass.vo.js +1 -1
  183. package/dist/smtp-pass.vo.js.map +1 -1
  184. package/dist/smtp-port.vo.d.ts +1 -1
  185. package/dist/smtp-port.vo.d.ts.map +1 -1
  186. package/dist/smtp-user.vo.d.ts +1 -1
  187. package/dist/smtp-user.vo.d.ts.map +1 -1
  188. package/dist/smtp-user.vo.js +1 -1
  189. package/dist/smtp-user.vo.js.map +1 -1
  190. package/dist/tsconfig.tsbuildinfo +1 -1
  191. package/dist/uuid.vo.d.ts +1 -1
  192. package/dist/uuid.vo.d.ts.map +1 -1
  193. package/dist/uuid.vo.js +1 -1
  194. package/dist/uuid.vo.js.map +1 -1
  195. package/package.json +4 -3
  196. package/src/antivirus-clamav.adapter.ts +34 -0
  197. package/src/antivirus-noop.adapter.ts +8 -0
  198. package/src/antivirus.port.ts +9 -0
  199. package/src/api-version.middleware.ts +31 -0
  200. package/src/basic-auth-password.vo.ts +17 -0
  201. package/src/basic-auth-username.vo.ts +17 -0
  202. package/src/basic-auth.service.ts +14 -0
  203. package/src/better-auth-logger.service.ts +45 -0
  204. package/src/binary.vo.ts +22 -0
  205. package/src/bots.vo.ts +132 -0
  206. package/src/build-info-repository-file.strategy.ts +23 -0
  207. package/src/build-info-repository-noop.strategy.ts +16 -0
  208. package/src/build-info-repository-package-json.strategy.ts +25 -0
  209. package/src/build-info-repository.strategy.ts +13 -0
  210. package/src/cache-file.service.ts +37 -0
  211. package/src/cache-repository-lru-cache.adapter.ts +58 -0
  212. package/src/cache-repository-node-cache.adapter.ts +32 -0
  213. package/src/cache-repository-noop.adapter.ts +15 -0
  214. package/src/cache-repository.port.ts +11 -0
  215. package/src/cache-resolver-simple.strategy.ts +33 -0
  216. package/src/cache-resolver.strategy.ts +17 -0
  217. package/src/cache-response.middleware.ts +51 -0
  218. package/src/cache-subject-application-resolver.vo.ts +34 -0
  219. package/src/cache-subject-request-resolver.vo.ts +37 -0
  220. package/src/cache-subject-segment-application.strategy.ts +7 -0
  221. package/src/cache-subject-segment-build.strategy.ts +14 -0
  222. package/src/cache-subject-segment-cookie.strategy.ts +14 -0
  223. package/src/cache-subject-segment-env.strategy.ts +10 -0
  224. package/src/cache-subject-segment-fixed.strategy.ts +12 -0
  225. package/src/cache-subject-segment-header.strategy.ts +13 -0
  226. package/src/cache-subject-segment-ip.strategy.ts +9 -0
  227. package/src/cache-subject-segment-path.strategy.ts +8 -0
  228. package/src/cache-subject-segment-query.strategy.ts +18 -0
  229. package/src/cache-subject-segment-request.strategy.ts +9 -0
  230. package/src/cache-subject-segment-user.strategy.ts +8 -0
  231. package/src/certificate-inspector-noop.adapter.ts +10 -0
  232. package/src/certificate-inspector-tls.adapter.ts +42 -0
  233. package/src/certificate-inspector.port.ts +7 -0
  234. package/src/checksum.service.ts +12 -0
  235. package/src/client-from-hono.adapter.ts +20 -0
  236. package/src/client-ip.vo.ts +17 -0
  237. package/src/client-user-agent.vo.ts +17 -0
  238. package/src/client.vo.ts +52 -0
  239. package/src/clock-fixed.adapter.ts +14 -0
  240. package/src/clock-offset.adapter.ts +15 -0
  241. package/src/clock-system.adapter.ts +8 -0
  242. package/src/clock.port.ts +5 -0
  243. package/src/command-envelope.ts +16 -0
  244. package/src/command-logger.service.ts +23 -0
  245. package/src/command.types.ts +13 -0
  246. package/src/commit-sha-value.vo.ts +18 -0
  247. package/src/commit-sha.vo.ts +29 -0
  248. package/src/context.middleware.ts +24 -0
  249. package/src/correlation-id.vo.ts +6 -0
  250. package/src/correlation-storage.service.ts +31 -0
  251. package/src/crypto-aes-gcm.service.ts +37 -0
  252. package/src/crypto-key-provider-file.adapter.ts +29 -0
  253. package/src/crypto-key-provider-memory.adapter.ts +17 -0
  254. package/src/crypto-key-provider-noop.adapter.ts +9 -0
  255. package/src/crypto-key-provider-with-cache.adapter.ts +28 -0
  256. package/src/crypto-key-provider.port.ts +3 -0
  257. package/src/csv-stringifier.adapter.ts +8 -0
  258. package/src/csv-stringifier.port.ts +8 -0
  259. package/src/disk-space-checker-bun.adapter.ts +18 -0
  260. package/src/disk-space-checker-noop.adapter.ts +10 -0
  261. package/src/disk-space-checker.port.ts +5 -0
  262. package/src/dispatching-event-store.ts +36 -0
  263. package/src/encryption-aes-gcm.adapter.ts +57 -0
  264. package/src/encryption-iv.vo.ts +9 -0
  265. package/src/encryption-key-value.vo.ts +18 -0
  266. package/src/encryption-key.vo.ts +41 -0
  267. package/src/encryption-noop.adapter.ts +18 -0
  268. package/src/encryption.port.ts +12 -0
  269. package/src/environment-file-parser.service.ts +35 -0
  270. package/src/environment-loader-encrypted.adapter.ts +31 -0
  271. package/src/environment-loader-noop.adapter.ts +16 -0
  272. package/src/environment-loader-process-safe.adapter.ts +35 -0
  273. package/src/environment-loader-process.adapter.ts +16 -0
  274. package/src/environment-loader.port.ts +8 -0
  275. package/src/etag-extractor.middleware.ts +18 -0
  276. package/src/event-bus-like.types.ts +10 -0
  277. package/src/event-envelope.ts +27 -0
  278. package/src/event-handler-bare.strategy.ts +9 -0
  279. package/src/event-handler-noop.strategy.ts +9 -0
  280. package/src/event-handler-with-logger.strategy.ts +31 -0
  281. package/src/event-handler.strategy.ts +8 -0
  282. package/src/event-logger.service.ts +23 -0
  283. package/src/event-loop-lag.service.ts +32 -0
  284. package/src/event-loop-utilization.service.ts +21 -0
  285. package/src/event-publisher.types.ts +3 -0
  286. package/src/event-store-like.types.ts +3 -0
  287. package/src/event-store.ts +64 -0
  288. package/src/event-stream.vo.ts +14 -0
  289. package/src/event.types.ts +16 -0
  290. package/src/file-cleaner-bun-forgiving.adapter.ts +10 -0
  291. package/src/file-cleaner-bun.adapter.ts +8 -0
  292. package/src/file-cleaner-noop.adapter.ts +6 -0
  293. package/src/file-cleaner.port.ts +5 -0
  294. package/src/file-draft-zip.service.ts +34 -0
  295. package/src/file-draft.service.ts +33 -0
  296. package/src/file-reader-json-bun-forgiving.adapter.ts +14 -0
  297. package/src/file-reader-json-bun.adapter.ts +10 -0
  298. package/src/file-reader-json-noop.adapter.ts +12 -0
  299. package/src/file-reader-json-with-cache.adapter.ts +34 -0
  300. package/src/file-reader-json.port.ts +7 -0
  301. package/src/file-renamer-fs-forgiving.adapter.ts +17 -0
  302. package/src/file-renamer-fs.adapter.ts +15 -0
  303. package/src/file-renamer-noop.adapter.ts +9 -0
  304. package/src/file-renamer.port.ts +8 -0
  305. package/src/file-uploader.middleware.ts +39 -0
  306. package/src/graceful-shutdown.service.ts +68 -0
  307. package/src/gzip-bun.adapter.ts +12 -0
  308. package/src/gzip-noop.adapter.ts +7 -0
  309. package/src/gzip-stream.adapter.ts +19 -0
  310. package/src/gzip.port.ts +10 -0
  311. package/src/hash-content-noop.strategy.ts +8 -0
  312. package/src/hash-content-sha256-bun.strategy.ts +11 -0
  313. package/src/hash-content.strategy.ts +5 -0
  314. package/src/hash-file-noop.adapter.ts +14 -0
  315. package/src/hash-file-sha256-bun.adapter.ts +23 -0
  316. package/src/hash-file.port.ts +13 -0
  317. package/src/hash-value.vo.ts +15 -0
  318. package/src/hash.vo.ts +29 -0
  319. package/src/hcaptcha-secret-key.vo.ts +15 -0
  320. package/src/hcaptcha-site-key.vo.ts +12 -0
  321. package/src/healthcheck.service.ts +138 -0
  322. package/src/http-logger.middleware.ts +100 -0
  323. package/src/i18n.service.ts +62 -0
  324. package/src/id-provider-crypto.adapter.ts +8 -0
  325. package/src/id-provider-deterministic.adapter.ts +21 -0
  326. package/src/id-provider.port.ts +5 -0
  327. package/src/image-alpha-noop.adapter.ts +7 -0
  328. package/src/image-alpha-sharp.adapter.ts +38 -0
  329. package/src/image-alpha.port.ts +19 -0
  330. package/src/image-blur-noop.adapter.ts +7 -0
  331. package/src/image-blur-sharp.adapter.ts +33 -0
  332. package/src/image-blur.port.ts +18 -0
  333. package/src/image-compressor-noop.adapter.ts +7 -0
  334. package/src/image-compressor-sharp.adapter.ts +36 -0
  335. package/src/image-compressor.port.ts +18 -0
  336. package/src/image-exif-clear-noop.adapter.ts +7 -0
  337. package/src/image-exif-clear-sharp.adapter.ts +30 -0
  338. package/src/image-exif-clear.port.ts +16 -0
  339. package/src/image-formatter-noop.adapter.ts +10 -0
  340. package/src/image-formatter-sharp.adapter.ts +40 -0
  341. package/src/image-formatter.port.ts +17 -0
  342. package/src/image-info-noop.adapter.ts +15 -0
  343. package/src/image-info-sharp.adapter.ts +27 -0
  344. package/src/image-info.port.ts +12 -0
  345. package/src/image-processor-noop.adapter.ts +10 -0
  346. package/src/image-processor-sharp.adapter.ts +60 -0
  347. package/src/image-processor.port.ts +25 -0
  348. package/src/image-resizer-noop.adapter.ts +7 -0
  349. package/src/image-resizer-sharp.adapter.ts +41 -0
  350. package/src/image-resizer.port.ts +18 -0
  351. package/src/in-flight-requests-tracker.service.ts +22 -0
  352. package/src/in-flight-requests.middleware.ts +15 -0
  353. package/src/index.ts +307 -0
  354. package/src/instrumentation.service.ts +39 -0
  355. package/src/invariant-error-handler.service.ts +22 -0
  356. package/src/invariant.service.ts +27 -0
  357. package/src/job-handler-bare.strategy.ts +15 -0
  358. package/src/job-handler-noop.strategy.ts +9 -0
  359. package/src/job-handler-with-logger.strategy.ts +44 -0
  360. package/src/job-handler.strategy.ts +10 -0
  361. package/src/jobs.service.ts +16 -0
  362. package/src/logger-format-error.service.ts +27 -0
  363. package/src/logger-noop.adapter.ts +17 -0
  364. package/src/logger-winston-local.adapter.ts +24 -0
  365. package/src/logger-winston-production.adapter.ts +38 -0
  366. package/src/logger-winston.adapter.ts +63 -0
  367. package/src/logger.port.ts +64 -0
  368. package/src/mailer-content-html.vo.ts +11 -0
  369. package/src/mailer-noop.adapter.ts +21 -0
  370. package/src/mailer-smtp-with-logger.adapter.ts +33 -0
  371. package/src/mailer-smtp.adapter.ts +33 -0
  372. package/src/mailer-subject.vo.ts +10 -0
  373. package/src/mailer.port.ts +6 -0
  374. package/src/maintenance-mode.middleware.ts +15 -0
  375. package/src/memory-consumption.service.ts +23 -0
  376. package/src/modules/history/event-handlers/index.ts +2 -0
  377. package/src/modules/history/event-handlers/onHistoryClearedEvent.ts +6 -0
  378. package/src/modules/history/event-handlers/onHistoryPopulatedEvent.ts +10 -0
  379. package/src/modules/history/events/HISTORY_CLEARED_EVENT.ts +13 -0
  380. package/src/modules/history/events/HISTORY_POPULATED_EVENT.ts +17 -0
  381. package/src/modules/history/events/index.ts +2 -0
  382. package/src/modules/history/index.ts +4 -0
  383. package/src/modules/history/ports/history-projection.ts +7 -0
  384. package/src/modules/history/ports/history-reader.ts +5 -0
  385. package/src/modules/history/ports/history-writer.ts +7 -0
  386. package/src/modules/history/ports/index.ts +3 -0
  387. package/src/modules/history/value-objects/history-created-at.ts +6 -0
  388. package/src/modules/history/value-objects/history-id.ts +3 -0
  389. package/src/modules/history/value-objects/history-operation.ts +12 -0
  390. package/src/modules/history/value-objects/history-payload.ts +15 -0
  391. package/src/modules/history/value-objects/history-subject.ts +14 -0
  392. package/src/modules/history/value-objects/history.ts +26 -0
  393. package/src/modules/history/value-objects/index.ts +6 -0
  394. package/src/modules/preferences/command-handlers/handleSetUserLanguageCommand.ts +36 -0
  395. package/src/modules/preferences/command-handlers/index.ts +1 -0
  396. package/src/modules/preferences/commands/SET_USER_LANGUAGE_COMMAND.ts +14 -0
  397. package/src/modules/preferences/commands/index.ts +1 -0
  398. package/src/modules/preferences/events/USER_LANGUAGE_SET_EVENT.ts +14 -0
  399. package/src/modules/preferences/events/index.ts +1 -0
  400. package/src/modules/preferences/index.ts +7 -0
  401. package/src/modules/preferences/invariants/index.ts +1 -0
  402. package/src/modules/preferences/invariants/user-language-has-changed.ts +26 -0
  403. package/src/modules/preferences/open-host-queries/index.ts +1 -0
  404. package/src/modules/preferences/open-host-queries/user-language.ts +23 -0
  405. package/src/modules/preferences/ports/index.ts +2 -0
  406. package/src/modules/preferences/ports/user-language-query.ts +5 -0
  407. package/src/modules/preferences/ports/user-language-resolver.ts +22 -0
  408. package/src/modules/preferences/value-objects/index.ts +1 -0
  409. package/src/modules/preferences/value-objects/supported-languages-set.ts +17 -0
  410. package/src/modules/system/events/HOUR_HAS_PASSED_EVENT.ts +13 -0
  411. package/src/modules/system/events/MINUTE_HAS_PASSED_EVENT.ts +13 -0
  412. package/src/modules/system/events/SECURITY_VIOLATION_DETECTED_EVENT.ts +23 -0
  413. package/src/modules/system/events/index.ts +3 -0
  414. package/src/modules/system/index.ts +2 -0
  415. package/src/modules/system/services/index.ts +2 -0
  416. package/src/modules/system/services/passage-of-time-hourly.service.ts +35 -0
  417. package/src/modules/system/services/passage-of-time-minute.service.ts +35 -0
  418. package/src/node-env.vo.ts +12 -0
  419. package/src/nonce-provider-crypto.adapter.ts +18 -0
  420. package/src/nonce-provider-deterministic.adapter.ts +21 -0
  421. package/src/nonce-provider-noop.adapter.ts +8 -0
  422. package/src/nonce-provider.port.ts +5 -0
  423. package/src/nonce-value.vo.ts +18 -0
  424. package/src/pdf-generator-noop.adapter.ts +23 -0
  425. package/src/pdf-generator.port.ts +5 -0
  426. package/src/ping.service.ts +7 -0
  427. package/src/port.vo.ts +14 -0
  428. package/src/prerequisite-runner-startup.service.ts +44 -0
  429. package/src/prerequisite-verifier-binary.adapter.ts +21 -0
  430. package/src/prerequisite-verifier-bun.adapter.ts +21 -0
  431. package/src/prerequisite-verifier-clock-drift.adapter.ts +32 -0
  432. package/src/prerequisite-verifier-directory.adapter.ts +48 -0
  433. package/src/prerequisite-verifier-dns.adapter.ts +20 -0
  434. package/src/prerequisite-verifier-external-api.adapter.ts +20 -0
  435. package/src/prerequisite-verifier-file.adapter.ts +49 -0
  436. package/src/prerequisite-verifier-jobs.adapter.ts +15 -0
  437. package/src/prerequisite-verifier-log-file.adapter.ts +29 -0
  438. package/src/prerequisite-verifier-mailer.adapter.ts +22 -0
  439. package/src/prerequisite-verifier-memory.adapter.ts +22 -0
  440. package/src/prerequisite-verifier-node.adapter.ts +21 -0
  441. package/src/prerequisite-verifier-os.adapter.ts +22 -0
  442. package/src/prerequisite-verifier-outside-connectivity.adapter.ts +23 -0
  443. package/src/prerequisite-verifier-port.adapter.ts +23 -0
  444. package/src/prerequisite-verifier-ram.adapter.ts +20 -0
  445. package/src/prerequisite-verifier-running-user.adapter.ts +17 -0
  446. package/src/prerequisite-verifier-self.adapter.ts +11 -0
  447. package/src/prerequisite-verifier-space.adapter.ts +32 -0
  448. package/src/prerequisite-verifier-sqlite.adapter.ts +25 -0
  449. package/src/prerequisite-verifier-ssl-certificate-expiry.adapter.ts +28 -0
  450. package/src/prerequisite-verifier-timezone-utc.adapter.ts +20 -0
  451. package/src/prerequisite-verifier-translations.adapter.ts +65 -0
  452. package/src/prerequisite-verifier-with-cache.adapter.ts +35 -0
  453. package/src/prerequisite-verifier-with-fail-safe.adapter.ts +37 -0
  454. package/src/prerequisite-verifier-with-logger.adapter.ts +51 -0
  455. package/src/prerequisite-verifier-with-retry.adapter.ts +35 -0
  456. package/src/prerequisite-verifier-with-timeout.adapter.ts +31 -0
  457. package/src/prerequisite-verifier.decorator.ts +49 -0
  458. package/src/prerequisite-verifier.port.ts +38 -0
  459. package/src/prerequisite.vo.ts +28 -0
  460. package/src/recaptcha-secret-key.vo.ts +15 -0
  461. package/src/recaptcha-site-key.vo.ts +12 -0
  462. package/src/redactor-compact-array.strategy.ts +10 -0
  463. package/src/redactor-compact-object.strategy.ts +25 -0
  464. package/src/redactor-composite.strategy.ts +9 -0
  465. package/src/redactor-mask.strategy.ts +38 -0
  466. package/src/redactor-noop.strategy.ts +7 -0
  467. package/src/redactor.strategy.ts +3 -0
  468. package/src/remote-file-storage-disk.adapter.ts +72 -0
  469. package/src/remote-file-storage-noop.adapter.ts +70 -0
  470. package/src/remote-file-storage.port.ts +17 -0
  471. package/src/retry-backoff-exponential.strategy.ts +10 -0
  472. package/src/retry-backoff-fibonacci.strategy.ts +16 -0
  473. package/src/retry-backoff-linear.strategy.ts +10 -0
  474. package/src/retry-backoff-noop.strategy.ts +8 -0
  475. package/src/retry-backoff.strategy.ts +5 -0
  476. package/src/retry.service.ts +37 -0
  477. package/src/safe-parse-body.service.ts +12 -0
  478. package/src/sealer-aes-gcm.adapter.ts +38 -0
  479. package/src/sealer-noop.adapter.ts +11 -0
  480. package/src/sealer.port.ts +4 -0
  481. package/src/security-context.vo.ts +13 -0
  482. package/src/security-countermeasure-ban.strategy.ts +63 -0
  483. package/src/security-countermeasure-mirage.strategy.ts +31 -0
  484. package/src/security-countermeasure-name.vo.ts +22 -0
  485. package/src/security-countermeasure-noop.strategy.ts +12 -0
  486. package/src/security-countermeasure-report.strategy.ts +27 -0
  487. package/src/security-countermeasure-tarpit.strategy.ts +31 -0
  488. package/src/security-countermeasure.strategy.ts +16 -0
  489. package/src/security-policy.vo.ts +9 -0
  490. package/src/security-rule-and.strategy.ts +25 -0
  491. package/src/security-rule-bait-routes.strategy.ts +15 -0
  492. package/src/security-rule-fail.strategy.ts +12 -0
  493. package/src/security-rule-honey-pot-field.strategy.ts +20 -0
  494. package/src/security-rule-name.vo.ts +22 -0
  495. package/src/security-rule-or.strategy.ts +25 -0
  496. package/src/security-rule-pass.strategy.ts +12 -0
  497. package/src/security-rule-user-agent.strategy.ts +20 -0
  498. package/src/security-rule-violation-threshold.strategy.ts +50 -0
  499. package/src/security-rule.strategy.ts +8 -0
  500. package/src/setup.service.ts +102 -0
  501. package/src/shield-api-key.strategy.ts +20 -0
  502. package/src/shield-auth.strategy.ts +38 -0
  503. package/src/shield-basic-auth.strategy.ts +17 -0
  504. package/src/shield-csrf.strategy.ts +23 -0
  505. package/src/shield-hcaptcha-local.strategy.ts +22 -0
  506. package/src/shield-hcaptcha.strategy.ts +24 -0
  507. package/src/shield-noop.strategy.ts +6 -0
  508. package/src/shield-rate-limit.strategy.ts +41 -0
  509. package/src/shield-recaptcha.strategy.ts +49 -0
  510. package/src/shield-security.strategy.ts +80 -0
  511. package/src/shield-timeout.strategy.ts +19 -0
  512. package/src/shield.strategy.ts +5 -0
  513. package/src/simulated-error.middleware.ts +8 -0
  514. package/src/sleeper-noop.adapter.ts +5 -0
  515. package/src/sleeper-system.adapter.ts +8 -0
  516. package/src/sleeper.port.ts +5 -0
  517. package/src/slower.middleware.ts +14 -0
  518. package/src/smtp-host.vo.ts +17 -0
  519. package/src/smtp-pass.vo.ts +17 -0
  520. package/src/smtp-port.vo.ts +5 -0
  521. package/src/smtp-user.vo.ts +17 -0
  522. package/src/ssr.ts +57 -0
  523. package/src/static-files.service.ts +85 -0
  524. package/src/stopwatch.service.ts +30 -0
  525. package/src/temporary-file-absolute.adapter.ts +31 -0
  526. package/src/temporary-file-noop.adapter.ts +16 -0
  527. package/src/temporary-file.port.ts +9 -0
  528. package/src/time-zone-offset.middleware.ts +24 -0
  529. package/src/timekeeper-google.adapter.ts +18 -0
  530. package/src/timekeeper-noop.adapter.ts +12 -0
  531. package/src/timekeeper.port.ts +5 -0
  532. package/src/timeout-cancellable-runner-bare.adapter.ts +35 -0
  533. package/src/timeout-cancellable-runner-noop.adapter.ts +9 -0
  534. package/src/timeout-cancellable-runner.port.ts +7 -0
  535. package/src/timeout-runner-bare.adapter.ts +23 -0
  536. package/src/timeout-runner-error.adapter.ts +8 -0
  537. package/src/timeout-runner-monitor.adapter.ts +24 -0
  538. package/src/timeout-runner-noop.adapter.ts +8 -0
  539. package/src/timeout-runner.port.ts +7 -0
  540. package/src/to-event-map.types.ts +3 -0
  541. package/src/translations.service.ts +20 -0
  542. package/src/uptime.service.ts +17 -0
  543. package/src/uuid.vo.ts +7 -0
  544. package/src/visitor-id-client.strategy.ts +20 -0
  545. package/src/visitor-id.strategy.ts +5 -0
  546. package/src/weak-etag-extractor.middleware.ts +17 -0
@@ -0,0 +1,5 @@
1
+ import type { Hash } from "./hash.vo";
2
+
3
+ export interface HashContentStrategy {
4
+ hash(content: string): Promise<Hash>;
5
+ }
@@ -0,0 +1,14 @@
1
+ import * as tools from "@bgord/tools";
2
+ import { Hash } from "./hash.vo";
3
+ import type { HashFilePort } from "./hash-file.port";
4
+
5
+ export class HashFileNoopAdapter implements HashFilePort {
6
+ async hash(_path: tools.FilePathAbsolute | tools.FilePathRelative) {
7
+ return {
8
+ etag: Hash.fromString("0000000000000000000000000000000000000000000000000000000000000000"),
9
+ size: tools.Size.fromBytes(10),
10
+ lastModified: tools.Timestamp.fromNumber(1000),
11
+ mime: tools.MIMES.text,
12
+ };
13
+ }
14
+ }
@@ -0,0 +1,23 @@
1
+ import * as tools from "@bgord/tools";
2
+ import type { HashContentStrategy } from "./hash-content.strategy";
3
+ import type { HashFilePort } from "./hash-file.port";
4
+
5
+ type Dependencies = { HashContent: HashContentStrategy };
6
+
7
+ export class HashFileSha256BunAdapter implements HashFilePort {
8
+ constructor(private readonly deps: Dependencies) {}
9
+
10
+ async hash(path: tools.FilePathAbsolute | tools.FilePathRelative) {
11
+ const file = Bun.file(path.get());
12
+ const extension = path.getFilename().getExtension();
13
+
14
+ const arrayBuffer = await file.arrayBuffer();
15
+
16
+ return {
17
+ etag: await this.deps.HashContent.hash(await file.text()),
18
+ size: tools.Size.fromBytes(arrayBuffer.byteLength),
19
+ lastModified: tools.Timestamp.fromNumber(file.lastModified),
20
+ mime: tools.Mime.fromExtension(extension),
21
+ };
22
+ }
23
+ }
@@ -0,0 +1,13 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { Hash } from "./hash.vo";
3
+
4
+ export type HashFileResult = {
5
+ etag: Hash;
6
+ size: tools.Size;
7
+ lastModified: tools.Timestamp;
8
+ mime: tools.Mime;
9
+ };
10
+
11
+ export interface HashFilePort {
12
+ hash(path: tools.FilePathAbsolute | tools.FilePathRelative): Promise<HashFileResult>;
13
+ }
@@ -0,0 +1,15 @@
1
+ import * as z from "zod/v4";
2
+
3
+ export const HashValueError = { Type: "hash.value.type", InvalidHex: "hash.value.invalid.hex" } as const;
4
+
5
+ // 64 hex chars allowed
6
+ const CHARS_WHITELIST = /^[a-fA-F0-9]{64}$/;
7
+
8
+ // Stryker disable all
9
+ export const HashValue = z
10
+ // Stryker restore all
11
+ .string(HashValueError.Type)
12
+ .regex(CHARS_WHITELIST, HashValueError.InvalidHex)
13
+ .brand("HashValue");
14
+
15
+ export type HashValueType = z.infer<typeof HashValue>;
package/src/hash.vo.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { HashValue, type HashValueType } from "./hash-value.vo";
2
+
3
+ export class Hash {
4
+ private constructor(private readonly value: HashValueType) {}
5
+
6
+ static fromValue(value: HashValueType): Hash {
7
+ return new Hash(value);
8
+ }
9
+
10
+ static fromString(candidate: string): Hash {
11
+ return new Hash(HashValue.parse(candidate));
12
+ }
13
+
14
+ get(): HashValueType {
15
+ return this.value;
16
+ }
17
+
18
+ matches(another: Hash): boolean {
19
+ return this.value === another.value;
20
+ }
21
+
22
+ toString(): string {
23
+ return this.value;
24
+ }
25
+
26
+ toJSON(): string {
27
+ return this.value;
28
+ }
29
+ }
@@ -0,0 +1,15 @@
1
+ import * as z from "zod/v4";
2
+
3
+ export const HCaptchaSecretKeyError = {
4
+ Type: "hcaptcha.secret.key.type",
5
+ Length: "hcaptcha.secret.key.length",
6
+ };
7
+
8
+ // Stryker disable all
9
+ export const HCaptchaSecretKey = z
10
+ // Stryker restore all
11
+ .string(HCaptchaSecretKeyError.Type)
12
+ .length(35, HCaptchaSecretKeyError.Length)
13
+ .brand("HCaptchaSecretKey");
14
+
15
+ export type HCaptchaSecretKeyType = z.infer<typeof HCaptchaSecretKey>;
@@ -0,0 +1,12 @@
1
+ import * as z from "zod/v4";
2
+
3
+ export const HCaptchaSiteKeyError = { Type: "hcaptcha.site.key.type", Length: "hcaptcha.site.key.length" };
4
+
5
+ // Stryker disable all
6
+ export const HCaptchaSiteKey = z
7
+ // Stryker restore all
8
+ .string(HCaptchaSiteKeyError.Type)
9
+ .length(36, HCaptchaSiteKeyError.Length)
10
+ .brand("HCaptchaSiteKey");
11
+
12
+ export type HCaptchaSiteKeyType = z.infer<typeof HCaptchaSiteKey>;
@@ -0,0 +1,138 @@
1
+ import os from "node:os";
2
+ import * as tools from "@bgord/tools";
3
+ import { createFactory } from "hono/factory";
4
+ import type { BuildInfoRepositoryStrategy } from "./build-info-repository.strategy";
5
+ import type { ClockPort } from "./clock.port";
6
+ import type { CommitShaValueType } from "./commit-sha-value.vo";
7
+ import { EventLoopLag } from "./event-loop-lag.service";
8
+ import { EventLoopUtilization, type EventLoopUtilizationSnapshot } from "./event-loop-utilization.service";
9
+ import { InFlightRequestsTracker } from "./in-flight-requests-tracker.service";
10
+ import { MemoryConsumption } from "./memory-consumption.service";
11
+ import type { NodeEnvironmentEnum } from "./node-env.vo";
12
+ import { Prerequisite, type PrerequisiteLabelType } from "./prerequisite.vo";
13
+ import {
14
+ PrerequisiteVerificationOutcome,
15
+ type PrerequisiteVerificationResult,
16
+ } from "./prerequisite-verifier.port";
17
+ import { PrerequisiteVerifierSelfAdapter } from "./prerequisite-verifier-self.adapter";
18
+ import { Stopwatch } from "./stopwatch.service";
19
+ import { Uptime, type UptimeResultType } from "./uptime.service";
20
+
21
+ const handler = createFactory();
22
+ const self = new Prerequisite("self", new PrerequisiteVerifierSelfAdapter());
23
+
24
+ type HealthcheckResultType = {
25
+ ok: boolean;
26
+ deployment: {
27
+ version: string;
28
+ timestamp: tools.TimestampValueType;
29
+ date: string;
30
+ sha: CommitShaValueType;
31
+ size: string;
32
+ environment: NodeEnvironmentEnum;
33
+ };
34
+ server: {
35
+ pid: typeof process.pid;
36
+ hostname: ReturnType<typeof os.hostname>;
37
+ cpus: number;
38
+ startup: tools.TimestampValueType;
39
+ uptime: Omit<UptimeResultType, "duration"> & { durationMs: tools.DurationMsType };
40
+ memory: {
41
+ total: { bytes: tools.Size["bytes"]; formatted: ReturnType<tools.Size["format"]> };
42
+ heap: {
43
+ used: { bytes: tools.Size["bytes"]; formatted: ReturnType<tools.Size["format"]> };
44
+ total: { bytes: tools.Size["bytes"]; formatted: ReturnType<tools.Size["format"]> };
45
+ };
46
+ };
47
+ eventLoop: {
48
+ lag: { p50: tools.DurationMsType; p95: tools.DurationMsType; p99: tools.DurationMsType };
49
+ utilization: EventLoopUtilizationSnapshot;
50
+ };
51
+ inFlight: tools.IntegerType;
52
+ };
53
+ details: {
54
+ label: PrerequisiteLabelType;
55
+ outcome: PrerequisiteVerificationResult;
56
+ durationMs: tools.DurationMsType;
57
+ }[];
58
+ durationMs: tools.Duration["ms"];
59
+ timestamp: tools.TimestampValueType;
60
+ };
61
+
62
+ type Dependencies = { Clock: ClockPort; BuildInfoRepository: BuildInfoRepositoryStrategy };
63
+
64
+ export class Healthcheck {
65
+ static build = (Env: NodeEnvironmentEnum, _prerequisites: Prerequisite[], deps: Dependencies) =>
66
+ handler.createHandlers(async (c) => {
67
+ const stopwatch = new Stopwatch(deps);
68
+
69
+ const prerequisites = [self, ..._prerequisites]
70
+ .filter((prerequisite) => prerequisite.enabled)
71
+ .filter((prerequisite) => prerequisite.kind !== "port");
72
+
73
+ const details: HealthcheckResultType["details"][number][] = await Promise.all(
74
+ prerequisites.map(async (prerequisite) => {
75
+ const stopwatch = new Stopwatch(deps);
76
+
77
+ return {
78
+ label: prerequisite.label,
79
+ outcome: await prerequisite.build().verify(),
80
+ durationMs: stopwatch.stop().ms,
81
+ };
82
+ }),
83
+ );
84
+
85
+ const ok = details.every(
86
+ (result) => result.outcome.outcome !== PrerequisiteVerificationOutcome.failure,
87
+ );
88
+
89
+ const code = ok ? 200 : 424;
90
+
91
+ const build = await deps.BuildInfoRepository.extract();
92
+ const uptime = Uptime.get(deps.Clock);
93
+ const histogram = EventLoopLag.snapshot();
94
+ const memory = MemoryConsumption.snapshot();
95
+
96
+ const response: HealthcheckResultType = {
97
+ ok,
98
+ details,
99
+ deployment: {
100
+ version: build.version.toString(),
101
+ timestamp: build.timestamp.ms,
102
+ date: new Date(build.timestamp.ms).toISOString(),
103
+ sha: build.sha.toString(),
104
+ size: build.size.format(tools.Size.unit.MB),
105
+ environment: Env,
106
+ },
107
+ server: {
108
+ pid: process.pid,
109
+ hostname: os.hostname(),
110
+ cpus: os.cpus().length,
111
+ startup: deps.Clock.now().subtract(uptime.duration).ms,
112
+ uptime: { durationMs: uptime.duration.ms, formatted: uptime.formatted },
113
+ memory: {
114
+ total: { bytes: memory.total.toBytes(), formatted: memory.total.format(tools.Size.unit.MB) },
115
+ heap: {
116
+ used: {
117
+ bytes: memory.heap.used.toBytes(),
118
+ formatted: memory.heap.used.format(tools.Size.unit.MB),
119
+ },
120
+ total: {
121
+ bytes: memory.heap.total.toBytes(),
122
+ formatted: memory.heap.total.format(tools.Size.unit.MB),
123
+ },
124
+ },
125
+ },
126
+ eventLoop: {
127
+ lag: { p50: histogram.p50.ms, p95: histogram.p95.ms, p99: histogram.p99.ms },
128
+ utilization: EventLoopUtilization.snapshot(),
129
+ },
130
+ inFlight: InFlightRequestsTracker.get(),
131
+ },
132
+ durationMs: stopwatch.stop().ms,
133
+ timestamp: deps.Clock.now().ms,
134
+ };
135
+
136
+ return c.json(response, code);
137
+ });
138
+ }
@@ -0,0 +1,100 @@
1
+ import { createMiddleware } from "hono/factory";
2
+ import { CacheSourceEnum } from "./cache-resolver.strategy";
3
+ import { CacheResponse } from "./cache-response.middleware";
4
+ import { ClientFromHono } from "./client-from-hono.adapter";
5
+ import type { ClockPort } from "./clock.port";
6
+ import type { LoggerPort } from "./logger.port";
7
+ import { Stopwatch } from "./stopwatch.service";
8
+
9
+ export const UNINFORMATIVE_HEADERS = [
10
+ "accept",
11
+ "accept-encoding",
12
+ "cache-control",
13
+ "connection",
14
+ "content-length",
15
+ "content-type",
16
+ "cookie",
17
+ "dnt",
18
+ "host",
19
+ "pragma",
20
+ "sec-fetch-dest",
21
+ "sec-fetch-mode",
22
+ "sec-fetch-site",
23
+ "sec-fetch-user",
24
+ "sec-gpc",
25
+ "upgrade-insecure-requests",
26
+ "user-agent",
27
+ "if-none-match",
28
+ "priority",
29
+ ];
30
+
31
+ type Dependencies = { Logger: LoggerPort; Clock: ClockPort };
32
+
33
+ export type HttpLoggerOptions = { skip?: readonly string[] };
34
+
35
+ export class HttpLogger {
36
+ static build = (deps: Dependencies, options?: HttpLoggerOptions) =>
37
+ createMiddleware(async (context, next) => {
38
+ const request = context.req.raw.clone();
39
+
40
+ const pathname = new URL(request.url).pathname;
41
+
42
+ // Stryker disable all
43
+ if (options?.skip?.some((prefix) => pathname.startsWith(prefix))) return await next();
44
+ // Stryker restore all
45
+
46
+ const correlationId = context.get("requestId");
47
+ const client = ClientFromHono.translate(context).toJSON();
48
+
49
+ const httpRequestBeforeMetadata = {
50
+ params: context.req.param(),
51
+ headers: Object.fromEntries(
52
+ Object.entries(request.headers.toJSON()).filter(
53
+ ([header]) => !UNINFORMATIVE_HEADERS.includes(header),
54
+ ),
55
+ ),
56
+ body: (await HttpLogger.parseJSON(request)) ?? {},
57
+ query: context.req.queries(),
58
+ };
59
+
60
+ deps.Logger.http({
61
+ component: "http",
62
+ operation: "http_request_before",
63
+ correlationId,
64
+ message: "request",
65
+ method: request.method,
66
+ url: request.url,
67
+ client,
68
+ metadata: httpRequestBeforeMetadata,
69
+ });
70
+
71
+ const stopwatch = new Stopwatch(deps);
72
+ await next();
73
+ const duration = stopwatch.stop();
74
+
75
+ const response = context.res.clone();
76
+
77
+ deps.Logger.http({
78
+ component: "http",
79
+ operation: "http_request_after",
80
+ correlationId,
81
+ message: "response",
82
+ method: request.method,
83
+ url: request.url,
84
+ status: response.status,
85
+ durationMs: duration.ms,
86
+ client,
87
+ cacheHit: response.headers.get(CacheResponse.CACHE_HIT_HEADER) === CacheSourceEnum.hit,
88
+ metadata: { response: await HttpLogger.parseJSON(response) },
89
+ });
90
+ });
91
+
92
+ private static async parseJSON(resource: Request | Response) {
93
+ let result: any;
94
+ try {
95
+ result = await resource.json();
96
+ } catch {}
97
+
98
+ return result;
99
+ }
100
+ }
@@ -0,0 +1,62 @@
1
+ import * as tools from "@bgord/tools";
2
+ import type { FileReaderJsonPort } from "../src/file-reader-json.port";
3
+ import type { LoggerPort } from "../src/logger.port";
4
+
5
+ export type TranslationsKeyType = string;
6
+ export type TranslationsValueType = string;
7
+ export type TranslationsType = Record<TranslationsKeyType, TranslationsValueType>;
8
+ export type TranslationPlaceholderType = string;
9
+ export type TranslationPlaceholderValueType = string | number;
10
+ export type TranslationVariableType = Record<TranslationPlaceholderType, TranslationPlaceholderValueType>;
11
+
12
+ export type TranslationsSupportedLanguagesType = Record<string, string>;
13
+
14
+ export type I18nConfigType = {
15
+ supportedLanguages: TranslationsSupportedLanguagesType;
16
+ translationsPath?: tools.DirectoryPathRelativeType;
17
+ defaultLanguage?: string;
18
+ };
19
+
20
+ type Dependencies = { FileReaderJson: FileReaderJsonPort; Logger: LoggerPort };
21
+
22
+ export class I18n {
23
+ private readonly base = { component: "infra", operation: "translations" };
24
+
25
+ static DEFAULT_TRANSLATIONS_PATH = tools.DirectoryPathRelativeSchema.parse("infra/translations");
26
+
27
+ constructor(
28
+ private readonly deps: Dependencies,
29
+ private translationsPath: tools.DirectoryPathRelativeType = I18n.DEFAULT_TRANSLATIONS_PATH,
30
+ ) {}
31
+
32
+ async getTranslations(language: tools.LanguageType): Promise<TranslationsType> {
33
+ return this.deps.FileReaderJson.read(this.getTranslationPathForLanguage(language));
34
+ }
35
+
36
+ useTranslations(translations: TranslationsType) {
37
+ const that = this;
38
+
39
+ return function translate(key: TranslationsKeyType, variables?: TranslationVariableType) {
40
+ const translation = translations[key];
41
+
42
+ if (!translation) {
43
+ that.deps.Logger.warn({ message: `Missing translation for key ${key}`, ...that.base });
44
+ return key;
45
+ }
46
+
47
+ if (!variables) return translation;
48
+
49
+ return Object.entries(variables).reduce(
50
+ (result, [placeholder, value]) => result.replace(`{{${placeholder}}}`, String(value)),
51
+ translation,
52
+ );
53
+ };
54
+ }
55
+
56
+ getTranslationPathForLanguage(language: tools.LanguageType): tools.FilePathRelative {
57
+ return tools.FilePathRelative.fromParts(
58
+ this.translationsPath,
59
+ tools.Filename.fromParts(language, "json"),
60
+ );
61
+ }
62
+ }
@@ -0,0 +1,8 @@
1
+ import type { IdProviderPort } from "./id-provider.port";
2
+ import type { UUIDType } from "./uuid.vo";
3
+
4
+ export class IdProviderCryptoAdapter implements IdProviderPort {
5
+ generate(): UUIDType {
6
+ return crypto.randomUUID();
7
+ }
8
+ }
@@ -0,0 +1,21 @@
1
+ import type { IdProviderPort } from "./id-provider.port";
2
+ import type { UUIDType } from "./uuid.vo";
3
+
4
+ export const IdProviderDeterministicAdapterError = {
5
+ SequenceExhausted: "id.provider.deterministic.adapter.sequence.exhausted",
6
+ } as const;
7
+
8
+ export class IdProviderDeterministicAdapter implements IdProviderPort {
9
+ private readonly queue: UUIDType[];
10
+
11
+ constructor(sequence: UUIDType[]) {
12
+ this.queue = [...sequence];
13
+ }
14
+
15
+ generate(): UUIDType {
16
+ const next = this.queue.shift();
17
+
18
+ if (!next) throw new Error(IdProviderDeterministicAdapterError.SequenceExhausted);
19
+ return next;
20
+ }
21
+ }
@@ -0,0 +1,5 @@
1
+ import type { UUIDType } from "./uuid.vo";
2
+
3
+ export interface IdProviderPort {
4
+ generate(): UUIDType;
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { ImageAlphaPort, ImageAlphaStrategy } from "./image-alpha.port";
2
+
3
+ export class ImageAlphaNoopAdapter implements ImageAlphaPort {
4
+ async flatten(recipe: ImageAlphaStrategy) {
5
+ return recipe.strategy === "output_path" ? recipe.output : recipe.input;
6
+ }
7
+ }
@@ -0,0 +1,38 @@
1
+ import type { FileRenamerPort } from "./file-renamer.port";
2
+ import type { ImageAlphaPort, ImageAlphaStrategy } from "./image-alpha.port";
3
+
4
+ type Dependencies = { FileRenamer: FileRenamerPort };
5
+
6
+ export class ImageAlphaSharpAdapter implements ImageAlphaPort {
7
+ constructor(private readonly deps: Dependencies) {}
8
+
9
+ private async load() {
10
+ const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
11
+ return (await import(name)).default;
12
+ }
13
+
14
+ async flatten(recipe: ImageAlphaStrategy) {
15
+ const sharp = await this.load();
16
+
17
+ const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
18
+
19
+ const filename = final.getFilename();
20
+ const temporary = final.withFilename(filename.withSuffix("-flattened"));
21
+
22
+ const extension = final.getFilename().getExtension();
23
+ const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
24
+
25
+ const pipeline = sharp(recipe.input.get());
26
+ using _pipeline_dispose = { [Symbol.dispose]: () => pipeline.destroy() };
27
+
28
+ await pipeline
29
+ .rotate()
30
+ .flatten({ background: recipe.background })
31
+ .toFormat(format)
32
+ .toFile(temporary.get());
33
+
34
+ await this.deps.FileRenamer.rename(temporary, final);
35
+
36
+ return final;
37
+ }
38
+ }
@@ -0,0 +1,19 @@
1
+ import type * as tools from "@bgord/tools";
2
+
3
+ type ImageAlphaBackground = string | { r: number; g: number; b: number; alpha?: number };
4
+ type ImageAlphaOutputPathStrategy = {
5
+ strategy: "output_path";
6
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
7
+ output: tools.FilePathRelative | tools.FilePathAbsolute;
8
+ background: ImageAlphaBackground;
9
+ };
10
+ type ImageAlphaInPlaceStrategy = {
11
+ strategy: "in_place";
12
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
13
+ background: ImageAlphaBackground;
14
+ };
15
+ export type ImageAlphaStrategy = ImageAlphaInPlaceStrategy | ImageAlphaOutputPathStrategy;
16
+
17
+ export interface ImageAlphaPort {
18
+ flatten(recipe: ImageAlphaStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute>;
19
+ }
@@ -0,0 +1,7 @@
1
+ import type { ImageBlurPort, ImageBlurStrategy } from "./image-blur.port";
2
+
3
+ export class ImageBlurNoopAdapter implements ImageBlurPort {
4
+ async blur(recipe: ImageBlurStrategy) {
5
+ return recipe.strategy === "output_path" ? recipe.output : recipe.input;
6
+ }
7
+ }
@@ -0,0 +1,33 @@
1
+ import type { FileRenamerPort } from "./file-renamer.port";
2
+ import type { ImageBlurPort, ImageBlurStrategy } from "./image-blur.port";
3
+
4
+ type Dependencies = { FileRenamer: FileRenamerPort };
5
+
6
+ export class ImageBlurSharpAdapter implements ImageBlurPort {
7
+ constructor(private readonly deps: Dependencies) {}
8
+
9
+ private async load() {
10
+ const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
11
+ return (await import(name)).default;
12
+ }
13
+
14
+ async blur(recipe: ImageBlurStrategy) {
15
+ const sharp = await this.load();
16
+
17
+ const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
18
+
19
+ const filename = final.getFilename();
20
+ const temporary = final.withFilename(filename.withSuffix("-blurred"));
21
+
22
+ const extension = final.getFilename().getExtension();
23
+ const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
24
+
25
+ const pipeline = sharp(recipe.input.get());
26
+ using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
27
+
28
+ await pipeline.rotate().blur(recipe.sigma).toFormat(format).toFile(temporary.get());
29
+ await this.deps.FileRenamer.rename(temporary, final);
30
+
31
+ return final;
32
+ }
33
+ }
@@ -0,0 +1,18 @@
1
+ import type * as tools from "@bgord/tools";
2
+
3
+ type ImageBlurOutputPathStrategy = {
4
+ strategy: "output_path";
5
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
6
+ output: tools.FilePathRelative | tools.FilePathAbsolute;
7
+ sigma?: number;
8
+ };
9
+ type ImageBlurInPlaceStrategy = {
10
+ strategy: "in_place";
11
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
12
+ sigma?: number;
13
+ };
14
+ export type ImageBlurStrategy = ImageBlurOutputPathStrategy | ImageBlurInPlaceStrategy;
15
+
16
+ export interface ImageBlurPort {
17
+ blur(recipe: ImageBlurStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute>;
18
+ }
@@ -0,0 +1,7 @@
1
+ import type { ImageCompressorPort, ImageCompressorStrategy } from "./image-compressor.port";
2
+
3
+ export class ImageCompressorNoopAdapter implements ImageCompressorPort {
4
+ async compress(recipe: ImageCompressorStrategy) {
5
+ return recipe.strategy === "output_path" ? recipe.output : recipe.input;
6
+ }
7
+ }
@@ -0,0 +1,36 @@
1
+ import type { FileRenamerPort } from "./file-renamer.port";
2
+ import type { ImageCompressorPort, ImageCompressorStrategy } from "./image-compressor.port";
3
+
4
+ type Dependencies = { FileRenamer: FileRenamerPort };
5
+
6
+ export class ImageCompressorSharpAdapter implements ImageCompressorPort {
7
+ constructor(private readonly deps: Dependencies) {}
8
+
9
+ private async load() {
10
+ const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
11
+ return (await import(name)).default;
12
+ }
13
+
14
+ private static readonly DEFAULT_QUALITY = 85;
15
+
16
+ async compress(recipe: ImageCompressorStrategy) {
17
+ const sharp = await this.load();
18
+
19
+ const quality = recipe.quality ?? ImageCompressorSharpAdapter.DEFAULT_QUALITY;
20
+
21
+ const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
22
+ const filename = final.getFilename();
23
+ const temporary = final.withFilename(filename.withSuffix("-compressed"));
24
+
25
+ const extension = final.getFilename().getExtension();
26
+ const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
27
+
28
+ const pipeline = sharp(recipe.input.get());
29
+ using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
30
+
31
+ await pipeline.rotate().toFormat(format, { quality }).toFile(temporary.get());
32
+ await this.deps.FileRenamer.rename(temporary, final);
33
+
34
+ return final;
35
+ }
36
+ }
@@ -0,0 +1,18 @@
1
+ import type * as tools from "@bgord/tools";
2
+
3
+ export type ImageCompressorOutputPathStrategy = {
4
+ strategy: "output_path";
5
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
6
+ output: tools.FilePathRelative | tools.FilePathAbsolute;
7
+ quality?: tools.IntegerPositiveType;
8
+ };
9
+ export type ImageCompressorInPlaceStrategy = {
10
+ strategy: "in_place";
11
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
12
+ quality?: tools.IntegerPositiveType;
13
+ };
14
+ export type ImageCompressorStrategy = ImageCompressorInPlaceStrategy | ImageCompressorOutputPathStrategy;
15
+
16
+ export interface ImageCompressorPort {
17
+ compress(recipe: ImageCompressorStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute>;
18
+ }