@bgord/bun 1.12.7 → 1.12.8

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 (81) hide show
  1. package/dist/certificate-inspector-tls.adapter.d.ts.map +1 -1
  2. package/dist/certificate-inspector-tls.adapter.js +17 -14
  3. package/dist/certificate-inspector-tls.adapter.js.map +1 -1
  4. package/dist/csv-stringifier.adapter.d.ts +1 -4
  5. package/dist/csv-stringifier.adapter.d.ts.map +1 -1
  6. package/dist/csv-stringifier.adapter.js +4 -13
  7. package/dist/csv-stringifier.adapter.js.map +1 -1
  8. package/dist/dynamic-import.service.d.ts +10 -0
  9. package/dist/dynamic-import.service.d.ts.map +1 -0
  10. package/dist/dynamic-import.service.js +26 -0
  11. package/dist/dynamic-import.service.js.map +1 -0
  12. package/dist/file-draft-zip.service.d.ts +1 -4
  13. package/dist/file-draft-zip.service.d.ts.map +1 -1
  14. package/dist/file-draft-zip.service.js +4 -13
  15. package/dist/file-draft-zip.service.js.map +1 -1
  16. package/dist/i18n.service.d.ts.map +1 -1
  17. package/dist/i18n.service.js +2 -1
  18. package/dist/i18n.service.js.map +1 -1
  19. package/dist/image-alpha-sharp.adapter.d.ts +1 -7
  20. package/dist/image-alpha-sharp.adapter.d.ts.map +1 -1
  21. package/dist/image-alpha-sharp.adapter.js +4 -16
  22. package/dist/image-alpha-sharp.adapter.js.map +1 -1
  23. package/dist/image-blur-sharp.adapter.d.ts +1 -7
  24. package/dist/image-blur-sharp.adapter.d.ts.map +1 -1
  25. package/dist/image-blur-sharp.adapter.js +4 -16
  26. package/dist/image-blur-sharp.adapter.js.map +1 -1
  27. package/dist/image-compressor-sharp.adapter.d.ts +1 -7
  28. package/dist/image-compressor-sharp.adapter.d.ts.map +1 -1
  29. package/dist/image-compressor-sharp.adapter.js +4 -16
  30. package/dist/image-compressor-sharp.adapter.js.map +1 -1
  31. package/dist/image-exif-clear-sharp.adapter.d.ts +1 -7
  32. package/dist/image-exif-clear-sharp.adapter.d.ts.map +1 -1
  33. package/dist/image-exif-clear-sharp.adapter.js +4 -16
  34. package/dist/image-exif-clear-sharp.adapter.js.map +1 -1
  35. package/dist/image-formatter-sharp.adapter.d.ts +1 -7
  36. package/dist/image-formatter-sharp.adapter.d.ts.map +1 -1
  37. package/dist/image-formatter-sharp.adapter.js +4 -16
  38. package/dist/image-formatter-sharp.adapter.js.map +1 -1
  39. package/dist/image-info-sharp.adapter.d.ts +1 -7
  40. package/dist/image-info-sharp.adapter.d.ts.map +1 -1
  41. package/dist/image-info-sharp.adapter.js +4 -16
  42. package/dist/image-info-sharp.adapter.js.map +1 -1
  43. package/dist/image-processor-sharp.adapter.d.ts +1 -7
  44. package/dist/image-processor-sharp.adapter.d.ts.map +1 -1
  45. package/dist/image-processor-sharp.adapter.js +4 -16
  46. package/dist/image-processor-sharp.adapter.js.map +1 -1
  47. package/dist/image-resizer-sharp.adapter.d.ts +1 -7
  48. package/dist/image-resizer-sharp.adapter.d.ts.map +1 -1
  49. package/dist/image-resizer-sharp.adapter.js +4 -16
  50. package/dist/image-resizer-sharp.adapter.js.map +1 -1
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +1 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/mailer-resend.adapter.d.ts +1 -2
  56. package/dist/mailer-resend.adapter.d.ts.map +1 -1
  57. package/dist/mailer-resend.adapter.js +6 -17
  58. package/dist/mailer-resend.adapter.js.map +1 -1
  59. package/dist/mailer-smtp.adapter.d.ts +1 -2
  60. package/dist/mailer-smtp.adapter.d.ts.map +1 -1
  61. package/dist/mailer-smtp.adapter.js +3 -13
  62. package/dist/mailer-smtp.adapter.js.map +1 -1
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +4 -4
  65. package/readme.md +1 -0
  66. package/src/certificate-inspector-tls.adapter.ts +17 -12
  67. package/src/csv-stringifier.adapter.ts +8 -13
  68. package/src/dynamic-import.service.ts +26 -0
  69. package/src/file-draft-zip.service.ts +8 -14
  70. package/src/i18n.service.ts +2 -1
  71. package/src/image-alpha-sharp.adapter.ts +10 -19
  72. package/src/image-blur-sharp.adapter.ts +10 -19
  73. package/src/image-compressor-sharp.adapter.ts +9 -19
  74. package/src/image-exif-clear-sharp.adapter.ts +10 -19
  75. package/src/image-formatter-sharp.adapter.ts +10 -19
  76. package/src/image-info-sharp.adapter.ts +10 -19
  77. package/src/image-processor-sharp.adapter.ts +9 -19
  78. package/src/image-resizer-sharp.adapter.ts +10 -19
  79. package/src/index.ts +1 -0
  80. package/src/mailer-resend.adapter.ts +9 -22
  81. package/src/mailer-smtp.adapter.ts +9 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/bun",
3
- "version": "1.12.7",
3
+ "version": "1.12.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": "Bartosz Gordon",
@@ -33,7 +33,7 @@
33
33
  "knip": "5.86.0",
34
34
  "lefthook": "2.1.3",
35
35
  "lockfile-lint": "5.0.0",
36
- "nodemailer": "8.0.1",
36
+ "nodemailer": "8.0.2",
37
37
  "only-allow": "1.2.2",
38
38
  "resend": "6.9.3",
39
39
  "sharp": "0.34.5",
@@ -45,13 +45,13 @@
45
45
  "dependencies": {
46
46
  "@bgord/tools": "1.3.24",
47
47
  "emittery": "2.0.0",
48
- "hono": "4.12.5",
48
+ "hono": "4.12.6",
49
49
  "node-cache": "5.1.2"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "better-auth": "1.5.4",
53
53
  "csv": "6.4.1",
54
- "nodemailer": "8.0.1",
54
+ "nodemailer": "8.0.2",
55
55
  "resend": "6.9.3",
56
56
  "sharp": "0.34.5",
57
57
  "yazl": "3.3.1",
package/readme.md CHANGED
@@ -97,6 +97,7 @@ src/
97
97
  ├── disk-space-checker-shell.adapter.ts
98
98
  ├── disk-space-checker.port.ts
99
99
  ├── dispatching-event-store.ts
100
+ ├── dynamic-import.service.ts
100
101
  ├── encryption-aes-gcm.adapter.ts
101
102
  ├── encryption-iv.vo.ts
102
103
  ├── encryption-key-value.vo.ts
@@ -5,38 +5,43 @@ import type { ClockPort } from "./clock.port";
5
5
 
6
6
  type Dependencies = { Clock: ClockPort };
7
7
 
8
- // Stryker disable all
9
8
  export class CertificateInspectorTLSAdapter implements CertificateInspectorPort {
10
9
  constructor(private readonly deps: Dependencies) {}
11
10
 
12
11
  async inspect(hostname: string): Promise<CertificateInspection> {
13
12
  return new Promise((resolve) => {
14
- const settle = (value: CertificateInspection) => {
15
- try {
16
- socket.end();
17
- socket.destroy();
18
- } finally {
19
- resolve(value);
20
- }
13
+ // Stryker disable all
14
+ const cleanup = (socket: tls.TLSSocket) => {
15
+ socket.end();
16
+ socket.destroy();
21
17
  };
18
+ // Stryker restore all
22
19
 
23
20
  const socket = tls.connect(
24
21
  { host: hostname, port: 443, servername: hostname, rejectUnauthorized: false },
25
22
  () => {
26
23
  const certificate = socket.getPeerCertificate();
27
24
 
28
- if (!certificate?.valid_to) return settle({ success: false });
25
+ if (!certificate?.valid_to) {
26
+ cleanup(socket);
27
+ return resolve({ success: false });
28
+ }
29
29
 
30
30
  const remaining = tools.Timestamp.fromDateLike(certificate.valid_to).difference(
31
31
  this.deps.Clock.now(),
32
32
  );
33
33
 
34
- settle({ success: true, remaining });
34
+ cleanup(socket);
35
+ resolve({ success: true, remaining });
35
36
  },
36
37
  );
37
38
 
38
- socket.once("error", () => settle({ success: false }));
39
+ // Stryker disable all
40
+ socket.once("error", () => {
41
+ // Stryker restore all
42
+ cleanup(socket);
43
+ resolve({ success: false });
44
+ });
39
45
  });
40
46
  }
41
47
  }
42
- // Stryker restore all
@@ -1,5 +1,6 @@
1
1
  import { text } from "node:stream/consumers";
2
2
  import type { CsvColumnType, CsvRowType, CsvStringifierPort } from "./csv-stringifier.port";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
 
4
5
  export const CsvStringifierAdapterError = {
5
6
  MissingDependency: "csv.stringifier.adapter.error.missing.dependency",
@@ -8,23 +9,17 @@ export const CsvStringifierAdapterError = {
8
9
  type CsvLibrary = typeof import("csv");
9
10
 
10
11
  export class CsvStringifierAdapter implements CsvStringifierPort {
12
+ private static readonly importer = DynamicImport.for<CsvLibrary>(
13
+ "csv",
14
+ CsvStringifierAdapterError.MissingDependency,
15
+ );
16
+
11
17
  private constructor(private readonly csv: CsvLibrary) {}
12
18
 
13
19
  static async build(): Promise<CsvStringifierAdapter> {
14
- return new CsvStringifierAdapter(await CsvStringifierAdapter.resolve());
15
- }
16
-
17
- private static async resolve(): Promise<CsvLibrary> {
18
- try {
19
- return await CsvStringifierAdapter.import();
20
- } catch {
21
- throw new Error(CsvStringifierAdapterError.MissingDependency);
22
- }
23
- }
20
+ const dependency = await CsvStringifierAdapter.importer.resolve();
24
21
 
25
- static async import(): Promise<CsvLibrary> {
26
- const name = "c" + "sv"; // Bun does not resolve dynamic imports with a dynamic name
27
- return import(name) as Promise<CsvLibrary>;
22
+ return new CsvStringifierAdapter(dependency);
28
23
  }
29
24
 
30
25
  async process(columns: ReadonlyArray<CsvColumnType>, data: Array<CsvRowType>): Promise<string> {
@@ -0,0 +1,26 @@
1
+ export class DynamicImport<T> {
2
+ private constructor(
3
+ private readonly dependency: string,
4
+ private readonly error: string,
5
+ ) {}
6
+
7
+ static for<T>(dependency: string, error: string): DynamicImport<T> {
8
+ return new DynamicImport<T>(dependency, error);
9
+ }
10
+
11
+ async resolve(): Promise<T> {
12
+ try {
13
+ return await this.import();
14
+ } catch {
15
+ throw new Error(this.error);
16
+ }
17
+ }
18
+
19
+ private async import(): Promise<T> {
20
+ return import(this.obfuscate(this.dependency)) as Promise<T>;
21
+ }
22
+
23
+ private obfuscate(name: string): string {
24
+ return name;
25
+ }
26
+ }
@@ -1,5 +1,6 @@
1
1
  import { Readable } from "node:stream";
2
2
  import * as tools from "@bgord/tools";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import { FileDraft } from "./file-draft.service";
4
5
 
5
6
  export const FileDraftZipError = {
@@ -9,6 +10,11 @@ export const FileDraftZipError = {
9
10
  type YazlLibrary = typeof import("yazl");
10
11
 
11
12
  export class FileDraftZip extends FileDraft {
13
+ private static readonly importer = DynamicImport.for<YazlLibrary>(
14
+ "yazl",
15
+ FileDraftZipError.MissingDependency,
16
+ );
17
+
12
18
  private constructor(
13
19
  basename: tools.BasenameType,
14
20
  private readonly parts: ReadonlyArray<FileDraft>,
@@ -18,20 +24,8 @@ export class FileDraftZip extends FileDraft {
18
24
  }
19
25
 
20
26
  static async build(basename: tools.BasenameType, parts: ReadonlyArray<FileDraft>): Promise<FileDraftZip> {
21
- return new FileDraftZip(basename, parts, await FileDraftZip.resolve());
22
- }
23
-
24
- private static async resolve(): Promise<YazlLibrary> {
25
- try {
26
- return await FileDraftZip.import();
27
- } catch {
28
- throw new Error(FileDraftZipError.MissingDependency);
29
- }
30
- }
31
-
32
- static async import(): Promise<YazlLibrary> {
33
- const name = "ya" + "zl"; // Bun does not resolve dynamic imports with a dynamic name
34
- return import(name) as Promise<YazlLibrary>;
27
+ const library = await FileDraftZip.importer.resolve();
28
+ return new FileDraftZip(basename, parts, library);
35
29
  }
36
30
 
37
31
  async create(): Promise<BodyInit> {
@@ -31,9 +31,10 @@ export class I18n {
31
31
 
32
32
  if (!translation) {
33
33
  that.deps.Logger.warn({
34
- message: `Missing translation for key ${key}`,
34
+ message: "Missing translation key",
35
35
  component: "infra",
36
36
  operation: "translations",
37
+ metadata: { key },
37
38
  });
38
39
 
39
40
  return key;
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileRenamerPort } from "./file-renamer.port";
4
5
  import type { ImageAlphaPort, ImageAlphaStrategy } from "./image-alpha.port";
5
6
 
@@ -8,34 +9,24 @@ export const ImageAlphaSharpAdapterError = {
8
9
  };
9
10
 
10
11
  type Dependencies = { FileRenamer: FileRenamerPort };
11
- type SharpCallable = typeof sharp;
12
- type SharpModule = { default: SharpCallable };
12
+ type Sharp = typeof sharp;
13
13
 
14
14
  export class ImageAlphaSharpAdapter implements ImageAlphaPort {
15
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
+ "sharp",
17
+ ImageAlphaSharpAdapterError.MissingDependency,
18
+ );
19
+
15
20
  private constructor(
16
- private readonly sharp: SharpCallable,
21
+ private readonly sharp: Sharp,
17
22
  private readonly deps: Dependencies,
18
23
  ) {}
19
24
 
20
25
  static async build(deps: Dependencies): Promise<ImageAlphaSharpAdapter> {
21
- return new ImageAlphaSharpAdapter(await ImageAlphaSharpAdapter.resolve(), deps);
22
- }
23
-
24
- private static async resolve(): Promise<SharpCallable> {
25
- try {
26
- const module = await ImageAlphaSharpAdapter.import();
27
- return module.default;
28
- } catch {
29
- throw new Error(ImageAlphaSharpAdapterError.MissingDependency);
30
- }
31
- }
26
+ const library = await ImageAlphaSharpAdapter.importer.resolve();
32
27
 
33
- // Stryker disable all
34
- static async import(): Promise<SharpModule> {
35
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
36
- return import(name) as Promise<SharpModule>;
28
+ return new ImageAlphaSharpAdapter(library.default, deps);
37
29
  }
38
- // Stryker restore all
39
30
 
40
31
  async flatten(recipe: ImageAlphaStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
41
32
  const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileRenamerPort } from "./file-renamer.port";
4
5
  import type { ImageBlurPort, ImageBlurStrategy } from "./image-blur.port";
5
6
 
@@ -8,34 +9,24 @@ export const ImageBlurSharpAdapterError = {
8
9
  };
9
10
 
10
11
  type Dependencies = { FileRenamer: FileRenamerPort };
11
- type SharpCallable = typeof sharp;
12
- type SharpModule = { default: SharpCallable };
12
+ type Sharp = typeof sharp;
13
13
 
14
14
  export class ImageBlurSharpAdapter implements ImageBlurPort {
15
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
+ "sharp",
17
+ ImageBlurSharpAdapterError.MissingDependency,
18
+ );
19
+
15
20
  private constructor(
16
- private readonly sharp: SharpCallable,
21
+ private readonly sharp: Sharp,
17
22
  private readonly deps: Dependencies,
18
23
  ) {}
19
24
 
20
25
  static async build(deps: Dependencies): Promise<ImageBlurSharpAdapter> {
21
- return new ImageBlurSharpAdapter(await ImageBlurSharpAdapter.resolve(), deps);
22
- }
23
-
24
- private static async resolve(): Promise<SharpCallable> {
25
- try {
26
- const module = await ImageBlurSharpAdapter.import();
27
- return module.default;
28
- } catch {
29
- throw new Error(ImageBlurSharpAdapterError.MissingDependency);
30
- }
31
- }
26
+ const library = await ImageBlurSharpAdapter.importer.resolve();
32
27
 
33
- // Stryker disable all
34
- static async import(): Promise<SharpModule> {
35
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
36
- return import(name) as Promise<SharpModule>;
28
+ return new ImageBlurSharpAdapter(library.default, deps);
37
29
  }
38
- // Stryker restore all
39
30
 
40
31
  async blur(recipe: ImageBlurStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
41
32
  const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileRenamerPort } from "./file-renamer.port";
4
5
  import type { ImageCompressorPort, ImageCompressorStrategy } from "./image-compressor.port";
5
6
 
@@ -8,36 +9,25 @@ export const ImageCompressorSharpAdapterError = {
8
9
  };
9
10
 
10
11
  type Dependencies = { FileRenamer: FileRenamerPort };
11
- type SharpCallable = typeof sharp;
12
- type SharpModule = { default: SharpCallable };
12
+ type Sharp = typeof sharp;
13
13
 
14
14
  export class ImageCompressorSharpAdapter implements ImageCompressorPort {
15
15
  private static readonly DEFAULT_QUALITY = 85;
16
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
17
+ "sharp",
18
+ ImageCompressorSharpAdapterError.MissingDependency,
19
+ );
16
20
 
17
21
  private constructor(
18
- private readonly sharp: SharpCallable,
22
+ private readonly sharp: Sharp,
19
23
  private readonly deps: Dependencies,
20
24
  ) {}
21
25
 
22
26
  static async build(deps: Dependencies): Promise<ImageCompressorSharpAdapter> {
23
- return new ImageCompressorSharpAdapter(await ImageCompressorSharpAdapter.resolve(), deps);
24
- }
25
-
26
- private static async resolve(): Promise<SharpCallable> {
27
- try {
28
- const module = await ImageCompressorSharpAdapter.import();
29
- return module.default;
30
- } catch {
31
- throw new Error(ImageCompressorSharpAdapterError.MissingDependency);
32
- }
33
- }
27
+ const library = await ImageCompressorSharpAdapter.importer.resolve();
34
28
 
35
- // Stryker disable all
36
- static async import(): Promise<SharpModule> {
37
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
38
- return import(name) as Promise<SharpModule>;
29
+ return new ImageCompressorSharpAdapter(library.default, deps);
39
30
  }
40
- // Stryker restore all
41
31
 
42
32
  async compress(recipe: ImageCompressorStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
43
33
  const quality = recipe.quality ?? ImageCompressorSharpAdapter.DEFAULT_QUALITY;
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileRenamerPort } from "./file-renamer.port";
4
5
  import type { ImageExifClearPort, ImageExifClearStrategy } from "./image-exif-clear.port";
5
6
 
@@ -8,34 +9,24 @@ export const ImageExifClearSharpAdapterError = {
8
9
  };
9
10
 
10
11
  type Dependencies = { FileRenamer: FileRenamerPort };
11
- type SharpCallable = typeof sharp;
12
- type SharpModule = { default: SharpCallable };
12
+ type Sharp = typeof sharp;
13
13
 
14
14
  export class ImageExifClearSharpAdapter implements ImageExifClearPort {
15
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
+ "sharp",
17
+ ImageExifClearSharpAdapterError.MissingDependency,
18
+ );
19
+
15
20
  private constructor(
16
- private readonly sharp: SharpCallable,
21
+ private readonly sharp: Sharp,
17
22
  private readonly deps: Dependencies,
18
23
  ) {}
19
24
 
20
25
  static async build(deps: Dependencies): Promise<ImageExifClearSharpAdapter> {
21
- return new ImageExifClearSharpAdapter(await ImageExifClearSharpAdapter.resolve(), deps);
22
- }
23
-
24
- private static async resolve(): Promise<SharpCallable> {
25
- try {
26
- const module = await ImageExifClearSharpAdapter.import();
27
- return module.default;
28
- } catch {
29
- throw new Error(ImageExifClearSharpAdapterError.MissingDependency);
30
- }
31
- }
26
+ const library = await ImageExifClearSharpAdapter.importer.resolve();
32
27
 
33
- // Stryker disable all
34
- static async import(): Promise<SharpModule> {
35
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
36
- return import(name) as Promise<SharpModule>;
28
+ return new ImageExifClearSharpAdapter(library.default, deps);
37
29
  }
38
- // Stryker restore all
39
30
 
40
31
  async clear(recipe: ImageExifClearStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
41
32
  const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileCleanerPort } from "./file-cleaner.port";
4
5
  import type { FileRenamerPort } from "./file-renamer.port";
5
6
  import type { ImageFormatterPort, ImageFormatterStrategy } from "./image-formatter.port";
@@ -9,34 +10,24 @@ export const ImageFormatterSharpAdapterError = {
9
10
  };
10
11
 
11
12
  type Dependencies = { FileCleaner: FileCleanerPort; FileRenamer: FileRenamerPort };
12
- type SharpCallable = typeof sharp;
13
- type SharpModule = { default: SharpCallable };
13
+ type Sharp = typeof sharp;
14
14
 
15
15
  export class ImageFormatterSharpAdapter implements ImageFormatterPort {
16
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
17
+ "sharp",
18
+ ImageFormatterSharpAdapterError.MissingDependency,
19
+ );
20
+
16
21
  private constructor(
17
- private readonly sharp: SharpCallable,
22
+ private readonly sharp: Sharp,
18
23
  private readonly deps: Dependencies,
19
24
  ) {}
20
25
 
21
26
  static async build(deps: Dependencies): Promise<ImageFormatterSharpAdapter> {
22
- return new ImageFormatterSharpAdapter(await ImageFormatterSharpAdapter.resolve(), deps);
23
- }
24
-
25
- private static async resolve(): Promise<SharpCallable> {
26
- try {
27
- const module = await ImageFormatterSharpAdapter.import();
28
- return module.default;
29
- } catch {
30
- throw new Error(ImageFormatterSharpAdapterError.MissingDependency);
31
- }
32
- }
27
+ const library = await ImageFormatterSharpAdapter.importer.resolve();
33
28
 
34
- // Stryker disable all
35
- static async import(): Promise<SharpModule> {
36
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
37
- return import(name) as Promise<SharpModule>;
29
+ return new ImageFormatterSharpAdapter(library.default, deps);
38
30
  }
39
- // Stryker restore all
40
31
 
41
32
  async format(recipe: ImageFormatterStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
42
33
  const final =
@@ -1,5 +1,6 @@
1
1
  import * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileInspectionPort } from "./file-inspection.port";
4
5
  import type { ImageInfoPort, ImageInfoType } from "./image-info.port";
5
6
 
@@ -8,34 +9,24 @@ export const ImageInfoSharpAdapterError = {
8
9
  };
9
10
 
10
11
  type Dependencies = { FileInspection: FileInspectionPort; MimeRegistry: tools.MimeRegistry };
11
- type SharpCallable = typeof sharp;
12
- type SharpModule = { default: SharpCallable };
12
+ type Sharp = typeof sharp;
13
13
 
14
14
  export class ImageInfoSharpAdapter implements ImageInfoPort {
15
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
+ "sharp",
17
+ ImageInfoSharpAdapterError.MissingDependency,
18
+ );
19
+
15
20
  private constructor(
16
- private readonly sharp: SharpCallable,
21
+ private readonly sharp: Sharp,
17
22
  private readonly deps: Dependencies,
18
23
  ) {}
19
24
 
20
25
  static async build(deps: Dependencies): Promise<ImageInfoSharpAdapter> {
21
- return new ImageInfoSharpAdapter(await ImageInfoSharpAdapter.resolve(), deps);
22
- }
23
-
24
- private static async resolve(): Promise<SharpCallable> {
25
- try {
26
- const module = await ImageInfoSharpAdapter.import();
27
- return module.default;
28
- } catch {
29
- throw new Error(ImageInfoSharpAdapterError.MissingDependency);
30
- }
31
- }
26
+ const library = await ImageInfoSharpAdapter.importer.resolve();
32
27
 
33
- // Stryker disable all
34
- static async import(): Promise<SharpModule> {
35
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
36
- return import(name) as Promise<SharpModule>;
28
+ return new ImageInfoSharpAdapter(library.default, deps);
37
29
  }
38
- // Stryker restore all
39
30
 
40
31
  async inspect(input: tools.FilePathRelative | tools.FilePathAbsolute): Promise<ImageInfoType> {
41
32
  const size = await this.deps.FileInspection.size(input);
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileCleanerPort } from "./file-cleaner.port";
4
5
  import type { FileRenamerPort } from "./file-renamer.port";
5
6
  import type { ImageProcessorPort, ImageProcessorStrategy } from "./image-processor.port";
@@ -9,36 +10,25 @@ export const ImageProcessorSharpAdapterError = {
9
10
  };
10
11
 
11
12
  type Dependencies = { FileCleaner: FileCleanerPort; FileRenamer: FileRenamerPort };
12
- type SharpCallable = typeof sharp;
13
- type SharpModule = { default: SharpCallable };
13
+ type Sharp = typeof sharp;
14
14
 
15
15
  export class ImageProcessorSharpAdapter implements ImageProcessorPort {
16
16
  private static readonly DEFAULT_QUALITY = 85;
17
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
18
+ "sharp",
19
+ ImageProcessorSharpAdapterError.MissingDependency,
20
+ );
17
21
 
18
22
  private constructor(
19
- private readonly sharp: SharpCallable,
23
+ private readonly sharp: Sharp,
20
24
  private readonly deps: Dependencies,
21
25
  ) {}
22
26
 
23
27
  static async build(deps: Dependencies): Promise<ImageProcessorSharpAdapter> {
24
- return new ImageProcessorSharpAdapter(await ImageProcessorSharpAdapter.resolve(), deps);
25
- }
26
-
27
- private static async resolve(): Promise<SharpCallable> {
28
- try {
29
- const module = await ImageProcessorSharpAdapter.import();
30
- return module.default;
31
- } catch {
32
- throw new Error(ImageProcessorSharpAdapterError.MissingDependency);
33
- }
34
- }
28
+ const library = await ImageProcessorSharpAdapter.importer.resolve();
35
29
 
36
- // Stryker disable all
37
- static async import(): Promise<SharpModule> {
38
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
39
- return import(name) as Promise<SharpModule>;
30
+ return new ImageProcessorSharpAdapter(library.default, deps);
40
31
  }
41
- // Stryker restore all
42
32
 
43
33
  async process(recipe: ImageProcessorStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
44
34
  const final =
@@ -1,5 +1,6 @@
1
1
  import type * as tools from "@bgord/tools";
2
2
  import type sharp from "sharp";
3
+ import { DynamicImport } from "./dynamic-import.service";
3
4
  import type { FileRenamerPort } from "./file-renamer.port";
4
5
  import type { ImageResizerPort, ImageResizerStrategy } from "./image-resizer.port";
5
6
 
@@ -8,34 +9,24 @@ export const ImageResizerSharpAdapterError = {
8
9
  };
9
10
 
10
11
  type Dependencies = { FileRenamer: FileRenamerPort };
11
- type SharpCallable = typeof sharp;
12
- type SharpModule = { default: SharpCallable };
12
+ type Sharp = typeof sharp;
13
13
 
14
14
  export class ImageResizerSharpAdapter implements ImageResizerPort {
15
+ private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
+ "sharp",
17
+ ImageResizerSharpAdapterError.MissingDependency,
18
+ );
19
+
15
20
  private constructor(
16
- private readonly sharp: SharpCallable,
21
+ private readonly sharp: Sharp,
17
22
  private readonly deps: Dependencies,
18
23
  ) {}
19
24
 
20
25
  static async build(deps: Dependencies): Promise<ImageResizerSharpAdapter> {
21
- return new ImageResizerSharpAdapter(await ImageResizerSharpAdapter.resolve(), deps);
22
- }
23
-
24
- private static async resolve(): Promise<SharpCallable> {
25
- try {
26
- const module = await ImageResizerSharpAdapter.import();
27
- return module.default;
28
- } catch {
29
- throw new Error(ImageResizerSharpAdapterError.MissingDependency);
30
- }
31
- }
26
+ const library = await ImageResizerSharpAdapter.importer.resolve();
32
27
 
33
- // Stryker disable all
34
- static async import(): Promise<SharpModule> {
35
- const name = "sha" + "rp"; // Bun does not resolve dynamic imports with a dynamic name
36
- return import(name) as Promise<SharpModule>;
28
+ return new ImageResizerSharpAdapter(library.default, deps);
37
29
  }
38
- // Stryker restore all
39
30
 
40
31
  async resize(recipe: ImageResizerStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
41
32
  const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
package/src/index.ts CHANGED
@@ -69,6 +69,7 @@ export * from "./disk-space-checker.port";
69
69
  export * from "./disk-space-checker-noop.adapter";
70
70
  export * from "./disk-space-checker-shell.adapter";
71
71
  export * from "./dispatching-event-store";
72
+ export * from "./dynamic-import.service";
72
73
  export * from "./encryption.port";
73
74
  export * from "./encryption-aes-gcm.adapter";
74
75
  export * from "./encryption-key.vo";