@bgord/bun 1.23.11 → 1.24.0

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 (194) hide show
  1. package/dist/atomic-file-writer-collecting.adapter.d.ts +8 -0
  2. package/dist/atomic-file-writer-collecting.adapter.d.ts.map +1 -0
  3. package/dist/atomic-file-writer-collecting.adapter.js +7 -0
  4. package/dist/atomic-file-writer-collecting.adapter.js.map +1 -0
  5. package/dist/atomic-file-writer-noop.adapter.d.ts +7 -0
  6. package/dist/atomic-file-writer-noop.adapter.d.ts.map +1 -0
  7. package/dist/atomic-file-writer-noop.adapter.js +4 -0
  8. package/dist/atomic-file-writer-noop.adapter.js.map +1 -0
  9. package/dist/atomic-file-writer.adapter.d.ts +19 -0
  10. package/dist/atomic-file-writer.adapter.d.ts.map +1 -0
  11. package/dist/atomic-file-writer.adapter.js +18 -0
  12. package/dist/atomic-file-writer.adapter.js.map +1 -0
  13. package/dist/atomic-file-writer.port.d.ts +6 -0
  14. package/dist/atomic-file-writer.port.d.ts.map +1 -0
  15. package/dist/atomic-file-writer.port.js +2 -0
  16. package/dist/atomic-file-writer.port.js.map +1 -0
  17. package/dist/build-info.vo.d.ts +7 -1
  18. package/dist/build-info.vo.d.ts.map +1 -1
  19. package/dist/build-info.vo.js +4 -1
  20. package/dist/build-info.vo.js.map +1 -1
  21. package/dist/encryption-aes-gcm.adapter.d.ts +2 -2
  22. package/dist/encryption-aes-gcm.adapter.d.ts.map +1 -1
  23. package/dist/encryption-aes-gcm.adapter.js +2 -2
  24. package/dist/encryption-aes-gcm.adapter.js.map +1 -1
  25. package/dist/event-stream-accept.service.d.ts +4 -0
  26. package/dist/event-stream-accept.service.d.ts.map +1 -0
  27. package/dist/event-stream-accept.service.js +11 -0
  28. package/dist/event-stream-accept.service.js.map +1 -0
  29. package/dist/gzip.adapter.d.ts +2 -2
  30. package/dist/gzip.adapter.d.ts.map +1 -1
  31. package/dist/gzip.adapter.js +1 -1
  32. package/dist/gzip.adapter.js.map +1 -1
  33. package/dist/healthcheck.handler.d.ts +11 -3
  34. package/dist/healthcheck.handler.d.ts.map +1 -1
  35. package/dist/healthcheck.handler.js +11 -12
  36. package/dist/healthcheck.handler.js.map +1 -1
  37. package/dist/http-exception-errors.js +2 -2
  38. package/dist/http-exception-errors.js.map +1 -1
  39. package/dist/http-logger-hono.middleware.d.ts +1 -0
  40. package/dist/http-logger-hono.middleware.d.ts.map +1 -1
  41. package/dist/http-logger-hono.middleware.js +10 -6
  42. package/dist/http-logger-hono.middleware.js.map +1 -1
  43. package/dist/http-logger.middleware.d.ts.map +1 -1
  44. package/dist/http-logger.middleware.js +4 -6
  45. package/dist/http-logger.middleware.js.map +1 -1
  46. package/dist/image-blur.adapter.d.ts +2 -6
  47. package/dist/image-blur.adapter.d.ts.map +1 -1
  48. package/dist/image-blur.adapter.js +1 -4
  49. package/dist/image-blur.adapter.js.map +1 -1
  50. package/dist/image-compressor.adapter.d.ts +2 -6
  51. package/dist/image-compressor.adapter.d.ts.map +1 -1
  52. package/dist/image-compressor.adapter.js +1 -4
  53. package/dist/image-compressor.adapter.js.map +1 -1
  54. package/dist/image-exif-clear.adapter.d.ts +2 -6
  55. package/dist/image-exif-clear.adapter.d.ts.map +1 -1
  56. package/dist/image-exif-clear.adapter.js +1 -3
  57. package/dist/image-exif-clear.adapter.js.map +1 -1
  58. package/dist/image-formatter.adapter.d.ts +2 -6
  59. package/dist/image-formatter.adapter.d.ts.map +1 -1
  60. package/dist/image-formatter.adapter.js +1 -3
  61. package/dist/image-formatter.adapter.js.map +1 -1
  62. package/dist/image-grayscale.adapter.d.ts +2 -6
  63. package/dist/image-grayscale.adapter.d.ts.map +1 -1
  64. package/dist/image-grayscale.adapter.js +1 -4
  65. package/dist/image-grayscale.adapter.js.map +1 -1
  66. package/dist/image-processor.adapter.d.ts +2 -6
  67. package/dist/image-processor.adapter.d.ts.map +1 -1
  68. package/dist/image-processor.adapter.js +1 -3
  69. package/dist/image-processor.adapter.js.map +1 -1
  70. package/dist/image-resizer.adapter.d.ts +2 -6
  71. package/dist/image-resizer.adapter.d.ts.map +1 -1
  72. package/dist/image-resizer.adapter.js +1 -4
  73. package/dist/image-resizer.adapter.js.map +1 -1
  74. package/dist/index.d.ts +11 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +11 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/job-pruner-sqlite.adapter.d.ts.map +1 -1
  79. package/dist/job-pruner-sqlite.adapter.js +2 -1
  80. package/dist/job-pruner-sqlite.adapter.js.map +1 -1
  81. package/dist/job-queue-stats-provider-sqlite.adapter.js +4 -4
  82. package/dist/job-queue-stats-provider-sqlite.adapter.js.map +1 -1
  83. package/dist/job-recoverer-collecting.adapter.d.ts +7 -0
  84. package/dist/job-recoverer-collecting.adapter.d.ts.map +1 -0
  85. package/dist/job-recoverer-collecting.adapter.js +10 -0
  86. package/dist/job-recoverer-collecting.adapter.js.map +1 -0
  87. package/dist/job-recoverer-noop.adapter.d.ts +6 -0
  88. package/dist/job-recoverer-noop.adapter.d.ts.map +1 -0
  89. package/dist/job-recoverer-noop.adapter.js +7 -0
  90. package/dist/job-recoverer-noop.adapter.js.map +1 -0
  91. package/dist/job-recoverer-sqlite.adapter.d.ts +13 -0
  92. package/dist/job-recoverer-sqlite.adapter.d.ts.map +1 -0
  93. package/dist/job-recoverer-sqlite.adapter.js +14 -0
  94. package/dist/job-recoverer-sqlite.adapter.js.map +1 -0
  95. package/dist/job-recoverer.port.d.ts +5 -0
  96. package/dist/job-recoverer.port.d.ts.map +1 -0
  97. package/dist/job-recoverer.port.js +2 -0
  98. package/dist/job-recoverer.port.js.map +1 -0
  99. package/dist/memory-consumption.service.d.ts +1 -2
  100. package/dist/memory-consumption.service.d.ts.map +1 -1
  101. package/dist/memory-consumption.service.js.map +1 -1
  102. package/dist/path-matcher.service.d.ts +6 -0
  103. package/dist/path-matcher.service.d.ts.map +1 -0
  104. package/dist/path-matcher.service.js +13 -0
  105. package/dist/path-matcher.service.js.map +1 -0
  106. package/dist/remote-file-storage-disk.adapter.d.ts +2 -6
  107. package/dist/remote-file-storage-disk.adapter.d.ts.map +1 -1
  108. package/dist/remote-file-storage-disk.adapter.js +1 -9
  109. package/dist/remote-file-storage-disk.adapter.js.map +1 -1
  110. package/dist/request-context-hono.adapter.d.ts +1 -1
  111. package/dist/request-context-hono.adapter.d.ts.map +1 -1
  112. package/dist/request-context-hono.adapter.js +3 -3
  113. package/dist/request-context-hono.adapter.js.map +1 -1
  114. package/dist/request-context.port.d.ts +4 -0
  115. package/dist/request-context.port.d.ts.map +1 -1
  116. package/dist/request-context.port.js +4 -1
  117. package/dist/request-context.port.js.map +1 -1
  118. package/dist/runtime-stats-provider-noop.adapter.d.ts +5 -0
  119. package/dist/runtime-stats-provider-noop.adapter.d.ts.map +1 -0
  120. package/dist/runtime-stats-provider-noop.adapter.js +14 -0
  121. package/dist/runtime-stats-provider-noop.adapter.js.map +1 -0
  122. package/dist/runtime-stats-provider-system.adapter.d.ts +12 -0
  123. package/dist/runtime-stats-provider-system.adapter.d.ts.map +1 -0
  124. package/dist/runtime-stats-provider-system.adapter.js +21 -0
  125. package/dist/runtime-stats-provider-system.adapter.js.map +1 -0
  126. package/dist/runtime-stats-provider.port.d.ts +18 -0
  127. package/dist/runtime-stats-provider.port.d.ts.map +1 -0
  128. package/dist/runtime-stats-provider.port.js +2 -0
  129. package/dist/runtime-stats-provider.port.js.map +1 -0
  130. package/dist/security-rule-violation-threshold.strategy.js +1 -1
  131. package/dist/security-rule-violation-threshold.strategy.js.map +1 -1
  132. package/dist/setup-hono.service.js +1 -1
  133. package/dist/setup-hono.service.js.map +1 -1
  134. package/dist/shield-auth-hono.strategy.d.ts.map +1 -1
  135. package/dist/shield-auth-hono.strategy.js +4 -3
  136. package/dist/shield-auth-hono.strategy.js.map +1 -1
  137. package/dist/shield-auth.strategy.d.ts +1 -5
  138. package/dist/shield-auth.strategy.d.ts.map +1 -1
  139. package/dist/shield-auth.strategy.js +3 -6
  140. package/dist/shield-auth.strategy.js.map +1 -1
  141. package/dist/shield-maintenance.strategy.d.ts.map +1 -1
  142. package/dist/shield-maintenance.strategy.js +2 -5
  143. package/dist/shield-maintenance.strategy.js.map +1 -1
  144. package/dist/temporary-file-absolute.adapter.d.ts +2 -6
  145. package/dist/temporary-file-absolute.adapter.d.ts.map +1 -1
  146. package/dist/temporary-file-absolute.adapter.js +1 -9
  147. package/dist/temporary-file-absolute.adapter.js.map +1 -1
  148. package/dist/timing.middleware.d.ts +0 -1
  149. package/dist/timing.middleware.d.ts.map +1 -1
  150. package/dist/timing.middleware.js +2 -9
  151. package/dist/timing.middleware.js.map +1 -1
  152. package/package.json +2 -2
  153. package/readme.md +13 -0
  154. package/src/atomic-file-writer-collecting.adapter.ts +14 -0
  155. package/src/atomic-file-writer-noop.adapter.ts +10 -0
  156. package/src/atomic-file-writer.adapter.ts +34 -0
  157. package/src/atomic-file-writer.port.ts +6 -0
  158. package/src/build-info.vo.ts +4 -1
  159. package/src/encryption-aes-gcm.adapter.ts +4 -4
  160. package/src/event-stream-accept.service.ts +10 -0
  161. package/src/gzip.adapter.ts +3 -3
  162. package/src/healthcheck.handler.ts +16 -13
  163. package/src/http-exception-errors.ts +2 -2
  164. package/src/http-logger-hono.middleware.ts +12 -6
  165. package/src/http-logger.middleware.ts +4 -7
  166. package/src/image-blur.adapter.ts +3 -13
  167. package/src/image-compressor.adapter.ts +3 -12
  168. package/src/image-exif-clear.adapter.ts +3 -11
  169. package/src/image-formatter.adapter.ts +3 -12
  170. package/src/image-grayscale.adapter.ts +3 -13
  171. package/src/image-processor.adapter.ts +3 -11
  172. package/src/image-resizer.adapter.ts +3 -12
  173. package/src/index.ts +11 -0
  174. package/src/job-pruner-sqlite.adapter.ts +2 -1
  175. package/src/job-queue-stats-provider-sqlite.adapter.ts +4 -4
  176. package/src/job-recoverer-collecting.adapter.ts +14 -0
  177. package/src/job-recoverer-noop.adapter.ts +8 -0
  178. package/src/job-recoverer-sqlite.adapter.ts +20 -0
  179. package/src/job-recoverer.port.ts +5 -0
  180. package/src/memory-consumption.service.ts +4 -1
  181. package/src/path-matcher.service.ts +14 -0
  182. package/src/remote-file-storage-disk.adapter.ts +3 -17
  183. package/src/request-context-hono.adapter.ts +3 -4
  184. package/src/request-context.port.ts +5 -0
  185. package/src/runtime-stats-provider-noop.adapter.ts +16 -0
  186. package/src/runtime-stats-provider-system.adapter.ts +24 -0
  187. package/src/runtime-stats-provider.port.ts +16 -0
  188. package/src/security-rule-violation-threshold.strategy.ts +2 -2
  189. package/src/setup-hono.service.ts +1 -1
  190. package/src/shield-auth-hono.strategy.ts +4 -3
  191. package/src/shield-auth.strategy.ts +3 -8
  192. package/src/shield-maintenance.strategy.ts +2 -6
  193. package/src/temporary-file-absolute.adapter.ts +3 -18
  194. package/src/timing.middleware.ts +2 -10
@@ -1,5 +1,6 @@
1
1
  import * as tools from "@bgord/tools";
2
2
  import * as v from "valibot";
3
+ import { PathMatcher } from "./path-matcher.service";
3
4
  export const Maintenance = v.object({ enabled: tools.FeatureFlagValue });
4
5
  export class ShieldMaintenanceStrategy {
5
6
  config;
@@ -12,11 +13,7 @@ export class ShieldMaintenanceStrategy {
12
13
  this.RetryAfter = config?.RetryAfter ?? tools.Duration.Hours(1);
13
14
  }
14
15
  shouldSkip(context) {
15
- return (this.config?.skip?.some((rule) => {
16
- if (rule instanceof URLPattern)
17
- return rule.test({ pathname: context.request.path });
18
- return context.request.path.startsWith(rule);
19
- }) ?? false);
16
+ return PathMatcher.matches(this.config?.skip, context.request.path);
20
17
  }
21
18
  async evaluate() {
22
19
  const maintenance = await this.MaintenanceConfig?.get();
@@ -1 +1 @@
1
- {"version":3,"file":"shield-maintenance.strategy.js","sourceRoot":"","sources":["../src/shield-maintenance.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAI7B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAiBzE,MAAM,OAAO,yBAAyB;IAKP,MAAM;IAJlB,QAAQ,GAAG,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;IAC1C,iBAAiB,CAAuC;IACxD,UAAU,CAAiB;IAE5C,YAA6B,MAAgC;sBAAhC,MAAM;QACjC,IAAI,CAAC,iBAAiB,GAAG,MAAM,EAAE,iBAAiB,CAAC;QACnD,IAAI,CAAC,UAAU,GAAG,MAAM,EAAE,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,OAAuB;QAChC,OAAO,CACL,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAC/B,IAAI,IAAI,YAAY,UAAU;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACrF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC,CAAC,IAAI,KAAK,CACZ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC;QACxD,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QAE9F,OAAO;YACL,OAAO;YACP,IAAI,EAAE,GAAG;YACT,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE;YAC/B,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;SACpF,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"shield-maintenance.strategy.js","sourceRoot":"","sources":["../src/shield-maintenance.strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAIrD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAiBzE,MAAM,OAAO,yBAAyB;IAKP,MAAM;IAJlB,QAAQ,GAAG,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;IAC1C,iBAAiB,CAAuC;IACxD,UAAU,CAAiB;IAE5C,YAA6B,MAAgC;sBAAhC,MAAM;QACjC,IAAI,CAAC,iBAAiB,GAAG,MAAM,EAAE,iBAAiB,CAAC;QACnD,IAAI,CAAC,UAAU,GAAG,MAAM,EAAE,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,OAAuB;QAChC,OAAO,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC;QACxD,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QAE9F,OAAO;YACL,OAAO;YACP,IAAI,EAAE,GAAG;YACT,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE;YAC/B,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;SACpF,CAAC;IACJ,CAAC;CACF"}
@@ -1,14 +1,10 @@
1
1
  import * as tools from "@bgord/tools";
2
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
2
3
  import type { FileCleanerPort } from "./file-cleaner.port";
3
- import type { FileRenamerPort } from "./file-renamer.port";
4
- import type { FileWriterPort } from "./file-writer.port";
5
- import type { NonceProviderPort } from "./nonce-provider.port";
6
4
  import type { TemporaryFilePort } from "./temporary-file.port";
7
5
  type Dependencies = {
6
+ AtomicFileWriter: AtomicFileWriterPort;
8
7
  FileCleaner: FileCleanerPort;
9
- FileRenamer: FileRenamerPort;
10
- FileWriter: FileWriterPort;
11
- NonceProvider: NonceProviderPort;
12
8
  };
13
9
  export declare class TemporaryFileAbsoluteAdapter implements TemporaryFilePort {
14
10
  private readonly directory;
@@ -1 +1 @@
1
- {"version":3,"file":"temporary-file-absolute.adapter.d.ts","sourceRoot":"","sources":["../src/temporary-file-absolute.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,KAAK,YAAY,GAAG;IAClB,WAAW,EAAE,eAAe,CAAC;IAC7B,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,iBAAiB,CAAC;CAClC,CAAC;AAEF,qBAAa,4BAA6B,YAAW,iBAAiB;IAElE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFvB,YACmB,SAAS,EAAE,KAAK,CAAC,yBAAyB,EAC1C,IAAI,EAAE,YAAY,EACjC;IAEE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAiBpF;IAEK,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;IAED,IAAI,IAAI,IAAI,KAAK,CAAC,yBAAyB,CAE1C;CACF"}
1
+ {"version":3,"file":"temporary-file-absolute.adapter.d.ts","sourceRoot":"","sources":["../src/temporary-file-absolute.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,KAAK,YAAY,GAAG;IAClB,gBAAgB,EAAE,oBAAoB,CAAC;IACvC,WAAW,EAAE,eAAe,CAAC;CAC9B,CAAC;AAEF,qBAAa,4BAA6B,YAAW,iBAAiB;IAElE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFvB,YACmB,SAAS,EAAE,KAAK,CAAC,yBAAyB,EAC1C,IAAI,EAAE,YAAY,EACjC;IAEE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAMpF;IAEK,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;IAED,IAAI,IAAI,IAAI,KAAK,CAAC,yBAAyB,CAE1C;CACF"}
@@ -7,16 +7,8 @@ export class TemporaryFileAbsoluteAdapter {
7
7
  this.deps = deps;
8
8
  }
9
9
  async write(filename, content) {
10
- const temporary = tools.FilePathAbsolute.fromPartsSafe(this.directory, filename.withSuffix(`-part-${this.deps.NonceProvider.generate()}`));
11
10
  const final = tools.FilePathAbsolute.fromPartsSafe(this.directory, filename);
12
- await this.deps.FileWriter.write(temporary.get(), content);
13
- try {
14
- await this.deps.FileRenamer.rename(temporary, final);
15
- }
16
- catch (error) {
17
- await this.deps.FileCleaner.delete(temporary);
18
- throw error;
19
- }
11
+ await this.deps.AtomicFileWriter.write(final, content);
20
12
  return final;
21
13
  }
22
14
  async cleanup(filename) {
@@ -1 +1 @@
1
- {"version":3,"file":"temporary-file-absolute.adapter.js","sourceRoot":"","sources":["../src/temporary-file-absolute.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AActC,MAAM,OAAO,4BAA4B;IAEpB,SAAS;IACT,IAAI;IAFvB,YACmB,SAA0C,EAC1C,IAAkB;yBADlB,SAAS;oBACT,IAAI;IACpB,CAAC;IAEJ,KAAK,CAAC,KAAK,CAAC,QAAwB,EAAE,OAAa;QACjD,MAAM,SAAS,GAAG,KAAK,CAAC,gBAAgB,CAAC,aAAa,CACpD,IAAI,CAAC,SAAS,EACd,QAAQ,CAAC,UAAU,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,CAAC,CACnE,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAE7E,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QAE3D,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC9C,MAAM,KAAK,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAwB;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrG,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF"}
1
+ {"version":3,"file":"temporary-file-absolute.adapter.js","sourceRoot":"","sources":["../src/temporary-file-absolute.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAUtC,MAAM,OAAO,4BAA4B;IAEpB,SAAS;IACT,IAAI;IAFvB,YACmB,SAA0C,EAC1C,IAAkB;yBADlB,SAAS;oBACT,IAAI;IACpB,CAAC;IAEJ,KAAK,CAAC,KAAK,CAAC,QAAwB,EAAE,OAAa;QACjD,MAAM,KAAK,GAAG,KAAK,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAE7E,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAEvD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAwB;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrG,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF"}
@@ -8,6 +8,5 @@ export declare class TimingMiddleware {
8
8
  static readonly HEADER_NAME = "Server-Timing";
9
9
  constructor(deps: TimingMiddlewareDependencies);
10
10
  measure(context: HasRequestHeader, action: () => void | Promise<void>): Promise<string | null>;
11
- private static isEventStream;
12
11
  }
13
12
  //# sourceMappingURL=timing.middleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"timing.middleware.d.ts","sourceRoot":"","sources":["../src/timing.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,MAAM,MAAM,4BAA4B,GAAG;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAEhE,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,MAAM,CAAC,QAAQ,CAAC,WAAW,mBAAmB;IAE9C,YAA6B,IAAI,EAAE,4BAA4B,EAAI;IAE7D,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnG;IAED,OAAO,CAAC,MAAM,CAAC,aAAa;CAQ7B"}
1
+ {"version":3,"file":"timing.middleware.d.ts","sourceRoot":"","sources":["../src/timing.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,MAAM,MAAM,4BAA4B,GAAG;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAEhE,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,MAAM,CAAC,QAAQ,CAAC,WAAW,mBAAmB;IAE9C,YAA6B,IAAI,EAAE,4BAA4B,EAAI;IAE7D,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnG;CACF"}
@@ -1,3 +1,4 @@
1
+ import { EventStreamAccept } from "./event-stream-accept.service";
1
2
  import { Stopwatch } from "./stopwatch.service";
2
3
  export class TimingMiddleware {
3
4
  deps;
@@ -6,19 +7,11 @@ export class TimingMiddleware {
6
7
  this.deps = deps;
7
8
  }
8
9
  async measure(context, action) {
9
- if (TimingMiddleware.isEventStream(context.request.header("accept")))
10
+ if (EventStreamAccept.matches(context.request.header("accept")))
10
11
  return null;
11
12
  const stopwatch = new Stopwatch(this.deps);
12
13
  await action();
13
14
  return `total;dur=${Math.max(0, stopwatch.stop().ms)}`;
14
15
  }
15
- static isEventStream(accept) {
16
- if (!accept)
17
- return false;
18
- return accept
19
- .toLowerCase()
20
- .split(",")
21
- .some((type) => type.split(";")[0]?.trim() === "text/event-stream");
22
- }
23
16
  }
24
17
  //# sourceMappingURL=timing.middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"timing.middleware.js","sourceRoot":"","sources":["../src/timing.middleware.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIhD,MAAM,OAAO,gBAAgB;IAGE,IAAI;IAFjC,MAAM,CAAU,WAAW,GAAG,eAAe,CAAC;IAE9C,YAA6B,IAAkC;oBAAlC,IAAI;IAAiC,CAAC;IAEnE,KAAK,CAAC,OAAO,CAAC,OAAyB,EAAE,MAAkC;QACzE,IAAI,gBAAgB,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAElF,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE3C,MAAM,MAAM,EAAE,CAAC;QAEf,OAAO,aAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;IAEO,MAAM,CAAC,aAAa,CAAC,MAA0B;QACrD,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAE1B,OAAO,MAAM;aACV,WAAW,EAAE;aACb,KAAK,CAAC,GAAG,CAAC;aACV,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,mBAAmB,CAAC,CAAC;IACxE,CAAC;CACF"}
1
+ {"version":3,"file":"timing.middleware.js","sourceRoot":"","sources":["../src/timing.middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIhD,MAAM,OAAO,gBAAgB;IAGE,IAAI;IAFjC,MAAM,CAAU,WAAW,GAAG,eAAe,CAAC;IAE9C,YAA6B,IAAkC;oBAAlC,IAAI;IAAiC,CAAC;IAEnE,KAAK,CAAC,OAAO,CAAC,OAAyB,EAAE,MAAkC;QACzE,IAAI,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAE7E,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE3C,MAAM,MAAM,EAAE,CAAC;QAEf,OAAO,aAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.23.11",
3
+ "version": "1.24.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Bartosz Gordon",
@@ -27,7 +27,7 @@
27
27
  "@types/bun": "1.4.2",
28
28
  "@types/nodemailer": "8.0.2",
29
29
  "better-auth": "1.7.6",
30
- "cspell": "10.3.3",
30
+ "cspell": "10.3.6",
31
31
  "fallow": "3.30.0",
32
32
  "lefthook": "2.1.14",
33
33
  "lockfile-lint": "5.0.1",
package/readme.md CHANGED
@@ -61,6 +61,10 @@ src/
61
61
  ├── antivirus.port.ts
62
62
  ├── api-version-hono.middleware.ts
63
63
  ├── api-version.middleware.ts
64
+ ├── atomic-file-writer-collecting.adapter.ts
65
+ ├── atomic-file-writer-noop.adapter.ts
66
+ ├── atomic-file-writer.adapter.ts
67
+ ├── atomic-file-writer.port.ts
64
68
  ├── auth-session-reader-better-auth.adapter.ts
65
69
  ├── auth-session-reader-noop.adapter.ts
66
70
  ├── auth-session-reader.port.ts
@@ -180,6 +184,7 @@ src/
180
184
  ├── event-store-with-logger.adapter.ts
181
185
  ├── event-store.adapter.ts
182
186
  ├── event-store.port.ts
187
+ ├── event-stream-accept.service.ts
183
188
  ├── event-stream.vo.ts
184
189
  ├── event-upcaster-chain.adapter.ts
185
190
  ├── event-upcaster-noop.adapter.ts
@@ -338,6 +343,10 @@ src/
338
343
  ├── job-queue-worker.service.ts
339
344
  ├── job-queue.adapter.ts
340
345
  ├── job-queue.port.ts
346
+ ├── job-recoverer-collecting.adapter.ts
347
+ ├── job-recoverer-noop.adapter.ts
348
+ ├── job-recoverer-sqlite.adapter.ts
349
+ ├── job-recoverer.port.ts
341
350
  ├── job-registry.adapter.ts
342
351
  ├── job-registry.port.ts
343
352
  ├── job-requeuer-collecting.adapter.ts
@@ -443,6 +452,7 @@ src/
443
452
  ├── nonce-provider-noop.adapter.ts
444
453
  ├── nonce-provider.port.ts
445
454
  ├── nonce-value.vo.ts
455
+ ├── path-matcher.service.ts
446
456
  ├── payload-serializer-collecting.adapter.ts
447
457
  ├── payload-serializer-json.adapter.ts
448
458
  ├── payload-serializer.port.ts
@@ -519,6 +529,9 @@ src/
519
529
  ├── retry-backoff-noop.strategy.ts
520
530
  ├── retry-backoff.strategy.ts
521
531
  ├── retry.service.ts
532
+ ├── runtime-stats-provider-noop.adapter.ts
533
+ ├── runtime-stats-provider-system.adapter.ts
534
+ ├── runtime-stats-provider.port.ts
522
535
  ├── sealer-aes-gcm.adapter.ts
523
536
  ├── sealer-noop.adapter.ts
524
537
  ├── sealer.port.ts
@@ -0,0 +1,14 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
3
+ import type { FileWriterContent } from "./file-writer.port";
4
+
5
+ export class AtomicFileWriterCollectingAdapter implements AtomicFileWriterPort {
6
+ readonly written: Array<[tools.FilePathRelative | tools.FilePathAbsolute, FileWriterContent]> = [];
7
+
8
+ async write(
9
+ path: tools.FilePathRelative | tools.FilePathAbsolute,
10
+ content: FileWriterContent,
11
+ ): Promise<void> {
12
+ this.written.push([path, content]);
13
+ }
14
+ }
@@ -0,0 +1,10 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
3
+ import type { FileWriterContent } from "./file-writer.port";
4
+
5
+ export class AtomicFileWriterNoopAdapter implements AtomicFileWriterPort {
6
+ async write(
7
+ _path: tools.FilePathRelative | tools.FilePathAbsolute,
8
+ _content: FileWriterContent,
9
+ ): Promise<void> {}
10
+ }
@@ -0,0 +1,34 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
3
+ import type { FileCleanerPort } from "./file-cleaner.port";
4
+ import type { FileRenamerPort } from "./file-renamer.port";
5
+ import type { FileWriterContent, FileWriterPort } from "./file-writer.port";
6
+ import type { NonceProviderPort } from "./nonce-provider.port";
7
+
8
+ type Dependencies = {
9
+ FileCleaner: FileCleanerPort;
10
+ FileRenamer: FileRenamerPort;
11
+ FileWriter: FileWriterPort;
12
+ NonceProvider: NonceProviderPort;
13
+ };
14
+
15
+ export class AtomicFileWriterAdapter implements AtomicFileWriterPort {
16
+ constructor(private readonly deps: Dependencies) {}
17
+
18
+ async write(
19
+ path: tools.FilePathRelative | tools.FilePathAbsolute,
20
+ content: FileWriterContent,
21
+ ): Promise<void> {
22
+ const temporary = path.withFilename(
23
+ path.getFilename().withSuffix(`-part-${this.deps.NonceProvider.generate()}`),
24
+ );
25
+
26
+ try {
27
+ await this.deps.FileWriter.write(temporary.get(), content);
28
+ await this.deps.FileRenamer.rename(temporary, path);
29
+ } catch (error) {
30
+ await this.deps.FileCleaner.delete(temporary).catch(() => {});
31
+ throw error;
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,6 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { FileWriterContent } from "./file-writer.port";
3
+
4
+ export interface AtomicFileWriterPort {
5
+ write(path: tools.FilePathRelative | tools.FilePathAbsolute, content: FileWriterContent): Promise<void>;
6
+ }
@@ -6,7 +6,10 @@ export const BuildInfo = v.object({
6
6
  timestamp: tools.TimestampValue,
7
7
  version: tools.PackageVersionSchema,
8
8
  sha: CommitShaValue,
9
- size: tools.SizeBytes,
9
+ sizes: v.object({
10
+ server: tools.SizeBytes,
11
+ web: v.object({ js: tools.SizeBytes, css: tools.SizeBytes }),
12
+ }),
10
13
  });
11
14
 
12
15
  export type BuildInfoType = v.InferOutput<typeof BuildInfo>;
@@ -1,15 +1,15 @@
1
1
  import type * as tools from "@bgord/tools";
2
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
2
3
  import { CryptoAesGcm } from "./crypto-aes-gcm.service";
3
4
  import type { CryptoKeyProviderPort } from "./crypto-key-provider.port";
4
5
  import type { EncryptionPort, EncryptionRecipe } from "./encryption.port";
5
6
  import { EncryptionIV } from "./encryption-iv.vo";
6
7
  import type { FileInspectionPort } from "./file-inspection.port";
7
8
  import type { FileReaderRawPort } from "./file-reader-raw.port";
8
- import type { FileWriterPort } from "./file-writer.port";
9
9
 
10
10
  type Dependencies = {
11
11
  FileReaderRaw: FileReaderRawPort;
12
- FileWriter: FileWriterPort;
12
+ AtomicFileWriter: AtomicFileWriterPort;
13
13
  CryptoKeyProvider: CryptoKeyProviderPort;
14
14
  FileInspection: FileInspectionPort;
15
15
  };
@@ -29,7 +29,7 @@ export class EncryptionAesGcmAdapter implements EncryptionPort {
29
29
  const plaintext = await this.deps.FileReaderRaw.read(recipe.input);
30
30
  const encrypted = await CryptoAesGcm.encrypt(key, plaintext, iv);
31
31
 
32
- await this.deps.FileWriter.write(recipe.output, encrypted);
32
+ await this.deps.AtomicFileWriter.write(recipe.output, encrypted);
33
33
 
34
34
  return recipe.output;
35
35
  }
@@ -43,7 +43,7 @@ export class EncryptionAesGcmAdapter implements EncryptionPort {
43
43
  const encrypted = new Uint8Array(await this.deps.FileReaderRaw.read(recipe.input));
44
44
  const plaintext = await CryptoAesGcm.decrypt(key, encrypted);
45
45
 
46
- await this.deps.FileWriter.write(recipe.output, plaintext);
46
+ await this.deps.AtomicFileWriter.write(recipe.output, plaintext);
47
47
 
48
48
  return recipe.output;
49
49
  }
@@ -0,0 +1,10 @@
1
+ export class EventStreamAccept {
2
+ static matches(accept: string | undefined): boolean {
3
+ if (!accept) return false;
4
+
5
+ return accept
6
+ .toLowerCase()
7
+ .split(",")
8
+ .some((type) => type.split(";")[0]?.trim() === "text/event-stream");
9
+ }
10
+ }
@@ -1,10 +1,10 @@
1
1
  // [BUN DEPENDENCY]
2
2
  import type * as tools from "@bgord/tools";
3
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
3
4
  import type { FileReaderRawPort } from "./file-reader-raw.port";
4
- import type { FileWriterPort } from "./file-writer.port";
5
5
  import type { GzipPort, GzipRecipe } from "./gzip.port";
6
6
 
7
- type Dependencies = { FileReaderRaw: FileReaderRawPort; FileWriter: FileWriterPort };
7
+ type Dependencies = { FileReaderRaw: FileReaderRawPort; AtomicFileWriter: AtomicFileWriterPort };
8
8
 
9
9
  export class GzipAdapter implements GzipPort {
10
10
  constructor(private readonly deps: Dependencies) {}
@@ -13,7 +13,7 @@ export class GzipAdapter implements GzipPort {
13
13
  const file = await this.deps.FileReaderRaw.read(recipe.input);
14
14
  const archive = Bun.gzipSync(file);
15
15
 
16
- await this.deps.FileWriter.write(recipe.output, archive);
16
+ await this.deps.AtomicFileWriter.write(recipe.output, archive);
17
17
 
18
18
  return recipe.output;
19
19
  }
@@ -3,12 +3,9 @@ import * as tools from "@bgord/tools";
3
3
  import type { BuildInfoType } from "./build-info.vo";
4
4
  import type { ClockPort } from "./clock.port";
5
5
  import type { CommitShaValueType } from "./commit-sha-value.vo";
6
- import { EventLoopLag } from "./event-loop-lag.service";
7
- import { EventLoopUtilization, type EventLoopUtilizationSnapshot } from "./event-loop-utilization.service";
8
- import { InFlightRequestsTracker } from "./in-flight-requests-tracker.service";
6
+ import type { EventLoopUtilizationSnapshot } from "./event-loop-utilization.service";
9
7
  import type { JobQueueStatsProviderPort, JobQueueStatsSnapshot } from "./job-queue-stats-provider.port";
10
8
  import type { LoggerStatsProviderPort, LoggerStatsSnapshot } from "./logger-stats-provider.port";
11
- import { MemoryConsumption } from "./memory-consumption.service";
12
9
  import type { NodeEnvironmentEnum } from "./node-env.vo";
13
10
  import { Prerequisite, type PrerequisiteLabelType } from "./prerequisite.vo";
14
11
  import {
@@ -18,8 +15,9 @@ import {
18
15
  import { PrerequisiteVerifierSelfAdapter } from "./prerequisite-verifier-self.adapter";
19
16
  import type { ReactiveConfigPort } from "./reactive-config.port";
20
17
  import type { RedactorStrategy } from "./redactor.strategy";
18
+ import type { RuntimeStatsProviderPort } from "./runtime-stats-provider.port";
21
19
  import { Stopwatch } from "./stopwatch.service";
22
- import { Uptime, type UptimeResultType } from "./uptime.service";
20
+ import type { UptimeResultType } from "./uptime.service";
23
21
 
24
22
  export enum HealthcheckStatusEnum {
25
23
  healthy = "healthy",
@@ -30,6 +28,7 @@ export enum HealthcheckStatusEnum {
30
28
  export type HealthcheckDependencies = {
31
29
  Clock: ClockPort;
32
30
  BuildInfoConfig: ReactiveConfigPort<BuildInfoType>;
31
+ RuntimeStatsProvider: RuntimeStatsProviderPort;
33
32
  LoggerStatsProvider?: LoggerStatsProviderPort;
34
33
  JobQueueStatsProvider?: JobQueueStatsProviderPort;
35
34
  };
@@ -56,7 +55,7 @@ export type HealthcheckResult = {
56
55
  timestamp: tools.TimestampValueType;
57
56
  date: string;
58
57
  sha: CommitShaValueType;
59
- size: string;
58
+ sizes: { server: string; web: { js: string; css: string } };
60
59
  environment: NodeEnvironmentEnum;
61
60
  };
62
61
  server: {
@@ -131,9 +130,7 @@ export class HealthcheckHandler {
131
130
  : HealthcheckStatusEnum.healthy;
132
131
 
133
132
  const build = await this.deps.BuildInfoConfig.get();
134
- const uptime = Uptime.get(this.deps.Clock);
135
- const histogram = EventLoopLag.snapshot();
136
- const memory = MemoryConsumption.snapshot();
133
+ const { uptime, memory, eventLoop, inFlight } = this.deps.RuntimeStatsProvider.getStats();
137
134
 
138
135
  return {
139
136
  status,
@@ -144,7 +141,13 @@ export class HealthcheckHandler {
144
141
  timestamp: build.timestamp,
145
142
  date: tools.Timestamp.fromNumber(build.timestamp).toInstant().toString(),
146
143
  sha: build.sha,
147
- size: tools.Size.fromBytes(build.size).format(tools.Size.unit.MB),
144
+ sizes: {
145
+ server: tools.Size.fromBytes(build.sizes.server).format(tools.Size.unit.MB),
146
+ web: {
147
+ js: tools.Size.fromBytes(build.sizes.web.js).format(tools.Size.unit.kB),
148
+ css: tools.Size.fromBytes(build.sizes.web.css).format(tools.Size.unit.kB),
149
+ },
150
+ },
148
151
  environment: this.config.Env,
149
152
  },
150
153
  server: {
@@ -167,10 +170,10 @@ export class HealthcheckHandler {
167
170
  },
168
171
  },
169
172
  eventLoop: {
170
- lag: { p50: histogram.p50.ms, p95: histogram.p95.ms, p99: histogram.p99.ms },
171
- utilization: EventLoopUtilization.snapshot(),
173
+ lag: { p50: eventLoop.lag.p50.ms, p95: eventLoop.lag.p95.ms, p99: eventLoop.lag.p99.ms },
174
+ utilization: eventLoop.utilization,
172
175
  },
173
- inFlight: InFlightRequestsTracker.get(),
176
+ inFlight,
174
177
  },
175
178
  logger: this.deps.LoggerStatsProvider?.getStats(),
176
179
  queue: await this.deps.JobQueueStatsProvider?.getStats(),
@@ -1,6 +1,6 @@
1
1
  import { FileUploaderError } from "./file-uploader.middleware";
2
+ import { RequestIdentityError } from "./request-context.port";
2
3
  import { ShieldApiKeyStrategyError } from "./shield-api-key.strategy";
3
- import { ShieldAuthStrategyError } from "./shield-auth.strategy";
4
4
  import { ShieldBasicAuthStrategyError } from "./shield-basic-auth.strategy";
5
5
  import { ShieldBodyLimitError } from "./shield-body-limit.strategy";
6
6
  import { ShieldCsrfStrategyError } from "./shield-csrf.strategy";
@@ -15,7 +15,7 @@ import { ShieldWebhookStrategyError } from "./shield-webhook.strategy";
15
15
 
16
16
  export const HttpExceptionErrors = {
17
17
  ShieldApiKeyRejected: ShieldApiKeyStrategyError.Rejected,
18
- ShieldAuthRejected: ShieldAuthStrategyError.Rejected,
18
+ ShieldAuthRejected: RequestIdentityError.Rejected,
19
19
  ShieldBasicAuthRejected: ShieldBasicAuthStrategyError.Rejected,
20
20
  ShieldBodyLimitTooBig: ShieldBodyLimitError.TooBig,
21
21
  ShieldCsrfRejected: ShieldCsrfStrategyError.Rejected,
@@ -20,28 +20,34 @@ export class HttpLoggerHonoMiddleware implements MiddlewareHonoPort {
20
20
 
21
21
  if (this.middleware.shouldSkip(context)) return await next();
22
22
 
23
- const request = c.req.raw.clone();
24
23
  const correlationId = c.get("correlationId");
25
- const body = await HttpLoggerHonoMiddleware.parseJSON(request);
24
+ const body = await HttpLoggerHonoMiddleware.parseJSON(c.req.raw);
26
25
 
27
26
  const { stopwatch } = this.middleware.before(context, correlationId, body);
28
27
 
29
28
  await next();
30
29
 
31
- const response = c.res.clone();
32
- const responseBody = await HttpLoggerHonoMiddleware.parseJSON(response);
30
+ const responseBody = await HttpLoggerHonoMiddleware.parseJSON(c.res);
33
31
 
34
32
  this.middleware.after(context, correlationId, {
35
33
  stopwatch,
36
- status: response.status,
34
+ status: c.res.status,
37
35
  responseBody,
38
36
  });
39
37
  };
40
38
  }
41
39
 
42
40
  private static async parseJSON(resource: Request | Response): Promise<any> {
41
+ if (!HttpLoggerHonoMiddleware.isJSON(resource.headers.get("content-type"))) return undefined;
42
+
43
43
  try {
44
- return await resource.json();
44
+ return await resource.clone().json();
45
45
  } catch {}
46
46
  }
47
+
48
+ private static isJSON(contentType: string | null): boolean {
49
+ const type = contentType?.split(";")[0]?.trim().toLowerCase();
50
+
51
+ return type === "application/json" || Boolean(type?.endsWith("+json"));
52
+ }
47
53
  }
@@ -1,6 +1,8 @@
1
1
  import type { ClockPort } from "./clock.port";
2
2
  import type { CorrelationIdType } from "./correlation-id.vo";
3
+ import { EventStreamAccept } from "./event-stream-accept.service";
3
4
  import { type LoggerPort, LogLevelEnum } from "./logger.port";
5
+ import { PathMatcher } from "./path-matcher.service";
4
6
  import type { HasRequestHeader, HasRequestPath, RequestContext } from "./request-context.port";
5
7
  import { Stopwatch } from "./stopwatch.service";
6
8
 
@@ -44,14 +46,9 @@ export class HttpLoggerMiddleware {
44
46
  ) {}
45
47
 
46
48
  shouldSkip(context: HasRequestPath & HasRequestHeader): boolean {
47
- if (context.request.header("accept")?.toLowerCase().startsWith("text/event-stream")) return true;
49
+ if (EventStreamAccept.matches(context.request.header("accept"))) return true;
48
50
 
49
- return (
50
- this.config?.skip?.some((rule) => {
51
- if (rule instanceof URLPattern) return rule.test({ pathname: context.request.path });
52
- return context.request.path.startsWith(rule);
53
- }) ?? false
54
- );
51
+ return PathMatcher.matches(this.config?.skip, context.request.path);
55
52
  }
56
53
 
57
54
  before(context: RequestContext, correlationId: CorrelationIdType, body: any = {}): HttpLoggerBeforeResult {
@@ -1,14 +1,10 @@
1
1
  // [BUN DEPENDENCY]
2
2
  import type * as tools from "@bgord/tools";
3
- import type { FileRenamerPort } from "./file-renamer.port";
4
- import type { FileWriterPort } from "./file-writer.port";
3
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
5
4
  import type { ImageBlurPort, ImageBlurStrategy } from "./image-blur.port";
6
- import type { NonceProviderPort } from "./nonce-provider.port";
7
5
 
8
6
  type Dependencies = {
9
- FileRenamer: FileRenamerPort;
10
- FileWriter: FileWriterPort;
11
- NonceProvider: NonceProviderPort;
7
+ AtomicFileWriter: AtomicFileWriterPort;
12
8
  };
13
9
 
14
10
  export class ImageBlurAdapter implements ImageBlurPort {
@@ -17,16 +13,10 @@ export class ImageBlurAdapter implements ImageBlurPort {
17
13
  async blur(recipe: ImageBlurStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
18
14
  const final = recipe.output;
19
15
 
20
- const filename = final.getFilename();
21
- const temporary = final.withFilename(
22
- filename.withSuffix(`-blurred-${this.deps.NonceProvider.generate()}`),
23
- );
24
-
25
16
  const blurred = await Bun.file(recipe.input.get()).image().placeholder();
26
17
  const bytes = Buffer.from(blurred.substring(blurred.indexOf(",") + 1), "base64");
27
18
 
28
- await this.deps.FileWriter.write(temporary.get(), bytes);
29
- await this.deps.FileRenamer.rename(temporary, final);
19
+ await this.deps.AtomicFileWriter.write(final, bytes);
30
20
 
31
21
  return final;
32
22
  }
@@ -1,15 +1,11 @@
1
1
  // [BUN DEPENDENCY]
2
2
  import type * as tools from "@bgord/tools";
3
- import type { FileRenamerPort } from "./file-renamer.port";
4
- import type { FileWriterPort } from "./file-writer.port";
3
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
5
4
  import type { ImageSupportedType } from "./image.types";
6
5
  import type { ImageCompressorPort, ImageCompressorStrategy } from "./image-compressor.port";
7
- import type { NonceProviderPort } from "./nonce-provider.port";
8
6
 
9
7
  type Dependencies = {
10
- FileRenamer: FileRenamerPort;
11
- FileWriter: FileWriterPort;
12
- NonceProvider: NonceProviderPort;
8
+ AtomicFileWriter: AtomicFileWriterPort;
13
9
  };
14
10
 
15
11
  export class ImageCompressorAdapter implements ImageCompressorPort {
@@ -21,18 +17,13 @@ export class ImageCompressorAdapter implements ImageCompressorPort {
21
17
  const quality = recipe.quality ?? ImageCompressorAdapter.DEFAULT_QUALITY;
22
18
 
23
19
  const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
24
- const filename = final.getFilename();
25
- const temporary = final.withFilename(
26
- filename.withSuffix(`-compressed-${this.deps.NonceProvider.generate()}`),
27
- );
28
20
 
29
21
  const extension = final.getFilename().getExtension();
30
22
  const format = (extension === "jpg" ? "jpeg" : extension) as ImageSupportedType;
31
23
 
32
24
  const compressed = await Bun.file(recipe.input.get()).image().rotate(0)[format]({ quality }).bytes();
33
25
 
34
- await this.deps.FileWriter.write(temporary.get(), compressed);
35
- await this.deps.FileRenamer.rename(temporary, final);
26
+ await this.deps.AtomicFileWriter.write(final, compressed);
36
27
 
37
28
  return final;
38
29
  }
@@ -1,16 +1,12 @@
1
1
  // [BUN DEPENDENCY]
2
2
  // cspell:ignore Exif
3
3
  import type * as tools from "@bgord/tools";
4
- import type { FileRenamerPort } from "./file-renamer.port";
5
- import type { FileWriterPort } from "./file-writer.port";
4
+ import type { AtomicFileWriterPort } from "./atomic-file-writer.port";
6
5
  import type { ImageSupportedType } from "./image.types";
7
6
  import type { ImageExifClearPort, ImageExifClearStrategy } from "./image-exif-clear.port";
8
- import type { NonceProviderPort } from "./nonce-provider.port";
9
7
 
10
8
  type Dependencies = {
11
- FileRenamer: FileRenamerPort;
12
- FileWriter: FileWriterPort;
13
- NonceProvider: NonceProviderPort;
9
+ AtomicFileWriter: AtomicFileWriterPort;
14
10
  };
15
11
 
16
12
  export class ImageExifClearAdapter implements ImageExifClearPort {
@@ -18,17 +14,13 @@ export class ImageExifClearAdapter implements ImageExifClearPort {
18
14
 
19
15
  async clear(recipe: ImageExifClearStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
20
16
  const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
21
- const temporary = final.withFilename(
22
- final.getFilename().withSuffix(`-exif-cleared-${this.deps.NonceProvider.generate()}`),
23
- );
24
17
 
25
18
  const extension = final.getFilename().getExtension();
26
19
  const format = (extension === "jpg" ? "jpeg" : extension) as ImageSupportedType;
27
20
 
28
21
  const cleared = await Bun.file(recipe.input.get()).image().rotate(0)[format]().bytes();
29
22
 
30
- await this.deps.FileWriter.write(temporary.get(), cleared);
31
- await this.deps.FileRenamer.rename(temporary, final);
23
+ await this.deps.AtomicFileWriter.write(final, cleared);
32
24
 
33
25
  return final;
34
26
  }