@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,12 @@
1
+ import type { SecurityRuleStrategy } from "./security-rule.strategy";
2
+ import { SecurityRuleName } from "./security-rule-name.vo";
3
+
4
+ export class SecurityRuleFailStrategy implements SecurityRuleStrategy {
5
+ async isViolated() {
6
+ return true;
7
+ }
8
+
9
+ get name() {
10
+ return SecurityRuleName.parse("fail");
11
+ }
12
+ }
@@ -0,0 +1,20 @@
1
+ import type { Context } from "hono";
2
+ import type { SecurityRuleStrategy } from "./security-rule.strategy";
3
+ import { SecurityRuleName } from "./security-rule-name.vo";
4
+
5
+ export class SecurityRuleHoneyPotFieldStrategy implements SecurityRuleStrategy {
6
+ constructor(private readonly field: string) {}
7
+
8
+ async isViolated(c: Context) {
9
+ const request = c.req.raw.clone();
10
+
11
+ const body = await request.json().catch(() => ({}));
12
+ const value = body[this.field];
13
+
14
+ return value !== undefined && value !== null && value !== "";
15
+ }
16
+
17
+ get name() {
18
+ return SecurityRuleName.parse("honey_pot_field");
19
+ }
20
+ }
@@ -0,0 +1,22 @@
1
+ import * as z from "zod/v4";
2
+
3
+ export const SecurityRuleNameError = {
4
+ Type: "security.rule.name.type",
5
+ Empty: "security.rule.name.empty",
6
+ TooLong: "security.rule.name.too.long",
7
+ BadChars: "security.rule.name.bad.chars",
8
+ };
9
+
10
+ // One to sixty four letters, digits, or underscores
11
+ const CHARS_WHITELIST = /^[a-zA-Z0-9_]{1,512}$/;
12
+
13
+ // Stryker disable all
14
+ export const SecurityRuleName = z
15
+ // Stryker restore all
16
+ .string(SecurityRuleNameError.Type)
17
+ .min(1, SecurityRuleNameError.Empty)
18
+ .max(512, SecurityRuleNameError.TooLong)
19
+ .regex(CHARS_WHITELIST, SecurityRuleNameError.BadChars)
20
+ .brand("SecurityRuleName");
21
+
22
+ export type SecurityRuleNameType = z.infer<typeof SecurityRuleName>;
@@ -0,0 +1,25 @@
1
+ import type { Context } from "hono";
2
+ import type { SecurityRuleStrategy } from "./security-rule.strategy";
3
+ import { SecurityRuleName } from "./security-rule-name.vo";
4
+
5
+ export const SecurityRuleOrStrategyError = {
6
+ MissingRules: "security.rule.or.adapter.error.missing.rules",
7
+ MaxRules: "security.rule.or.adapter.error.max.rules",
8
+ };
9
+
10
+ export class SecurityRuleOrStrategy implements SecurityRuleStrategy {
11
+ constructor(private readonly rules: SecurityRuleStrategy[]) {
12
+ if (rules.length === 0) throw new Error(SecurityRuleOrStrategyError.MissingRules);
13
+ if (rules.length > 5) throw new Error(SecurityRuleOrStrategyError.MaxRules);
14
+ }
15
+
16
+ async isViolated(c: Context) {
17
+ const reports = await Promise.all(this.rules.map((rule) => rule.isViolated(c)));
18
+
19
+ return reports.some(Boolean);
20
+ }
21
+
22
+ get name() {
23
+ return SecurityRuleName.parse(`or_${this.rules.map((rule) => rule.name).join("_")}`);
24
+ }
25
+ }
@@ -0,0 +1,12 @@
1
+ import type { SecurityRuleStrategy } from "./security-rule.strategy";
2
+ import { SecurityRuleName } from "./security-rule-name.vo";
3
+
4
+ export class SecurityRulePassStrategy implements SecurityRuleStrategy {
5
+ async isViolated() {
6
+ return false;
7
+ }
8
+
9
+ get name() {
10
+ return SecurityRuleName.parse("pass");
11
+ }
12
+ }
@@ -0,0 +1,20 @@
1
+ import type { Context } from "hono";
2
+ import { ALL_BOTS } from "./bots.vo";
3
+ import { ClientFromHono } from "./client-from-hono.adapter";
4
+ import { ClientUserAgent } from "./client-user-agent.vo";
5
+ import type { SecurityRuleStrategy } from "./security-rule.strategy";
6
+ import { SecurityRuleName } from "./security-rule-name.vo";
7
+
8
+ export class SecurityRuleUserAgentStrategy implements SecurityRuleStrategy {
9
+ constructor(private readonly blacklist: string[] = ALL_BOTS) {}
10
+
11
+ async isViolated(c: Context) {
12
+ const client = ClientFromHono.translate(c);
13
+
14
+ return this.blacklist.some((bot) => client.matchesUa(ClientUserAgent.parse(bot)));
15
+ }
16
+
17
+ get name() {
18
+ return SecurityRuleName.parse("user_agent");
19
+ }
20
+ }
@@ -0,0 +1,50 @@
1
+ import * as tools from "@bgord/tools";
2
+ import type { Context } from "hono";
3
+ import type { CacheRepositoryPort } from "./cache-repository.port";
4
+ import { CacheSubjectRequestResolver } from "./cache-subject-request-resolver.vo";
5
+ import { CacheSubjectSegmentFixedStrategy } from "./cache-subject-segment-fixed.strategy";
6
+ import { CacheSubjectSegmentIpStrategy } from "./cache-subject-segment-ip.strategy";
7
+ import type { HashContentStrategy } from "./hash-content.strategy";
8
+ import type { SecurityRuleStrategy } from "./security-rule.strategy";
9
+ import { SecurityRuleName } from "./security-rule-name.vo";
10
+
11
+ type Dependencies = { CacheRepository: CacheRepositoryPort; HashContent: HashContentStrategy };
12
+
13
+ export class SecurityRuleViolationThresholdStrategy implements SecurityRuleStrategy {
14
+ constructor(
15
+ private readonly rule: SecurityRuleStrategy,
16
+ private readonly config: { threshold: tools.IntegerPositiveType },
17
+ private readonly deps: Dependencies,
18
+ ) {}
19
+
20
+ // Best-effort increment, occasional lost increments are acceptable for concurrent requests.
21
+ async isViolated(c: Context) {
22
+ const resolver = new CacheSubjectRequestResolver(
23
+ [new CacheSubjectSegmentFixedStrategy(this.name), new CacheSubjectSegmentIpStrategy()],
24
+ this.deps,
25
+ );
26
+ const subject = await resolver.resolve(c);
27
+
28
+ const violated = await this.rule.isViolated(c);
29
+
30
+ if (!violated) return false;
31
+
32
+ try {
33
+ const count = (await this.deps.CacheRepository.get<tools.IntegerNonNegativeType>(subject.hex)) ?? 0;
34
+
35
+ await this.deps.CacheRepository.set<tools.IntegerNonNegativeType>(
36
+ subject.hex,
37
+ tools.IntegerNonNegative.parse(count + 1),
38
+ );
39
+
40
+ if (count + 1 >= this.config.threshold) return true;
41
+ return false;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ get name() {
48
+ return SecurityRuleName.parse(`violation_threshold_${this.config.threshold}_${this.rule.name}`);
49
+ }
50
+ }
@@ -0,0 +1,8 @@
1
+ import type { Context } from "hono";
2
+ import type { SecurityRuleNameType } from "./security-rule-name.vo";
3
+
4
+ export interface SecurityRuleStrategy {
5
+ isViolated(c: Context): Promise<boolean>;
6
+
7
+ get name(): SecurityRuleNameType;
8
+ }
@@ -0,0 +1,102 @@
1
+ import * as tools from "@bgord/tools";
2
+ import { bodyLimit } from "hono/body-limit";
3
+ import { cors } from "hono/cors";
4
+ import { languageDetector } from "hono/language";
5
+ import { requestId } from "hono/request-id";
6
+ import { secureHeaders } from "hono/secure-headers";
7
+ import { timing } from "hono/timing";
8
+ import { ApiVersion } from "./api-version.middleware";
9
+ import type { BuildInfoRepositoryStrategy } from "./build-info-repository.strategy";
10
+ import type { CacheResolverStrategy } from "./cache-resolver.strategy";
11
+ import type { ClockPort } from "./clock.port";
12
+ import { Context } from "./context.middleware";
13
+ import { CorrelationStorage } from "./correlation-storage.service";
14
+ import { ETagExtractor } from "./etag-extractor.middleware";
15
+ import type { HashContentStrategy } from "./hash-content.strategy";
16
+ import { HttpLogger, type HttpLoggerOptions } from "./http-logger.middleware";
17
+ import type { I18nConfigType } from "./i18n.service";
18
+ import type { IdProviderPort } from "./id-provider.port";
19
+ import type { LoggerPort } from "./logger.port";
20
+ import { MaintenanceMode, type MaintenanceModeConfigType } from "./maintenance-mode.middleware";
21
+ import { type ShieldCsrfConfigType, ShieldCsrfStrategy } from "./shield-csrf.strategy";
22
+ import { TimeZoneOffset } from "./time-zone-offset.middleware";
23
+ import { WeakETagExtractor } from "./weak-etag-extractor.middleware";
24
+
25
+ type SetupConfigType = {
26
+ csrf: ShieldCsrfConfigType;
27
+ cors?: Parameters<typeof cors>[0];
28
+ httpLogger?: HttpLoggerOptions;
29
+ maintenanceMode?: MaintenanceModeConfigType;
30
+ BODY_LIMIT_MAX_SIZE?: tools.Size;
31
+ };
32
+
33
+ type Dependencies = {
34
+ Logger: LoggerPort;
35
+ IdProvider: IdProviderPort;
36
+ I18n: I18nConfigType;
37
+ Clock: ClockPort;
38
+ CacheResolver: CacheResolverStrategy;
39
+ HashContent: HashContentStrategy;
40
+ BuildInfoRepository: BuildInfoRepositoryStrategy;
41
+ };
42
+
43
+ export class Setup {
44
+ static essentials(config: SetupConfigType, deps: Dependencies) {
45
+ const BODY_LIMIT_MAX_SIZE = config.BODY_LIMIT_MAX_SIZE ?? tools.Size.fromKb(128);
46
+
47
+ return [
48
+ MaintenanceMode.build(config.maintenanceMode),
49
+ requestId({
50
+ limitLength: 36,
51
+ headerName: "x-correlation-id",
52
+ generator: () => deps.IdProvider.generate(),
53
+ }),
54
+ ApiVersion.build(deps),
55
+ new ShieldCsrfStrategy(config.csrf).verify,
56
+ secureHeaders({
57
+ referrerPolicy: "no-referrer",
58
+ xContentTypeOptions: "nosniff",
59
+ xDnsPrefetchControl: false,
60
+ xDownloadOptions: true,
61
+ xPermittedCrossDomainPolicies: false,
62
+ crossOriginEmbedderPolicy: false,
63
+ crossOriginOpenerPolicy: false,
64
+ crossOriginResourcePolicy: false,
65
+ originAgentCluster: false,
66
+ xFrameOptions: false,
67
+ }),
68
+ bodyLimit({ maxSize: BODY_LIMIT_MAX_SIZE.toBytes() }),
69
+ cors({
70
+ // Stryker disable all
71
+ origin: (origin, c) => {
72
+ // server-to-server, curl, same-origin navigation
73
+ if (!origin) return undefined;
74
+
75
+ // same-origin fetch
76
+ if (origin === new URL(c.req.url).origin) return origin;
77
+
78
+ // deny cross-origin
79
+ return null;
80
+ },
81
+ // Stryker restore all
82
+ credentials: false,
83
+ maxAge: tools.Duration.Minutes(10).seconds,
84
+ ...config.cors,
85
+ }),
86
+ languageDetector({
87
+ supportedLanguages: Object.keys(deps.I18n.supportedLanguages),
88
+ fallbackLanguage: deps.I18n.defaultLanguage,
89
+ // Stryker disable all
90
+ caches: false,
91
+ // Stryker restore all
92
+ }),
93
+ TimeZoneOffset.attach,
94
+ Context.attach,
95
+ WeakETagExtractor.attach,
96
+ ETagExtractor.attach,
97
+ HttpLogger.build(deps, config.httpLogger),
98
+ timing(),
99
+ CorrelationStorage.handle(),
100
+ ];
101
+ }
102
+ }
@@ -0,0 +1,20 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import type { ShieldStrategy } from "./shield.strategy";
5
+
6
+ type ApiKeyShieldConfigType = { API_KEY: tools.ApiKeyType };
7
+
8
+ export const ShieldApiKeyError = new HTTPException(403, { message: "shield.api.key" });
9
+
10
+ export class ShieldApiKeyStrategy implements ShieldStrategy {
11
+ static readonly HEADER_NAME = "bgord-api-key";
12
+
13
+ constructor(private readonly config: ApiKeyShieldConfigType) {}
14
+
15
+ verify = createMiddleware(async (context, next) => {
16
+ if (context.req.header(ShieldApiKeyStrategy.HEADER_NAME) === this.config.API_KEY) return next();
17
+
18
+ throw ShieldApiKeyError;
19
+ });
20
+ }
@@ -0,0 +1,38 @@
1
+ import type { betterAuth } from "better-auth";
2
+ import type hono from "hono";
3
+ import { createMiddleware } from "hono/factory";
4
+ import { HTTPException } from "hono/http-exception";
5
+
6
+ export const ShieldAuthError = new HTTPException(403, { message: "shield.auth" });
7
+
8
+ export class ShieldAuthStrategy {
9
+ constructor(private readonly Auth: ReturnType<typeof betterAuth>) {}
10
+
11
+ attach = createMiddleware(async (c: hono.Context, next: hono.Next) => {
12
+ const session = await this.Auth.api.getSession({ headers: c.req.raw.headers });
13
+
14
+ if (!session) {
15
+ c.set("user", null);
16
+ c.set("session", null);
17
+ return next();
18
+ }
19
+
20
+ c.set("user", session.user);
21
+ c.set("session", session.session);
22
+ return next();
23
+ });
24
+
25
+ verify = createMiddleware(async (c: hono.Context, next: hono.Next) => {
26
+ const user = c.get("user");
27
+
28
+ if (!user) throw ShieldAuthError;
29
+ return next();
30
+ });
31
+
32
+ reverse = createMiddleware(async (c: hono.Context, next: hono.Next) => {
33
+ const user = c.get("user");
34
+
35
+ if (user) throw ShieldAuthError;
36
+ return next();
37
+ });
38
+ }
@@ -0,0 +1,17 @@
1
+ import { basicAuth } from "hono/basic-auth";
2
+ import { createMiddleware } from "hono/factory";
3
+ import type { BasicAuthPasswordType } from "./basic-auth-password.vo";
4
+ import type { BasicAuthUsernameType } from "./basic-auth-username.vo";
5
+ import type { ShieldStrategy } from "./shield.strategy";
6
+
7
+ type ShieldBasicAuthConfigType = { username: BasicAuthUsernameType; password: BasicAuthPasswordType };
8
+
9
+ export class ShieldBasicAuthStrategy implements ShieldStrategy {
10
+ private readonly basicAuth;
11
+
12
+ constructor(config: ShieldBasicAuthConfigType) {
13
+ this.basicAuth = basicAuth(config);
14
+ }
15
+
16
+ verify = createMiddleware(async (context, next) => this.basicAuth(context, next));
17
+ }
@@ -0,0 +1,23 @@
1
+ import { createMiddleware } from "hono/factory";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import type { ShieldStrategy } from "./shield.strategy";
4
+
5
+ const STATE_CHANGING_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
6
+
7
+ export type ShieldCsrfConfigType = { origin: string[] };
8
+
9
+ export const ShieldCsrfError = new HTTPException(403, { message: "shield.csrf" });
10
+
11
+ export class ShieldCsrfStrategy implements ShieldStrategy {
12
+ constructor(private readonly config: ShieldCsrfConfigType) {}
13
+
14
+ verify = createMiddleware(async (context, next) => {
15
+ if (!STATE_CHANGING_METHODS.includes(context.req.method)) return next();
16
+
17
+ const origin = context.req.header("origin");
18
+
19
+ if (!origin) return next();
20
+ if (!this.config.origin.includes(origin)) throw ShieldCsrfError;
21
+ return next();
22
+ });
23
+ }
@@ -0,0 +1,22 @@
1
+ import hcaptcha from "hcaptcha";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import type { HCaptchaSecretKeyType } from "./hcaptcha-secret-key.vo";
5
+ import type { ShieldStrategy } from "./shield.strategy";
6
+
7
+ export const ShieldHcaptchaLocalError = new HTTPException(403, { message: "shield.hcaptcha.local" });
8
+
9
+ export class ShieldHcaptchaLocalStrategy implements ShieldStrategy {
10
+ constructor(private readonly secretKey: HCaptchaSecretKeyType) {}
11
+
12
+ verify = createMiddleware(async (_, next) => {
13
+ try {
14
+ const result = await hcaptcha.verify(this.secretKey, "10000000-aaaa-bbbb-cccc-000000000001");
15
+
16
+ if (!result.success) throw ShieldHcaptchaLocalError;
17
+ return next();
18
+ } catch {
19
+ throw ShieldHcaptchaLocalError;
20
+ }
21
+ });
22
+ }
@@ -0,0 +1,24 @@
1
+ import hcaptcha from "hcaptcha";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import type { HCaptchaSecretKeyType } from "./hcaptcha-secret-key.vo";
5
+ import type { ShieldStrategy } from "./shield.strategy";
6
+
7
+ export const ShieldHcaptchaError = new HTTPException(403, { message: "shield.hcaptcha" });
8
+
9
+ export class ShieldHcaptchaStrategy implements ShieldStrategy {
10
+ constructor(private readonly secretKey: HCaptchaSecretKeyType) {}
11
+
12
+ verify = createMiddleware(async (context, next) => {
13
+ try {
14
+ const form = await context.req.formData();
15
+ const hcaptchaTokenFormData = form.get("h-captcha-response")?.toString() as string;
16
+ const result = await hcaptcha.verify(this.secretKey, hcaptchaTokenFormData);
17
+
18
+ if (!result.success) throw ShieldHcaptchaError;
19
+ return next();
20
+ } catch {
21
+ throw ShieldHcaptchaError;
22
+ }
23
+ });
24
+ }
@@ -0,0 +1,6 @@
1
+ import { createMiddleware } from "hono/factory";
2
+ import type { ShieldStrategy } from "./shield.strategy";
3
+
4
+ export class ShieldNoopStrategy implements ShieldStrategy {
5
+ verify = createMiddleware(async (_, next) => next());
6
+ }
@@ -0,0 +1,41 @@
1
+ import * as tools from "@bgord/tools";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import type { CacheResolverStrategy } from "./cache-resolver.strategy";
5
+ import type { CacheSubjectRequestResolver } from "./cache-subject-request-resolver.vo";
6
+ import type { ClockPort } from "./clock.port";
7
+ import type { ShieldStrategy } from "./shield.strategy";
8
+
9
+ type ShieldRateLimitOptionsType = {
10
+ enabled: boolean;
11
+ resolver: CacheSubjectRequestResolver;
12
+ window: tools.Duration;
13
+ };
14
+
15
+ type Dependencies = { Clock: ClockPort; CacheResolver: CacheResolverStrategy };
16
+
17
+ export const ShieldRateLimitError = new HTTPException(429, { message: "shield.rate.limit" });
18
+
19
+ export class ShieldRateLimitStrategy implements ShieldStrategy {
20
+ constructor(
21
+ private readonly options: ShieldRateLimitOptionsType,
22
+ private readonly deps: Dependencies,
23
+ ) {}
24
+
25
+ verify = createMiddleware(async (context, next) => {
26
+ if (!this.options.enabled) return next();
27
+
28
+ const subject = await this.options.resolver.resolve(context);
29
+
30
+ const limiter = await this.deps.CacheResolver.resolve(
31
+ subject.hex,
32
+ async () => new tools.RateLimiter(this.options.window),
33
+ );
34
+
35
+ const result = limiter.verify(this.deps.Clock.now());
36
+
37
+ if (!result.allowed) throw ShieldRateLimitError;
38
+
39
+ return next();
40
+ });
41
+ }
@@ -0,0 +1,49 @@
1
+ import * as tools from "@bgord/tools";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import type { RecaptchaSecretKeyType } from "./recaptcha-secret-key.vo";
5
+ import type { ShieldStrategy } from "./shield.strategy";
6
+
7
+ export type RecaptchaVerifierConfigType = { secretKey: RecaptchaSecretKeyType };
8
+ export type RecaptchaResultType = { success: boolean; score: number };
9
+
10
+ export const ShieldRecaptchaError = new HTTPException(403, { message: "shield.recaptcha" });
11
+
12
+ export class ShieldRecaptchaStrategy implements ShieldStrategy {
13
+ private static readonly URL = tools.UrlWithoutSlash.parse(
14
+ "https://www.google.com/recaptcha/api/siteverify",
15
+ );
16
+
17
+ constructor(private readonly config: RecaptchaVerifierConfigType) {}
18
+
19
+ verify = createMiddleware(async (context, next) => {
20
+ try {
21
+ const header = context.req.header("x-recaptcha-token");
22
+ const query = context.req.query("recaptchaToken");
23
+ const form = (await context.req.formData()).get("g-recaptcha-response")?.toString();
24
+
25
+ const token = header ?? query ?? form;
26
+
27
+ if (!token) throw ShieldRecaptchaError;
28
+
29
+ // cSpell:ignore remoteip
30
+ const remoteip = context.req.header("x-forwarded-for") ?? "";
31
+
32
+ const params = new URLSearchParams({ secret: this.config.secretKey, response: token, remoteip });
33
+
34
+ const response = await fetch(ShieldRecaptchaStrategy.URL, {
35
+ method: "POST",
36
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
37
+ body: params,
38
+ });
39
+
40
+ const result: RecaptchaResultType = await response.json();
41
+
42
+ if (!result.success || result.score < 0.5) throw ShieldRecaptchaError;
43
+
44
+ await next();
45
+ } catch {
46
+ throw ShieldRecaptchaError;
47
+ }
48
+ });
49
+ }
@@ -0,0 +1,80 @@
1
+ import { createMiddleware } from "hono/factory";
2
+ import type { ContentfulStatusCode } from "hono/utils/http-status";
3
+ import { ClientFromHono } from "./client-from-hono.adapter";
4
+ import { SecurityContext } from "./security-context.vo";
5
+ import type { SecurityPolicy } from "./security-policy.vo";
6
+ import type { ShieldStrategy } from "./shield.strategy";
7
+ import type { SleeperPort } from "./sleeper.port";
8
+
9
+ type Dependencies = { Sleeper: SleeperPort };
10
+
11
+ export const ShieldSecurityAdapterError = {
12
+ Unhandled: "shield.security.adapter.error.unhandled",
13
+ MissingPolicies: "shield.security.adapter.error.missing.policies",
14
+ MaxPolicies: "shield.security.adapter.error.max.policies",
15
+ };
16
+
17
+ export class ShieldSecurityStrategy implements ShieldStrategy {
18
+ constructor(
19
+ private readonly policies: SecurityPolicy[],
20
+ private readonly deps: Dependencies,
21
+ ) {
22
+ if (policies.length === 0) throw new Error(ShieldSecurityAdapterError.MissingPolicies);
23
+ if (policies.length > 5) throw new Error(ShieldSecurityAdapterError.MaxPolicies);
24
+ }
25
+
26
+ verify = createMiddleware(async (c, next) => {
27
+ for (const policy of this.policies) {
28
+ const violation = await policy.rule.isViolated(c);
29
+
30
+ if (!violation) continue;
31
+
32
+ const context = new SecurityContext(
33
+ policy.rule.name,
34
+ policy.countermeasure.name,
35
+ ClientFromHono.translate(c),
36
+ // Stryker disable all
37
+ c.get("user")?.id,
38
+ // Stryker restore all
39
+ );
40
+
41
+ const action = await policy.countermeasure.execute(context);
42
+
43
+ // Stryker disable all
44
+ switch (action.kind) {
45
+ case "allow":
46
+ return next();
47
+
48
+ case "deny":
49
+ return c.text(action.reason, action.response.status as ContentfulStatusCode);
50
+
51
+ case "mirage":
52
+ return c.json({}, action.response.status as ContentfulStatusCode);
53
+
54
+ case "delay": {
55
+ await this.deps.Sleeper.wait(action.duration);
56
+
57
+ switch (action.after.kind) {
58
+ case "allow":
59
+ return next();
60
+
61
+ case "deny":
62
+ return c.text(action.after.reason, action.after.response.status as ContentfulStatusCode);
63
+
64
+ case "mirage":
65
+ return c.json({}, action.after.response.status as ContentfulStatusCode);
66
+
67
+ case "delay":
68
+ throw new Error(ShieldSecurityAdapterError.Unhandled);
69
+
70
+ default:
71
+ throw new Error(ShieldSecurityAdapterError.Unhandled);
72
+ }
73
+ }
74
+ }
75
+ // Stryker restore all
76
+ }
77
+
78
+ return next();
79
+ });
80
+ }
@@ -0,0 +1,19 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import { createMiddleware } from "hono/factory";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import { timeout } from "hono/timeout";
5
+ import type { ShieldStrategy } from "./shield.strategy";
6
+
7
+ export const ShieldTimeoutError = new HTTPException(408, { message: "shield.timeout" });
8
+
9
+ type ShieldTimeoutConfigType = { duration: tools.Duration };
10
+
11
+ export class ShieldTimeoutStrategy implements ShieldStrategy {
12
+ private readonly timeout;
13
+
14
+ constructor(config: ShieldTimeoutConfigType) {
15
+ this.timeout = timeout(config.duration.ms, ShieldTimeoutError);
16
+ }
17
+
18
+ verify = createMiddleware(async (context, next) => this.timeout(context, next));
19
+ }
@@ -0,0 +1,5 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+
3
+ export interface ShieldStrategy {
4
+ verify: MiddlewareHandler;
5
+ }
@@ -0,0 +1,8 @@
1
+ import { createMiddleware } from "hono/factory";
2
+
3
+ export class SimulatedError {
4
+ static handle = () =>
5
+ createMiddleware(async (_, _next) => {
6
+ throw new Error("Simulated error");
7
+ });
8
+ }
@@ -0,0 +1,5 @@
1
+ import type { SleeperPort } from "./sleeper.port";
2
+
3
+ export class SleeperNoopAdapter implements SleeperPort {
4
+ async wait() {}
5
+ }
@@ -0,0 +1,8 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { SleeperPort } from "./sleeper.port";
3
+
4
+ export class SleeperSystemAdapter implements SleeperPort {
5
+ async wait(duration: tools.Duration) {
6
+ await Bun.sleep(duration.ms);
7
+ }
8
+ }