@bgord/bun 1.15.3 → 1.15.4

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 (118) hide show
  1. package/dist/cache-repository-redis.adapter.d.ts +13 -0
  2. package/dist/cache-repository-redis.adapter.d.ts.map +1 -0
  3. package/dist/cache-repository-redis.adapter.js +27 -0
  4. package/dist/cache-repository-redis.adapter.js.map +1 -0
  5. package/dist/healthcheck-hono.handler.d.ts +3 -2
  6. package/dist/healthcheck-hono.handler.d.ts.map +1 -1
  7. package/dist/healthcheck-hono.handler.js +1 -2
  8. package/dist/healthcheck-hono.handler.js.map +1 -1
  9. package/dist/healthcheck.handler.d.ts +12 -1
  10. package/dist/healthcheck.handler.d.ts.map +1 -1
  11. package/dist/healthcheck.handler.js +20 -2
  12. package/dist/healthcheck.handler.js.map +1 -1
  13. package/dist/image-grayscale-noop.adapter.d.ts +6 -0
  14. package/dist/image-grayscale-noop.adapter.d.ts.map +1 -0
  15. package/dist/image-grayscale-noop.adapter.js +6 -0
  16. package/dist/image-grayscale-noop.adapter.js.map +1 -0
  17. package/dist/image-grayscale-sharp.adapter.d.ts +19 -0
  18. package/dist/image-grayscale-sharp.adapter.d.ts.map +1 -0
  19. package/dist/image-grayscale-sharp.adapter.js +28 -0
  20. package/dist/image-grayscale-sharp.adapter.js.map +1 -0
  21. package/dist/image-grayscale.port.d.ts +15 -0
  22. package/dist/image-grayscale.port.d.ts.map +1 -0
  23. package/dist/image-grayscale.port.js +2 -0
  24. package/dist/image-grayscale.port.js.map +1 -0
  25. package/dist/index.d.ts +17 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +17 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/liveness-hono.handler.d.ts +8 -0
  30. package/dist/liveness-hono.handler.d.ts.map +1 -0
  31. package/dist/liveness-hono.handler.js +10 -0
  32. package/dist/liveness-hono.handler.js.map +1 -0
  33. package/dist/liveness.handler.d.ts +6 -0
  34. package/dist/liveness.handler.d.ts.map +1 -0
  35. package/dist/liveness.handler.js +6 -0
  36. package/dist/liveness.handler.js.map +1 -0
  37. package/dist/randomness-crypto.strategy.d.ts +5 -0
  38. package/dist/randomness-crypto.strategy.d.ts.map +1 -0
  39. package/dist/randomness-crypto.strategy.js +8 -0
  40. package/dist/randomness-crypto.strategy.js.map +1 -0
  41. package/dist/randomness-math.strategy.d.ts +5 -0
  42. package/dist/randomness-math.strategy.d.ts.map +1 -0
  43. package/dist/randomness-math.strategy.js +6 -0
  44. package/dist/randomness-math.strategy.js.map +1 -0
  45. package/dist/randomness-noop.strategy.d.ts +7 -0
  46. package/dist/randomness-noop.strategy.d.ts.map +1 -0
  47. package/dist/randomness-noop.strategy.js +10 -0
  48. package/dist/randomness-noop.strategy.js.map +1 -0
  49. package/dist/randomness.strategy.d.ts +4 -0
  50. package/dist/randomness.strategy.d.ts.map +1 -0
  51. package/dist/randomness.strategy.js +2 -0
  52. package/dist/randomness.strategy.js.map +1 -0
  53. package/dist/readiness-hono.handler.d.ts +26 -0
  54. package/dist/readiness-hono.handler.d.ts.map +1 -0
  55. package/dist/readiness-hono.handler.js +17 -0
  56. package/dist/readiness-hono.handler.js.map +1 -0
  57. package/dist/readiness.handler.d.ts +19 -0
  58. package/dist/readiness.handler.d.ts.map +1 -0
  59. package/dist/readiness.handler.js +19 -0
  60. package/dist/readiness.handler.js.map +1 -0
  61. package/dist/retry-backoff-jitter.strategy.d.ts +10 -0
  62. package/dist/retry-backoff-jitter.strategy.d.ts.map +1 -0
  63. package/dist/retry-backoff-jitter.strategy.js +14 -0
  64. package/dist/retry-backoff-jitter.strategy.js.map +1 -0
  65. package/dist/shield-ip-blacklist-hono.strategy.d.ts +11 -0
  66. package/dist/shield-ip-blacklist-hono.strategy.d.ts.map +1 -0
  67. package/dist/shield-ip-blacklist-hono.strategy.js +21 -0
  68. package/dist/shield-ip-blacklist-hono.strategy.js.map +1 -0
  69. package/dist/shield-ip-blacklist.strategy.d.ts +14 -0
  70. package/dist/shield-ip-blacklist.strategy.d.ts.map +1 -0
  71. package/dist/shield-ip-blacklist.strategy.js +16 -0
  72. package/dist/shield-ip-blacklist.strategy.js.map +1 -0
  73. package/dist/shield-ip-whitelist-hono.strategy.d.ts +11 -0
  74. package/dist/shield-ip-whitelist-hono.strategy.d.ts.map +1 -0
  75. package/dist/shield-ip-whitelist-hono.strategy.js +21 -0
  76. package/dist/shield-ip-whitelist-hono.strategy.js.map +1 -0
  77. package/dist/shield-ip-whitelist.strategy.d.ts +14 -0
  78. package/dist/shield-ip-whitelist.strategy.d.ts.map +1 -0
  79. package/dist/shield-ip-whitelist.strategy.js +16 -0
  80. package/dist/shield-ip-whitelist.strategy.js.map +1 -0
  81. package/dist/tsconfig.tsbuildinfo +1 -1
  82. package/package.json +9 -9
  83. package/readme.md +17 -3
  84. package/src/cache-repository-redis.adapter.ts +31 -0
  85. package/src/healthcheck-hono.handler.ts +1 -2
  86. package/src/healthcheck.handler.ts +28 -3
  87. package/src/image-grayscale-noop.adapter.ts +8 -0
  88. package/src/image-grayscale-sharp.adapter.ts +45 -0
  89. package/src/image-grayscale.port.ts +16 -0
  90. package/src/index.ts +17 -3
  91. package/src/liveness-hono.handler.ts +13 -0
  92. package/src/liveness.handler.ts +5 -0
  93. package/src/randomness-crypto.strategy.ts +9 -0
  94. package/src/randomness-math.strategy.ts +7 -0
  95. package/src/randomness-noop.strategy.ts +9 -0
  96. package/src/randomness.strategy.ts +3 -0
  97. package/src/readiness-hono.handler.ts +22 -0
  98. package/src/readiness.handler.ts +33 -0
  99. package/src/retry-backoff-jitter.strategy.ts +15 -0
  100. package/src/shield-ip-blacklist-hono.strategy.ts +30 -0
  101. package/src/shield-ip-blacklist.strategy.ts +18 -0
  102. package/src/shield-ip-whitelist-hono.strategy.ts +30 -0
  103. package/src/shield-ip-whitelist.strategy.ts +18 -0
  104. package/dist/ping-hono.handler.d.ts +0 -6
  105. package/dist/ping-hono.handler.d.ts.map +0 -1
  106. package/dist/ping-hono.handler.js +0 -13
  107. package/dist/ping-hono.handler.js.map +0 -1
  108. package/dist/ping.handler.d.ts +0 -4
  109. package/dist/ping.handler.d.ts.map +0 -1
  110. package/dist/ping.handler.js +0 -6
  111. package/dist/ping.handler.js.map +0 -1
  112. package/dist/retry-backoff-fibonacci.strategy.d.ts +0 -9
  113. package/dist/retry-backoff-fibonacci.strategy.d.ts.map +0 -1
  114. package/dist/retry-backoff-fibonacci.strategy.js +0 -19
  115. package/dist/retry-backoff-fibonacci.strategy.js.map +0 -1
  116. package/src/ping-hono.handler.ts +0 -17
  117. package/src/ping.handler.ts +0 -5
  118. package/src/retry-backoff-fibonacci.strategy.ts +0 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.15.3",
3
+ "version": "1.15.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -20,21 +20,21 @@
20
20
  "preinstall": "bunx only-allow bun"
21
21
  },
22
22
  "devDependencies": {
23
- "@biomejs/biome": "2.4.7",
23
+ "@biomejs/biome": "2.4.8",
24
24
  "@commitlint/cli": "20.5.0",
25
25
  "@commitlint/config-conventional": "20.5.0",
26
26
  "@standard-schema/spec": "1.1.0",
27
27
  "@stryker-mutator/core": "9.6.0",
28
- "@types/bun": "1.3.10",
28
+ "@types/bun": "1.3.11",
29
29
  "@types/nodemailer": "7.0.11",
30
30
  "@types/yazl": "3.3.0",
31
31
  "better-auth": "1.5.5",
32
32
  "cspell": "9.7.0",
33
- "csv": "6.5.0",
34
- "knip": "5.88.0",
33
+ "csv": "6.5.1",
34
+ "knip": "6.0.0",
35
35
  "lefthook": "2.1.4",
36
36
  "lockfile-lint": "5.0.0",
37
- "nodemailer": "8.0.2",
37
+ "nodemailer": "8.0.3",
38
38
  "only-allow": "1.2.2",
39
39
  "resend": "6.9.4",
40
40
  "sharp": "0.34.5",
@@ -43,15 +43,15 @@
43
43
  "yazl": "3.3.1"
44
44
  },
45
45
  "dependencies": {
46
- "@bgord/tools": "1.4.2",
46
+ "@bgord/tools": "1.4.3",
47
47
  "emittery": "2.0.0",
48
48
  "hono": "4.12.8",
49
49
  "node-cache": "5.1.2"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "better-auth": "1.5.5",
53
- "csv": "6.5.0",
54
- "nodemailer": "8.0.2",
53
+ "csv": "6.5.1",
54
+ "nodemailer": "8.0.3",
55
55
  "resend": "6.9.4",
56
56
  "sharp": "0.34.5",
57
57
  "yazl": "3.3.1"
package/readme.md CHANGED
@@ -57,6 +57,7 @@ src/
57
57
  ├── cache-file.service.ts
58
58
  ├── cache-repository-node-cache.adapter.ts
59
59
  ├── cache-repository-noop.adapter.ts
60
+ ├── cache-repository-redis.adapter.ts
60
61
  ├── cache-repository.port.ts
61
62
  ├── cache-resolver-simple.strategy.ts
62
63
  ├── cache-resolver.strategy.ts
@@ -209,6 +210,9 @@ src/
209
210
  ├── image-formatter-noop.adapter.ts
210
211
  ├── image-formatter-sharp.adapter.ts
211
212
  ├── image-formatter.port.ts
213
+ ├── image-grayscale-noop.adapter.ts
214
+ ├── image-grayscale-sharp.adapter.ts
215
+ ├── image-grayscale.port.ts
212
216
  ├── image-info-noop.adapter.ts
213
217
  ├── image-info-sharp.adapter.ts
214
218
  ├── image-info.port.ts
@@ -237,6 +241,8 @@ src/
237
241
  ├── language-detector.middleware.ts
238
242
  ├── language-detector.strategy.ts
239
243
  ├── languages.vo.ts
244
+ ├── liveness-hono.handler.ts
245
+ ├── liveness.handler.ts
240
246
  ├── logger-collecting.adapter.ts
241
247
  ├── logger-noop.adapter.ts
242
248
  ├── logger-stats-provider-noop.adapter.ts
@@ -319,8 +325,6 @@ src/
319
325
  ├── pdf-generator-noop.adapter.ts
320
326
  ├── pdf-generator-with-logger.adapter.ts
321
327
  ├── pdf-generator.port.ts
322
- ├── ping-hono.handler.ts
323
- ├── ping.handler.ts
324
328
  ├── port.vo.ts
325
329
  ├── prerequisite-runner-startup.service.ts
326
330
  ├── prerequisite-verifier-binary.adapter.ts
@@ -353,6 +357,12 @@ src/
353
357
  ├── prerequisite-verifier.decorator.ts
354
358
  ├── prerequisite-verifier.port.ts
355
359
  ├── prerequisite.vo.ts
360
+ ├── randomness-crypto.strategy.ts
361
+ ├── randomness-math.strategy.ts
362
+ ├── randomness-noop.strategy.ts
363
+ ├── randomness.strategy.ts
364
+ ├── readiness-hono.handler.ts
365
+ ├── readiness.handler.ts
356
366
  ├── recaptcha-secret-key.vo.ts
357
367
  ├── recaptcha-site-key.vo.ts
358
368
  ├── redactor-composite.strategy.ts
@@ -369,7 +379,7 @@ src/
369
379
  ├── request-context-hono.adapter.ts
370
380
  ├── request-context.port.ts
371
381
  ├── retry-backoff-exponential.strategy.ts
372
- ├── retry-backoff-fibonacci.strategy.ts
382
+ ├── retry-backoff-jitter.strategy.ts
373
383
  ├── retry-backoff-linear.strategy.ts
374
384
  ├── retry-backoff-noop.strategy.ts
375
385
  ├── retry-backoff.strategy.ts
@@ -413,6 +423,10 @@ src/
413
423
  ├── shield-hcaptcha-hono-local.strategy.ts
414
424
  ├── shield-hcaptcha-hono.strategy.ts
415
425
  ├── shield-hcaptcha.strategy.ts
426
+ ├── shield-ip-blacklist-hono.strategy.ts
427
+ ├── shield-ip-blacklist.strategy.ts
428
+ ├── shield-ip-whitelist-hono.strategy.ts
429
+ ├── shield-ip-whitelist.strategy.ts
416
430
  ├── shield-maintenance-hono.strategy.ts
417
431
  ├── shield-maintenance.strategy.ts
418
432
  ├── shield-rate-limit-hono.strategy.ts
@@ -0,0 +1,31 @@
1
+ import type { RedisClient } from "bun";
2
+ import type { CacheRepositoryPort, CacheRepositoryTtlType } from "./cache-repository.port";
3
+ import type { Hash } from "./hash.vo";
4
+
5
+ export class CacheRepositoryRedisAdapter implements CacheRepositoryPort {
6
+ constructor(
7
+ private readonly client: RedisClient,
8
+ private readonly config: CacheRepositoryTtlType,
9
+ ) {}
10
+
11
+ async get<T>(subject: Hash): Promise<T | null> {
12
+ const value = await this.client.get(subject.get());
13
+ if (value === null) return null;
14
+ return JSON.parse(value) as T;
15
+ }
16
+
17
+ async set<T>(subject: Hash, value: T): Promise<void> {
18
+ await this.client.set(subject.get(), JSON.stringify(value));
19
+ if (this.config.type === "finite") {
20
+ await this.client.expire(subject.get(), this.config.ttl.seconds);
21
+ }
22
+ }
23
+
24
+ async delete(subject: Hash): Promise<void> {
25
+ await this.client.del(subject.get());
26
+ }
27
+
28
+ async flush(): Promise<void> {
29
+ await this.client.send("FLUSHALL", []);
30
+ }
31
+ }
@@ -23,9 +23,8 @@ export class HealthcheckHonoHandler implements HandlerHonoPort {
23
23
  handle() {
24
24
  return factory.createHandlers(async (c) => {
25
25
  const healthcheck = await this.handler.check();
26
- const code = healthcheck.ok ? 200 : 424;
27
26
 
28
- return c.json(healthcheck, code);
27
+ return c.json(healthcheck, healthcheck.code);
29
28
  });
30
29
  }
31
30
  }
@@ -19,6 +19,12 @@ import type { RedactorStrategy } from "./redactor.strategy";
19
19
  import { Stopwatch } from "./stopwatch.service";
20
20
  import { Uptime, type UptimeResultType } from "./uptime.service";
21
21
 
22
+ export enum HealthcheckStatusEnum {
23
+ healthy = "healthy",
24
+ degraded = "degraded",
25
+ unhealthy = "unhealthy",
26
+ }
27
+
22
28
  type Dependencies = {
23
29
  Clock: ClockPort;
24
30
  BuildInfoRepository: BuildInfoRepositoryStrategy;
@@ -33,8 +39,15 @@ export type HealthcheckConfig = {
33
39
 
34
40
  const self = new Prerequisite("self", new PrerequisiteVerifierSelfAdapter());
35
41
 
42
+ export const HealthcheckStatusCode = {
43
+ [HealthcheckStatusEnum.healthy]: 200,
44
+ [HealthcheckStatusEnum.degraded]: 207,
45
+ [HealthcheckStatusEnum.unhealthy]: 424,
46
+ } as const;
47
+
36
48
  export type HealthcheckResult = {
37
- ok: boolean;
49
+ status: HealthcheckStatusEnum;
50
+ code: (typeof HealthcheckStatusCode)[keyof typeof HealthcheckStatusCode];
38
51
  deployment: {
39
52
  version: string;
40
53
  timestamp: tools.TimestampValueType;
@@ -99,7 +112,18 @@ export class HealthcheckHandler {
99
112
  }),
100
113
  );
101
114
 
102
- const ok = details.every((result) => result.outcome.outcome !== PrerequisiteVerificationOutcome.failure);
115
+ const hasFailure = details.some(
116
+ (result) => result.outcome.outcome === PrerequisiteVerificationOutcome.failure,
117
+ );
118
+ const hasUndetermined = details.some(
119
+ (result) => result.outcome.outcome === PrerequisiteVerificationOutcome.undetermined,
120
+ );
121
+
122
+ const status = hasFailure
123
+ ? HealthcheckStatusEnum.unhealthy
124
+ : hasUndetermined
125
+ ? HealthcheckStatusEnum.degraded
126
+ : HealthcheckStatusEnum.healthy;
103
127
 
104
128
  const build = await this.deps.BuildInfoRepository.extract();
105
129
  const uptime = Uptime.get(this.deps.Clock);
@@ -107,7 +131,8 @@ export class HealthcheckHandler {
107
131
  const memory = MemoryConsumption.snapshot();
108
132
 
109
133
  return {
110
- ok,
134
+ status,
135
+ code: HealthcheckStatusCode[status],
111
136
  details,
112
137
  deployment: {
113
138
  version: build.version.toString(),
@@ -0,0 +1,8 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { ImageGrayscalePort, ImageGrayscaleStrategy } from "./image-grayscale.port";
3
+
4
+ export class ImageGrayscaleNoopAdapter implements ImageGrayscalePort {
5
+ async grayscale(recipe: ImageGrayscaleStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
6
+ return recipe.strategy === "output_path" ? recipe.output : recipe.input;
7
+ }
8
+ }
@@ -0,0 +1,45 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
4
+ import type { FileRenamerPort } from "./file-renamer.port";
5
+ import type { ImageGrayscalePort, ImageGrayscaleStrategy } from "./image-grayscale.port";
6
+
7
+ export const ImageGrayscaleSharpAdapterError = {
8
+ MissingDependency: "image.grayscale.sharp.adapter.error.missing.dependency",
9
+ };
10
+
11
+ type Dependencies = { FileRenamer: FileRenamerPort };
12
+ type Sharp = typeof sharp;
13
+
14
+ export class ImageGrayscaleSharpAdapter implements ImageGrayscalePort {
15
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
+ "sharp",
17
+ ImageGrayscaleSharpAdapterError.MissingDependency,
18
+ );
19
+
20
+ private constructor(
21
+ private readonly sharp: Sharp,
22
+ private readonly deps: Dependencies,
23
+ ) {}
24
+
25
+ static async build(deps: Dependencies): Promise<ImageGrayscaleSharpAdapter> {
26
+ const library = await ImageGrayscaleSharpAdapter.importer.resolve();
27
+
28
+ return new ImageGrayscaleSharpAdapter(library.default, deps);
29
+ }
30
+
31
+ async grayscale(recipe: ImageGrayscaleStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
32
+ const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
33
+
34
+ const filename = final.getFilename();
35
+ const temporary = final.withFilename(filename.withSuffix("-grayscale"));
36
+
37
+ const pipeline = this.sharp(recipe.input.get());
38
+ using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
39
+
40
+ await pipeline.rotate().grayscale().toFile(temporary.get());
41
+ await this.deps.FileRenamer.rename(temporary, final);
42
+
43
+ return final;
44
+ }
45
+ }
@@ -0,0 +1,16 @@
1
+ import type * as tools from "@bgord/tools";
2
+
3
+ export type ImageGrayscaleOutputPathStrategy = {
4
+ strategy: "output_path";
5
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
6
+ output: tools.FilePathRelative | tools.FilePathAbsolute;
7
+ };
8
+ export type ImageGrayscaleInPlaceStrategy = {
9
+ strategy: "in_place";
10
+ input: tools.FilePathRelative | tools.FilePathAbsolute;
11
+ };
12
+ export type ImageGrayscaleStrategy = ImageGrayscaleInPlaceStrategy | ImageGrayscaleOutputPathStrategy;
13
+
14
+ export interface ImageGrayscalePort {
15
+ grayscale(recipe: ImageGrayscaleStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute>;
16
+ }
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ export * from "./cache-file.service";
32
32
  export * from "./cache-repository.port";
33
33
  export * from "./cache-repository-node-cache.adapter";
34
34
  export * from "./cache-repository-noop.adapter";
35
+ export * from "./cache-repository-redis.adapter";
35
36
  export * from "./cache-resolver.strategy";
36
37
  export * from "./cache-resolver-simple.strategy";
37
38
  export * from "./cache-response.middleware";
@@ -182,6 +183,9 @@ export * from "./image-exif-clear-sharp.adapter";
182
183
  export * from "./image-formatter.port";
183
184
  export * from "./image-formatter-noop.adapter";
184
185
  export * from "./image-formatter-sharp.adapter";
186
+ export * from "./image-grayscale.port";
187
+ export * from "./image-grayscale-noop.adapter";
188
+ export * from "./image-grayscale-sharp.adapter";
185
189
  export * from "./image-info.port";
186
190
  export * from "./image-info-noop.adapter";
187
191
  export * from "./image-info-sharp.adapter";
@@ -208,6 +212,8 @@ export * from "./language-detector-header.strategy";
208
212
  export * from "./language-detector-hono.middleware";
209
213
  export * from "./language-detector-query.strategy";
210
214
  export * from "./languages.vo";
215
+ export * from "./liveness.handler";
216
+ export * from "./liveness-hono.handler";
211
217
  export * from "./logger.port";
212
218
  export * from "./logger-collecting.adapter";
213
219
  export * from "./logger-noop.adapter";
@@ -253,8 +259,6 @@ export * from "./nonce-value.vo";
253
259
  export * from "./pdf-generator.port";
254
260
  export * from "./pdf-generator-noop.adapter";
255
261
  export * from "./pdf-generator-with-logger.adapter";
256
- export * from "./ping.handler";
257
- export * from "./ping-hono.handler";
258
262
  export * from "./port.vo";
259
263
  export * from "./prerequisite.vo";
260
264
  export * from "./prerequisite-runner-startup.service";
@@ -282,6 +286,12 @@ export * from "./prerequisite-verifier-sqlite.adapter";
282
286
  export * from "./prerequisite-verifier-ssl-certificate-expiry.adapter";
283
287
  export * from "./prerequisite-verifier-timezone-utc.adapter";
284
288
  export * from "./prerequisite-verifier-translations.adapter";
289
+ export * from "./randomness.strategy";
290
+ export * from "./randomness-crypto.strategy";
291
+ export * from "./randomness-math.strategy";
292
+ export * from "./randomness-noop.strategy";
293
+ export * from "./readiness.handler";
294
+ export * from "./readiness-hono.handler";
285
295
  export * from "./recaptcha-secret-key.vo";
286
296
  export * from "./recaptcha-site-key.vo";
287
297
  export * from "./redactor.strategy";
@@ -300,7 +310,7 @@ export * from "./request-context-hono.adapter";
300
310
  export * from "./retry.service";
301
311
  export * from "./retry-backoff.strategy";
302
312
  export * from "./retry-backoff-exponential.strategy";
303
- export * from "./retry-backoff-fibonacci.strategy";
313
+ export * from "./retry-backoff-jitter.strategy";
304
314
  export * from "./retry-backoff-linear.strategy";
305
315
  export * from "./retry-backoff-noop.strategy";
306
316
  export * from "./sealer.port";
@@ -342,6 +352,10 @@ export * from "./shield-csrf-hono.strategy";
342
352
  export * from "./shield-hcaptcha.strategy";
343
353
  export * from "./shield-hcaptcha-hono.strategy";
344
354
  export * from "./shield-hcaptcha-hono-local.strategy";
355
+ export * from "./shield-ip-blacklist.strategy";
356
+ export * from "./shield-ip-blacklist-hono.strategy";
357
+ export * from "./shield-ip-whitelist.strategy";
358
+ export * from "./shield-ip-whitelist-hono.strategy";
345
359
  export * from "./shield-maintenance.strategy";
346
360
  export * from "./shield-maintenance-hono.strategy";
347
361
  export * from "./shield-rate-limit.strategy";
@@ -0,0 +1,13 @@
1
+ import { createFactory } from "hono/factory";
2
+ import type { HandlerHonoPort } from "./handler-hono.port";
3
+ import { LivenessHandler } from "./liveness.handler";
4
+
5
+ const factory = createFactory();
6
+
7
+ export class LivenessHonoHandler implements HandlerHonoPort {
8
+ private readonly handler = new LivenessHandler();
9
+
10
+ handle() {
11
+ return factory.createHandlers(async (c) => c.json(this.handler.execute(), 200));
12
+ }
13
+ }
@@ -0,0 +1,5 @@
1
+ export class LivenessHandler {
2
+ execute(): { ok: true } {
3
+ return { ok: true };
4
+ }
5
+ }
@@ -0,0 +1,9 @@
1
+ import type { RandomnessStrategy } from "./randomness.strategy";
2
+
3
+ export class RandomnessCryptoStrategy implements RandomnessStrategy {
4
+ next(): number {
5
+ const array = new Uint32Array(1);
6
+ crypto.getRandomValues(array);
7
+ return array[0]! / 0xffffffff;
8
+ }
9
+ }
@@ -0,0 +1,7 @@
1
+ import type { RandomnessStrategy } from "./randomness.strategy";
2
+
3
+ export class RandomnessMathStrategy implements RandomnessStrategy {
4
+ next(): number {
5
+ return Math.random();
6
+ }
7
+ }
@@ -0,0 +1,9 @@
1
+ import type { RandomnessStrategy } from "./randomness.strategy";
2
+
3
+ export class RandomnessNoopStrategy implements RandomnessStrategy {
4
+ constructor(private readonly value: number) {}
5
+
6
+ next(): number {
7
+ return this.value;
8
+ }
9
+ }
@@ -0,0 +1,3 @@
1
+ export interface RandomnessStrategy {
2
+ next(): number;
3
+ }
@@ -0,0 +1,22 @@
1
+ import { createFactory } from "hono/factory";
2
+ import type { HandlerHonoPort } from "./handler-hono.port";
3
+ import { type ReadinessConfig, ReadinessHandler } from "./readiness.handler";
4
+
5
+ const factory = createFactory();
6
+
7
+ export class ReadinessHonoHandler implements HandlerHonoPort {
8
+ private readonly handler: ReadinessHandler;
9
+
10
+ constructor(config: ReadinessConfig) {
11
+ this.handler = new ReadinessHandler(config);
12
+ }
13
+
14
+ handle() {
15
+ return factory.createHandlers(async (c) => {
16
+ const readiness = await this.handler.check();
17
+ const code = readiness.ok ? 200 : 503;
18
+
19
+ return c.json(readiness, code);
20
+ });
21
+ }
22
+ }
@@ -0,0 +1,33 @@
1
+ import type { Prerequisite, PrerequisiteLabelType } from "./prerequisite.vo";
2
+ import {
3
+ PrerequisiteVerificationOutcome,
4
+ type PrerequisiteVerificationResult,
5
+ } from "./prerequisite-verifier.port";
6
+
7
+ export type ReadinessConfig = { prerequisites: ReadonlyArray<Prerequisite> };
8
+
9
+ type ReadinessResult = {
10
+ ok: boolean;
11
+ details: ReadonlyArray<{ label: PrerequisiteLabelType; outcome: PrerequisiteVerificationResult }>;
12
+ };
13
+
14
+ export class ReadinessHandler {
15
+ constructor(private readonly config: ReadinessConfig) {}
16
+
17
+ async check(): Promise<ReadinessResult> {
18
+ const prerequisites = this.config.prerequisites
19
+ .filter((prerequisite) => prerequisite.enabled)
20
+ .filter((prerequisite) => prerequisite.kind !== "port");
21
+
22
+ const details = await Promise.all(
23
+ prerequisites.map(async (prerequisite) => ({
24
+ label: prerequisite.label,
25
+ outcome: await prerequisite.build().verify(),
26
+ })),
27
+ );
28
+
29
+ const ok = details.every((detail) => detail.outcome.outcome !== PrerequisiteVerificationOutcome.failure);
30
+
31
+ return { ok, details };
32
+ }
33
+ }
@@ -0,0 +1,15 @@
1
+ import * as tools from "@bgord/tools";
2
+ import * as v from "valibot";
3
+ import type { RandomnessStrategy } from "./randomness.strategy";
4
+ import type { RetryBackoffStrategy } from "./retry-backoff.strategy";
5
+
6
+ export class RetryBackoffJitterStrategy implements RetryBackoffStrategy {
7
+ constructor(
8
+ private readonly delay: tools.Duration,
9
+ private readonly randomness: RandomnessStrategy,
10
+ ) {}
11
+
12
+ next(attempt: tools.IntegerPositiveType): tools.Duration {
13
+ return this.delay.times(v.parse(tools.MultiplicationFactor, attempt * this.randomness.next()));
14
+ }
15
+ }
@@ -0,0 +1,30 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import type { MiddlewareHonoPort } from "./middleware-hono.port";
4
+ import { RequestContextHonoAdapter } from "./request-context-hono.adapter";
5
+ import {
6
+ type ShieldIpBlacklistConfig,
7
+ ShieldIpBlacklistStrategy,
8
+ ShieldIpBlacklistStrategyError,
9
+ } from "./shield-ip-blacklist.strategy";
10
+
11
+ export const ShieldIpBlacklistError = new HTTPException(403, {
12
+ message: ShieldIpBlacklistStrategyError.Rejected,
13
+ });
14
+
15
+ export class ShieldIpBlacklistHonoStrategy implements MiddlewareHonoPort {
16
+ private readonly strategy: ShieldIpBlacklistStrategy;
17
+
18
+ constructor(config: ShieldIpBlacklistConfig) {
19
+ this.strategy = new ShieldIpBlacklistStrategy(config);
20
+ }
21
+
22
+ handle(): MiddlewareHandler {
23
+ return async (c, next) => {
24
+ const context = new RequestContextHonoAdapter(c);
25
+
26
+ if (this.strategy.evaluate(context)) return next();
27
+ throw ShieldIpBlacklistError;
28
+ };
29
+ }
30
+ }
@@ -0,0 +1,18 @@
1
+ import * as v from "valibot";
2
+ import { ClientIp, type ClientIpType } from "./client-ip.vo";
3
+ import type { HasIdentityIp } from "./request-context.port";
4
+
5
+ export type ShieldIpBlacklistConfig = { blacklist: Array<ClientIpType> };
6
+
7
+ export const ShieldIpBlacklistStrategyError = { Rejected: "shield.ip.blacklist.rejected" };
8
+
9
+ export class ShieldIpBlacklistStrategy {
10
+ constructor(private readonly config: ShieldIpBlacklistConfig) {}
11
+
12
+ evaluate(context: HasIdentityIp): boolean {
13
+ const ip = v.safeParse(ClientIp, context.identity.ip());
14
+
15
+ if (!ip.success) return false;
16
+ return !this.config.blacklist.includes(ip.output);
17
+ }
18
+ }
@@ -0,0 +1,30 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import type { MiddlewareHonoPort } from "./middleware-hono.port";
4
+ import { RequestContextHonoAdapter } from "./request-context-hono.adapter";
5
+ import {
6
+ type ShieldIpWhitelistConfig,
7
+ ShieldIpWhitelistStrategy,
8
+ ShieldIpWhitelistStrategyError,
9
+ } from "./shield-ip-whitelist.strategy";
10
+
11
+ export const ShieldIpWhitelistError = new HTTPException(403, {
12
+ message: ShieldIpWhitelistStrategyError.Rejected,
13
+ });
14
+
15
+ export class ShieldIpWhitelistHonoStrategy implements MiddlewareHonoPort {
16
+ private readonly strategy: ShieldIpWhitelistStrategy;
17
+
18
+ constructor(config: ShieldIpWhitelistConfig) {
19
+ this.strategy = new ShieldIpWhitelistStrategy(config);
20
+ }
21
+
22
+ handle(): MiddlewareHandler {
23
+ return async (c, next) => {
24
+ const context = new RequestContextHonoAdapter(c);
25
+
26
+ if (this.strategy.evaluate(context)) return next();
27
+ throw ShieldIpWhitelistError;
28
+ };
29
+ }
30
+ }
@@ -0,0 +1,18 @@
1
+ import * as v from "valibot";
2
+ import { ClientIp, type ClientIpType } from "./client-ip.vo";
3
+ import type { HasIdentityIp } from "./request-context.port";
4
+
5
+ export type ShieldIpWhitelistConfig = { whitelist: Array<ClientIpType> };
6
+
7
+ export const ShieldIpWhitelistStrategyError = { Rejected: "shield.ip.whitelist.rejected" };
8
+
9
+ export class ShieldIpWhitelistStrategy {
10
+ constructor(private readonly config: ShieldIpWhitelistConfig) {}
11
+
12
+ evaluate(context: HasIdentityIp): boolean {
13
+ const ip = v.safeParse(ClientIp, context.identity.ip());
14
+
15
+ if (!ip.success) return false;
16
+ return this.config.whitelist.includes(ip.output);
17
+ }
18
+ }
@@ -1,6 +0,0 @@
1
- import type { HandlerHonoPort } from "./handler-hono.port";
2
- export declare class PingHonoHandler implements HandlerHonoPort {
3
- private readonly handler;
4
- handle(): [import("hono/types").H<import("hono").Env, string, {}, Promise<Response & import("hono").TypedResponse<"pong", 200, "text">>>];
5
- }
6
- //# sourceMappingURL=ping-hono.handler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping-hono.handler.d.ts","sourceRoot":"","sources":["../src/ping-hono.handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAK3D,qBAAa,eAAgB,YAAW,eAAe;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,MAAM;CAOP"}
@@ -1,13 +0,0 @@
1
- import { createFactory } from "hono/factory";
2
- import { PingHandler } from "./ping.handler";
3
- const factory = createFactory();
4
- export class PingHonoHandler {
5
- handler = new PingHandler();
6
- handle() {
7
- return factory.createHandlers(async (c) => {
8
- const result = this.handler.execute();
9
- return c.text(result, 200);
10
- });
11
- }
12
- }
13
- //# sourceMappingURL=ping-hono.handler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping-hono.handler.js","sourceRoot":"","sources":["../src/ping-hono.handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAEhC,MAAM,OAAO,eAAe;IACT,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAE7C,MAAM;QACJ,OAAO,OAAO,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACxC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAEtC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -1,4 +0,0 @@
1
- export declare class PingHandler {
2
- execute(): "pong";
3
- }
4
- //# sourceMappingURL=ping.handler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping.handler.d.ts","sourceRoot":"","sources":["../src/ping.handler.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAW;IACtB,OAAO,IAAI,MAAM;CAGlB"}
@@ -1,6 +0,0 @@
1
- export class PingHandler {
2
- execute() {
3
- return "pong";
4
- }
5
- }
6
- //# sourceMappingURL=ping.handler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ping.handler.js","sourceRoot":"","sources":["../src/ping.handler.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAW;IACtB,OAAO;QACL,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -1,9 +0,0 @@
1
- import * as tools from "@bgord/tools";
2
- import type { RetryBackoffStrategy } from "./retry-backoff.strategy";
3
- export declare class RetryBackoffFibonacciStrategy implements RetryBackoffStrategy {
4
- private readonly delay;
5
- constructor(delay: tools.Duration);
6
- next(attempt: tools.IntegerPositiveType): tools.Duration;
7
- private fibonacci;
8
- }
9
- //# sourceMappingURL=retry-backoff-fibonacci.strategy.d.ts.map