@bgord/bun 1.15.4 → 1.15.5

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 (108) hide show
  1. package/dist/alert-channel-collecting.adapter.d.ts +8 -0
  2. package/dist/alert-channel-collecting.adapter.d.ts.map +1 -0
  3. package/dist/alert-channel-collecting.adapter.js +10 -0
  4. package/dist/alert-channel-collecting.adapter.js.map +1 -0
  5. package/dist/alert-channel-composite.adapter.d.ts +13 -0
  6. package/dist/alert-channel-composite.adapter.d.ts.map +1 -0
  7. package/dist/alert-channel-composite.adapter.js +22 -0
  8. package/dist/alert-channel-composite.adapter.js.map +1 -0
  9. package/dist/alert-channel-mailer.adapter.d.ts +18 -0
  10. package/dist/alert-channel-mailer.adapter.d.ts.map +1 -0
  11. package/dist/alert-channel-mailer.adapter.js +15 -0
  12. package/dist/alert-channel-mailer.adapter.js.map +1 -0
  13. package/dist/alert-channel-noop.adapter.d.ts +7 -0
  14. package/dist/alert-channel-noop.adapter.d.ts.map +1 -0
  15. package/dist/alert-channel-noop.adapter.js +7 -0
  16. package/dist/alert-channel-noop.adapter.js.map +1 -0
  17. package/dist/alert-channel-sms.adapter.d.ts +18 -0
  18. package/dist/alert-channel-sms.adapter.d.ts.map +1 -0
  19. package/dist/alert-channel-sms.adapter.js +15 -0
  20. package/dist/alert-channel-sms.adapter.js.map +1 -0
  21. package/dist/alert-channel-with-logger.adapter.d.ts +17 -0
  22. package/dist/alert-channel-with-logger.adapter.d.ts.map +1 -0
  23. package/dist/alert-channel-with-logger.adapter.js +33 -0
  24. package/dist/alert-channel-with-logger.adapter.js.map +1 -0
  25. package/dist/alert-channel-with-retry.adapter.d.ts +19 -0
  26. package/dist/alert-channel-with-retry.adapter.d.ts.map +1 -0
  27. package/dist/alert-channel-with-retry.adapter.js +16 -0
  28. package/dist/alert-channel-with-retry.adapter.js.map +1 -0
  29. package/dist/alert-channel-with-timeout.adapter.d.ts +19 -0
  30. package/dist/alert-channel-with-timeout.adapter.d.ts.map +1 -0
  31. package/dist/alert-channel-with-timeout.adapter.js +15 -0
  32. package/dist/alert-channel-with-timeout.adapter.js.map +1 -0
  33. package/dist/alert-channel.builder.d.ts +14 -0
  34. package/dist/alert-channel.builder.d.ts.map +1 -0
  35. package/dist/alert-channel.builder.js +28 -0
  36. package/dist/alert-channel.builder.js.map +1 -0
  37. package/dist/alert-channel.port.d.ts +6 -0
  38. package/dist/alert-channel.port.d.ts.map +1 -0
  39. package/dist/alert-channel.port.js +2 -0
  40. package/dist/alert-channel.port.js.map +1 -0
  41. package/dist/alert-message.vo.d.ts +10 -0
  42. package/dist/alert-message.vo.d.ts.map +1 -0
  43. package/dist/alert-message.vo.js +12 -0
  44. package/dist/alert-message.vo.js.map +1 -0
  45. package/dist/index.d.ts +20 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +20 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/mailer-collecting.adapter.d.ts +8 -0
  50. package/dist/mailer-collecting.adapter.d.ts.map +1 -0
  51. package/dist/mailer-collecting.adapter.js +10 -0
  52. package/dist/mailer-collecting.adapter.js.map +1 -0
  53. package/dist/prerequisite-verifier-sms.adapter.d.ts +13 -0
  54. package/dist/prerequisite-verifier-sms.adapter.d.ts.map +1 -0
  55. package/dist/prerequisite-verifier-sms.adapter.js +20 -0
  56. package/dist/prerequisite-verifier-sms.adapter.js.map +1 -0
  57. package/dist/sms-collecting.adapter.d.ts +8 -0
  58. package/dist/sms-collecting.adapter.d.ts.map +1 -0
  59. package/dist/sms-collecting.adapter.js +10 -0
  60. package/dist/sms-collecting.adapter.js.map +1 -0
  61. package/dist/sms-noop.adapter.d.ts +7 -0
  62. package/dist/sms-noop.adapter.d.ts.map +1 -0
  63. package/dist/sms-noop.adapter.js +7 -0
  64. package/dist/sms-noop.adapter.js.map +1 -0
  65. package/dist/sms-with-logger.adapter.d.ts +17 -0
  66. package/dist/sms-with-logger.adapter.d.ts.map +1 -0
  67. package/dist/sms-with-logger.adapter.js +28 -0
  68. package/dist/sms-with-logger.adapter.js.map +1 -0
  69. package/dist/sms-with-retry.adapter.d.ts +19 -0
  70. package/dist/sms-with-retry.adapter.d.ts.map +1 -0
  71. package/dist/sms-with-retry.adapter.js +16 -0
  72. package/dist/sms-with-retry.adapter.js.map +1 -0
  73. package/dist/sms-with-timeout.adapter.d.ts +18 -0
  74. package/dist/sms-with-timeout.adapter.d.ts.map +1 -0
  75. package/dist/sms-with-timeout.adapter.js +15 -0
  76. package/dist/sms-with-timeout.adapter.js.map +1 -0
  77. package/dist/sms.builder.d.ts +14 -0
  78. package/dist/sms.builder.d.ts.map +1 -0
  79. package/dist/sms.builder.js +28 -0
  80. package/dist/sms.builder.js.map +1 -0
  81. package/dist/sms.port.d.ts +6 -0
  82. package/dist/sms.port.d.ts.map +1 -0
  83. package/dist/sms.port.js +2 -0
  84. package/dist/sms.port.js.map +1 -0
  85. package/dist/tsconfig.tsbuildinfo +1 -1
  86. package/package.json +5 -5
  87. package/readme.md +20 -0
  88. package/src/alert-channel-collecting.adapter.ts +14 -0
  89. package/src/alert-channel-composite.adapter.ts +24 -0
  90. package/src/alert-channel-mailer.adapter.ts +22 -0
  91. package/src/alert-channel-noop.adapter.ts +10 -0
  92. package/src/alert-channel-sms.adapter.ts +22 -0
  93. package/src/alert-channel-with-logger.adapter.ts +46 -0
  94. package/src/alert-channel-with-retry.adapter.ts +22 -0
  95. package/src/alert-channel-with-timeout.adapter.ts +25 -0
  96. package/src/alert-channel.builder.ts +48 -0
  97. package/src/alert-channel.port.ts +7 -0
  98. package/src/alert-message.vo.ts +10 -0
  99. package/src/index.ts +20 -0
  100. package/src/mailer-collecting.adapter.ts +14 -0
  101. package/src/prerequisite-verifier-sms.adapter.ts +26 -0
  102. package/src/sms-collecting.adapter.ts +14 -0
  103. package/src/sms-noop.adapter.ts +10 -0
  104. package/src/sms-with-logger.adapter.ts +37 -0
  105. package/src/sms-with-retry.adapter.ts +22 -0
  106. package/src/sms-with-timeout.adapter.ts +21 -0
  107. package/src/sms.builder.ts +39 -0
  108. package/src/sms.port.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.15.4",
3
+ "version": "1.15.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -28,10 +28,10 @@
28
28
  "@types/bun": "1.3.11",
29
29
  "@types/nodemailer": "7.0.11",
30
30
  "@types/yazl": "3.3.0",
31
- "better-auth": "1.5.5",
31
+ "better-auth": "1.5.6",
32
32
  "cspell": "9.7.0",
33
33
  "csv": "6.5.1",
34
- "knip": "6.0.0",
34
+ "knip": "6.0.2",
35
35
  "lefthook": "2.1.4",
36
36
  "lockfile-lint": "5.0.0",
37
37
  "nodemailer": "8.0.3",
@@ -43,13 +43,13 @@
43
43
  "yazl": "3.3.1"
44
44
  },
45
45
  "dependencies": {
46
- "@bgord/tools": "1.4.3",
46
+ "@bgord/tools": "1.4.4",
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
- "better-auth": "1.5.5",
52
+ "better-auth": "1.5.6",
53
53
  "csv": "6.5.1",
54
54
  "nodemailer": "8.0.3",
55
55
  "resend": "6.9.4",
package/readme.md CHANGED
@@ -36,6 +36,17 @@ src/
36
36
  ├── ab-variant.vo.ts
37
37
  ├── ab-variants.vo.ts
38
38
  ├── ab.middleware.ts
39
+ ├── alert-channel-collecting.adapter.ts
40
+ ├── alert-channel-composite.adapter.ts
41
+ ├── alert-channel-mailer.adapter.ts
42
+ ├── alert-channel-noop.adapter.ts
43
+ ├── alert-channel-sms.adapter.ts
44
+ ├── alert-channel-with-logger.adapter.ts
45
+ ├── alert-channel-with-retry.adapter.ts
46
+ ├── alert-channel-with-timeout.adapter.ts
47
+ ├── alert-channel.builder.ts
48
+ ├── alert-channel.port.ts
49
+ ├── alert-message.vo.ts
39
50
  ├── antivirus-clamav.adapter.ts
40
51
  ├── antivirus-noop.adapter.ts
41
52
  ├── antivirus.port.ts
@@ -248,6 +259,7 @@ src/
248
259
  ├── logger-stats-provider-noop.adapter.ts
249
260
  ├── logger-stats-provider.port.ts
250
261
  ├── logger.port.ts
262
+ ├── mailer-collecting.adapter.ts
251
263
  ├── mailer-content-html.vo.ts
252
264
  ├── mailer-noop.adapter.ts
253
265
  ├── mailer-resend.adapter.ts
@@ -344,6 +356,7 @@ src/
344
356
  ├── prerequisite-verifier-ram.adapter.ts
345
357
  ├── prerequisite-verifier-running-user.adapter.ts
346
358
  ├── prerequisite-verifier-self.adapter.ts
359
+ ├── prerequisite-verifier-sms.adapter.ts
347
360
  ├── prerequisite-verifier-space.adapter.ts
348
361
  ├── prerequisite-verifier-sqlite.adapter.ts
349
362
  ├── prerequisite-verifier-ssl-certificate-expiry.adapter.ts
@@ -444,6 +457,13 @@ src/
444
457
  ├── sleeper.port.ts
445
458
  ├── slower-hono.middleware.ts
446
459
  ├── slower.middleware.ts
460
+ ├── sms-collecting.adapter.ts
461
+ ├── sms-noop.adapter.ts
462
+ ├── sms-with-logger.adapter.ts
463
+ ├── sms-with-retry.adapter.ts
464
+ ├── sms-with-timeout.adapter.ts
465
+ ├── sms.builder.ts
466
+ ├── sms.port.ts
447
467
  ├── smtp-host.vo.ts
448
468
  ├── smtp-pass.vo.ts
449
469
  ├── smtp-port.vo.ts
@@ -0,0 +1,14 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import type { AlertMessage } from "./alert-message.vo";
3
+
4
+ export class AlertChannelCollectingAdapter implements AlertChannelPort {
5
+ readonly alerts: Array<AlertMessage> = [];
6
+
7
+ async send(alert: AlertMessage): Promise<void> {
8
+ this.alerts.push(alert);
9
+ }
10
+
11
+ async verify(): Promise<boolean> {
12
+ return true;
13
+ }
14
+ }
@@ -0,0 +1,24 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import type { AlertMessage } from "./alert-message.vo";
3
+
4
+ export const AlertChannelCompositeError = {
5
+ Min: "alert.channel.composite.channels.min",
6
+ Max: "alert.channel.composite.channels.max",
7
+ };
8
+
9
+ export class AlertChannelCompositeAdapter implements AlertChannelPort {
10
+ constructor(private readonly channels: ReadonlyArray<AlertChannelPort>) {
11
+ if (this.channels.length === 0) throw new Error(AlertChannelCompositeError.Min);
12
+ if (this.channels.length > 5) throw new Error(AlertChannelCompositeError.Max);
13
+ }
14
+
15
+ async send(alert: AlertMessage): Promise<void> {
16
+ await Promise.allSettled(this.channels.map((channel) => channel.send(alert)));
17
+ }
18
+
19
+ async verify(): Promise<boolean> {
20
+ const checks = await Promise.all(this.channels.map((channel) => channel.verify()));
21
+
22
+ return checks.every(Boolean);
23
+ }
24
+ }
@@ -0,0 +1,22 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import type { AlertMessage } from "./alert-message.vo";
3
+ import type { MailerPort } from "./mailer.port";
4
+ import type { MailerTemplate } from "./mailer-template.vo";
5
+
6
+ export type AlertChannelMailerAdapterConfig = { template: (alert: AlertMessage) => MailerTemplate };
7
+ export type AlertChannelMailerAdapterDependencies = { Mailer: MailerPort };
8
+
9
+ export class AlertChannelMailerAdapter implements AlertChannelPort {
10
+ constructor(
11
+ private readonly config: AlertChannelMailerAdapterConfig,
12
+ private readonly deps: AlertChannelMailerAdapterDependencies,
13
+ ) {}
14
+
15
+ async send(alert: AlertMessage): Promise<void> {
16
+ await this.deps.Mailer.send(this.config.template(alert));
17
+ }
18
+
19
+ async verify(): Promise<boolean> {
20
+ return this.deps.Mailer.verify();
21
+ }
22
+ }
@@ -0,0 +1,10 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import type { AlertMessage } from "./alert-message.vo";
3
+
4
+ export class AlertChannelNoopAdapter implements AlertChannelPort {
5
+ async send(_alert: AlertMessage): Promise<void> {}
6
+
7
+ async verify(): Promise<boolean> {
8
+ return true;
9
+ }
10
+ }
@@ -0,0 +1,22 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { AlertChannelPort } from "./alert-channel.port";
3
+ import type { AlertMessage } from "./alert-message.vo";
4
+ import type { SmsPort } from "./sms.port";
5
+
6
+ export type AlertChannelSmsAdapterConfig = { message: (alert: AlertMessage) => tools.SmsMessage };
7
+ export type AlertChannelSmsAdapterDependencies = { Sms: SmsPort };
8
+
9
+ export class AlertChannelSmsAdapter implements AlertChannelPort {
10
+ constructor(
11
+ private readonly config: AlertChannelSmsAdapterConfig,
12
+ private readonly deps: AlertChannelSmsAdapterDependencies,
13
+ ) {}
14
+
15
+ async send(alert: AlertMessage): Promise<void> {
16
+ await this.deps.Sms.send(this.config.message(alert));
17
+ }
18
+
19
+ async verify(): Promise<boolean> {
20
+ return this.deps.Sms.verify();
21
+ }
22
+ }
@@ -0,0 +1,46 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import type { AlertMessage } from "./alert-message.vo";
3
+ import type { ClockPort } from "./clock.port";
4
+ import type { LoggerPort } from "./logger.port";
5
+ import { Stopwatch } from "./stopwatch.service";
6
+
7
+ export type AlertChannelWithLoggerAdapterDependencies = {
8
+ inner: AlertChannelPort;
9
+ Logger: LoggerPort;
10
+ Clock: ClockPort;
11
+ };
12
+
13
+ export class AlertChannelWithLoggerAdapter implements AlertChannelPort {
14
+ private readonly base = { component: "infra", operation: "alert_channel" };
15
+
16
+ constructor(private readonly deps: AlertChannelWithLoggerAdapterDependencies) {}
17
+
18
+ async send(alert: AlertMessage): Promise<void> {
19
+ const duration = new Stopwatch(this.deps);
20
+
21
+ try {
22
+ this.deps.Logger.info({ message: "Alert channel attempt", metadata: alert.toJSON(), ...this.base });
23
+
24
+ await this.deps.inner.send(alert);
25
+
26
+ this.deps.Logger.info({
27
+ message: "Alert channel success",
28
+ metadata: { alert: alert.toJSON(), duration: duration.stop() },
29
+ ...this.base,
30
+ });
31
+ } catch (error) {
32
+ this.deps.Logger.error({
33
+ message: "Alert channel error",
34
+ error,
35
+ metadata: duration.stop(),
36
+ ...this.base,
37
+ });
38
+
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ async verify(): Promise<boolean> {
44
+ return this.deps.inner.verify();
45
+ }
46
+ }
@@ -0,0 +1,22 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import type { AlertMessage } from "./alert-message.vo";
3
+ import { Retry, type RetryConfig } from "./retry.service";
4
+ import type { SleeperPort } from "./sleeper.port";
5
+
6
+ export type AlertChannelWithRetryAdapterDependencies = { Sleeper: SleeperPort; inner: AlertChannelPort };
7
+ export type AlertChannelWithRetryAdapterConfig = { retry: RetryConfig };
8
+
9
+ export class AlertChannelWithRetryAdapter implements AlertChannelPort {
10
+ constructor(
11
+ private readonly config: AlertChannelWithRetryAdapterConfig,
12
+ private readonly deps: AlertChannelWithRetryAdapterDependencies,
13
+ ) {}
14
+
15
+ async send(alert: AlertMessage): Promise<void> {
16
+ await new Retry(this.deps).run(async () => this.deps.inner.send(alert), this.config.retry);
17
+ }
18
+
19
+ async verify(): Promise<boolean> {
20
+ return this.deps.inner.verify();
21
+ }
22
+ }
@@ -0,0 +1,25 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { AlertChannelPort } from "./alert-channel.port";
3
+ import type { AlertMessage } from "./alert-message.vo";
4
+ import type { TimeoutRunnerPort } from "./timeout-runner.port";
5
+
6
+ export type AlertChannelWithTimeoutAdapterDependencies = {
7
+ TimeoutRunner: TimeoutRunnerPort;
8
+ inner: AlertChannelPort;
9
+ };
10
+ export type AlertChannelWithTimeoutAdapterConfig = { timeout: tools.Duration };
11
+
12
+ export class AlertChannelWithTimeoutAdapter implements AlertChannelPort {
13
+ constructor(
14
+ private readonly config: AlertChannelWithTimeoutAdapterConfig,
15
+ private readonly deps: AlertChannelWithTimeoutAdapterDependencies,
16
+ ) {}
17
+
18
+ async send(alert: AlertMessage): Promise<void> {
19
+ await this.deps.TimeoutRunner.run(this.deps.inner.send(alert), this.config.timeout);
20
+ }
21
+
22
+ async verify(): Promise<boolean> {
23
+ return this.deps.inner.verify();
24
+ }
25
+ }
@@ -0,0 +1,48 @@
1
+ import type { AlertChannelPort } from "./alert-channel.port";
2
+ import {
3
+ AlertChannelWithLoggerAdapter,
4
+ type AlertChannelWithLoggerAdapterDependencies,
5
+ } from "./alert-channel-with-logger.adapter";
6
+ import {
7
+ AlertChannelWithRetryAdapter,
8
+ type AlertChannelWithRetryAdapterConfig,
9
+ type AlertChannelWithRetryAdapterDependencies,
10
+ } from "./alert-channel-with-retry.adapter";
11
+ import {
12
+ AlertChannelWithTimeoutAdapter,
13
+ type AlertChannelWithTimeoutAdapterConfig,
14
+ type AlertChannelWithTimeoutAdapterDependencies,
15
+ } from "./alert-channel-with-timeout.adapter";
16
+
17
+ export class AlertChannelBuilder {
18
+ constructor(private inner: AlertChannelPort) {}
19
+
20
+ static of(channel: AlertChannelPort): AlertChannelBuilder {
21
+ return new AlertChannelBuilder(channel);
22
+ }
23
+
24
+ withLogger(deps: Omit<AlertChannelWithLoggerAdapterDependencies, "inner">) {
25
+ this.inner = new AlertChannelWithLoggerAdapter({ ...deps, inner: this.inner });
26
+ return this;
27
+ }
28
+
29
+ withRetry(
30
+ config: AlertChannelWithRetryAdapterConfig,
31
+ deps: Omit<AlertChannelWithRetryAdapterDependencies, "inner">,
32
+ ) {
33
+ this.inner = new AlertChannelWithRetryAdapter(config, { ...deps, inner: this.inner });
34
+ return this;
35
+ }
36
+
37
+ withTimeout(
38
+ config: AlertChannelWithTimeoutAdapterConfig,
39
+ deps: Omit<AlertChannelWithTimeoutAdapterDependencies, "inner">,
40
+ ) {
41
+ this.inner = new AlertChannelWithTimeoutAdapter(config, { ...deps, inner: this.inner });
42
+ return this;
43
+ }
44
+
45
+ build(): AlertChannelPort {
46
+ return this.inner;
47
+ }
48
+ }
@@ -0,0 +1,7 @@
1
+ import type { AlertMessage } from "./alert-message.vo";
2
+
3
+ export interface AlertChannelPort {
4
+ send(alert: AlertMessage): Promise<void>;
5
+
6
+ verify(): Promise<boolean>;
7
+ }
@@ -0,0 +1,10 @@
1
+ export class AlertMessage {
2
+ constructor(
3
+ readonly message: string,
4
+ readonly error?: unknown,
5
+ ) {}
6
+
7
+ toJSON() {
8
+ return { message: this.message, error: this.error };
9
+ }
10
+ }
package/src/index.ts CHANGED
@@ -10,6 +10,17 @@ export * from "./ab-variant.vo";
10
10
  export * from "./ab-variant-selector.service";
11
11
  export * from "./ab-variant-weight.vo";
12
12
  export * from "./ab-variants.vo";
13
+ export * from "./alert-channel.builder";
14
+ export * from "./alert-channel.port";
15
+ export * from "./alert-channel-collecting.adapter";
16
+ export * from "./alert-channel-composite.adapter";
17
+ export * from "./alert-channel-mailer.adapter";
18
+ export * from "./alert-channel-noop.adapter";
19
+ export * from "./alert-channel-sms.adapter";
20
+ export * from "./alert-channel-with-logger.adapter";
21
+ export * from "./alert-channel-with-retry.adapter";
22
+ export * from "./alert-channel-with-timeout.adapter";
23
+ export * from "./alert-message.vo";
13
24
  export * from "./antivirus.port";
14
25
  export * from "./antivirus-clamav.adapter";
15
26
  export * from "./antivirus-noop.adapter";
@@ -222,6 +233,7 @@ export * from "./logger-stats-provider-noop.adapter";
222
233
  export * from "./mailer.builder";
223
234
  export * from "./mailer.builder";
224
235
  export * from "./mailer.port";
236
+ export * from "./mailer-collecting.adapter";
225
237
  export * from "./mailer-content-html.vo";
226
238
  export * from "./mailer-noop.adapter";
227
239
  export * from "./mailer-resend.adapter";
@@ -281,6 +293,7 @@ export * from "./prerequisite-verifier-port.adapter";
281
293
  export * from "./prerequisite-verifier-ram.adapter";
282
294
  export * from "./prerequisite-verifier-running-user.adapter";
283
295
  export * from "./prerequisite-verifier-self.adapter";
296
+ export * from "./prerequisite-verifier-sms.adapter";
284
297
  export * from "./prerequisite-verifier-space.adapter";
285
298
  export * from "./prerequisite-verifier-sqlite.adapter";
286
299
  export * from "./prerequisite-verifier-ssl-certificate-expiry.adapter";
@@ -373,6 +386,13 @@ export * from "./sleeper-noop.adapter";
373
386
  export * from "./sleeper-system.adapter";
374
387
  export * from "./slower.middleware";
375
388
  export * from "./slower-hono.middleware";
389
+ export * from "./sms.builder";
390
+ export * from "./sms.port";
391
+ export * from "./sms-collecting.adapter";
392
+ export * from "./sms-noop.adapter";
393
+ export * from "./sms-with-logger.adapter";
394
+ export * from "./sms-with-retry.adapter";
395
+ export * from "./sms-with-timeout.adapter";
376
396
  export * from "./smtp-host.vo";
377
397
  export * from "./smtp-pass.vo";
378
398
  export * from "./smtp-port.vo";
@@ -0,0 +1,14 @@
1
+ import type { MailerPort } from "./mailer.port";
2
+ import type { MailerTemplate } from "./mailer-template.vo";
3
+
4
+ export class MailerCollectingAdapter implements MailerPort {
5
+ messages: Array<MailerTemplate> = [];
6
+
7
+ async send(template: MailerTemplate): Promise<void> {
8
+ this.messages.push(template);
9
+ }
10
+
11
+ async verify(): Promise<boolean> {
12
+ return true;
13
+ }
14
+ }
@@ -0,0 +1,26 @@
1
+ import {
2
+ PrerequisiteVerification,
3
+ type PrerequisiteVerificationResult,
4
+ type PrerequisiteVerifierPort,
5
+ } from "./prerequisite-verifier.port";
6
+ import type { SmsPort } from "./sms.port";
7
+
8
+ type Dependencies = { Sms: SmsPort };
9
+
10
+ export class PrerequisiteVerifierSmsAdapter implements PrerequisiteVerifierPort {
11
+ constructor(private readonly deps: Dependencies) {}
12
+
13
+ async verify(): Promise<PrerequisiteVerificationResult> {
14
+ try {
15
+ await this.deps.Sms.verify();
16
+
17
+ return PrerequisiteVerification.success;
18
+ } catch (error) {
19
+ return PrerequisiteVerification.failure(error);
20
+ }
21
+ }
22
+
23
+ get kind(): string {
24
+ return "sms";
25
+ }
26
+ }
@@ -0,0 +1,14 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { SmsPort } from "./sms.port";
3
+
4
+ export class SmsCollectingAdapter implements SmsPort {
5
+ readonly messages: Array<tools.SmsMessage> = [];
6
+
7
+ async send(message: tools.SmsMessage): Promise<void> {
8
+ this.messages.push(message);
9
+ }
10
+
11
+ async verify(): Promise<boolean> {
12
+ return true;
13
+ }
14
+ }
@@ -0,0 +1,10 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { SmsPort } from "./sms.port";
3
+
4
+ export class SmsNoopAdapter implements SmsPort {
5
+ async send(_message: tools.SmsMessage): Promise<void> {}
6
+
7
+ async verify(): Promise<boolean> {
8
+ return true;
9
+ }
10
+ }
@@ -0,0 +1,37 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { ClockPort } from "./clock.port";
3
+ import type { LoggerPort } from "./logger.port";
4
+ import type { SmsPort } from "./sms.port";
5
+ import { Stopwatch } from "./stopwatch.service";
6
+
7
+ export type SmsWithLoggerAdapterDependencies = { inner: SmsPort; Logger: LoggerPort; Clock: ClockPort };
8
+
9
+ export class SmsWithLoggerAdapter implements SmsPort {
10
+ private readonly base = { component: "infra", operation: "sms" };
11
+
12
+ constructor(private readonly deps: SmsWithLoggerAdapterDependencies) {}
13
+
14
+ async send(message: tools.SmsMessage): Promise<void> {
15
+ const duration = new Stopwatch(this.deps);
16
+
17
+ try {
18
+ this.deps.Logger.info({ message: "SMS attempt", metadata: message.toJSON(), ...this.base });
19
+
20
+ await this.deps.inner.send(message);
21
+
22
+ this.deps.Logger.info({
23
+ message: "SMS success",
24
+ metadata: { message: message.toJSON(), duration: duration.stop() },
25
+ ...this.base,
26
+ });
27
+ } catch (error) {
28
+ this.deps.Logger.error({ message: "SMS error", error, metadata: duration.stop(), ...this.base });
29
+
30
+ throw error;
31
+ }
32
+ }
33
+
34
+ async verify(): Promise<boolean> {
35
+ return this.deps.inner.verify();
36
+ }
37
+ }
@@ -0,0 +1,22 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import { Retry, type RetryConfig } from "./retry.service";
3
+ import type { SleeperPort } from "./sleeper.port";
4
+ import type { SmsPort } from "./sms.port";
5
+
6
+ export type SmsWithRetryAdapterDependencies = { Sleeper: SleeperPort; inner: SmsPort };
7
+ export type SmsWithRetryAdapterConfig = { retry: RetryConfig };
8
+
9
+ export class SmsWithRetryAdapter implements SmsPort {
10
+ constructor(
11
+ private readonly config: SmsWithRetryAdapterConfig,
12
+ private readonly deps: SmsWithRetryAdapterDependencies,
13
+ ) {}
14
+
15
+ async send(message: tools.SmsMessage): Promise<void> {
16
+ await new Retry(this.deps).run(async () => this.deps.inner.send(message), this.config.retry);
17
+ }
18
+
19
+ async verify(): Promise<boolean> {
20
+ return this.deps.inner.verify();
21
+ }
22
+ }
@@ -0,0 +1,21 @@
1
+ import type * as tools from "@bgord/tools";
2
+ import type { SmsPort } from "./sms.port";
3
+ import type { TimeoutRunnerPort } from "./timeout-runner.port";
4
+
5
+ export type SmsWithTimeoutAdapterDependencies = { TimeoutRunner: TimeoutRunnerPort; inner: SmsPort };
6
+ export type SmsWithTimeoutAdapterConfig = { timeout: tools.Duration };
7
+
8
+ export class SmsWithTimeoutAdapter implements SmsPort {
9
+ constructor(
10
+ private readonly config: SmsWithTimeoutAdapterConfig,
11
+ private readonly deps: SmsWithTimeoutAdapterDependencies,
12
+ ) {}
13
+
14
+ async send(message: tools.SmsMessage): Promise<void> {
15
+ await this.deps.TimeoutRunner.run(this.deps.inner.send(message), this.config.timeout);
16
+ }
17
+
18
+ async verify(): Promise<boolean> {
19
+ return this.deps.inner.verify();
20
+ }
21
+ }
@@ -0,0 +1,39 @@
1
+ import type { SmsPort } from "./sms.port";
2
+ import { SmsWithLoggerAdapter, type SmsWithLoggerAdapterDependencies } from "./sms-with-logger.adapter";
3
+ import {
4
+ SmsWithRetryAdapter,
5
+ type SmsWithRetryAdapterConfig,
6
+ type SmsWithRetryAdapterDependencies,
7
+ } from "./sms-with-retry.adapter";
8
+ import {
9
+ SmsWithTimeoutAdapter,
10
+ type SmsWithTimeoutAdapterConfig,
11
+ type SmsWithTimeoutAdapterDependencies,
12
+ } from "./sms-with-timeout.adapter";
13
+
14
+ export class SmsBuilder {
15
+ constructor(private inner: SmsPort) {}
16
+
17
+ static of(sms: SmsPort): SmsBuilder {
18
+ return new SmsBuilder(sms);
19
+ }
20
+
21
+ withLogger(deps: Omit<SmsWithLoggerAdapterDependencies, "inner">) {
22
+ this.inner = new SmsWithLoggerAdapter({ ...deps, inner: this.inner });
23
+ return this;
24
+ }
25
+
26
+ withRetry(config: SmsWithRetryAdapterConfig, deps: Omit<SmsWithRetryAdapterDependencies, "inner">) {
27
+ this.inner = new SmsWithRetryAdapter(config, { ...deps, inner: this.inner });
28
+ return this;
29
+ }
30
+
31
+ withTimeout(config: SmsWithTimeoutAdapterConfig, deps: Omit<SmsWithTimeoutAdapterDependencies, "inner">) {
32
+ this.inner = new SmsWithTimeoutAdapter(config, { ...deps, inner: this.inner });
33
+ return this;
34
+ }
35
+
36
+ build() {
37
+ return this.inner;
38
+ }
39
+ }
@@ -0,0 +1,6 @@
1
+ import type * as tools from "@bgord/tools";
2
+
3
+ export interface SmsPort {
4
+ send(message: tools.SmsMessage): Promise<void>;
5
+ verify(): Promise<boolean>;
6
+ }