@bgord/bun 1.18.15 → 1.19.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 (120) hide show
  1. package/dist/{image-blur-sharp.adapter.d.ts → image-blur.adapter.d.ts} +5 -9
  2. package/dist/image-blur.adapter.d.ts.map +1 -0
  3. package/dist/image-blur.adapter.js +19 -0
  4. package/dist/image-blur.adapter.js.map +1 -0
  5. package/dist/image-blur.port.d.ts +0 -2
  6. package/dist/image-blur.port.d.ts.map +1 -1
  7. package/dist/{image-compressor-sharp.adapter.d.ts → image-compressor.adapter.d.ts} +5 -9
  8. package/dist/image-compressor.adapter.d.ts.map +1 -0
  9. package/dist/image-compressor.adapter.js +20 -0
  10. package/dist/image-compressor.adapter.js.map +1 -0
  11. package/dist/image-exif-clear.adapter.d.ts +15 -0
  12. package/dist/image-exif-clear.adapter.d.ts.map +1 -0
  13. package/dist/image-exif-clear.adapter.js +17 -0
  14. package/dist/image-exif-clear.adapter.js.map +1 -0
  15. package/dist/{image-formatter-sharp.adapter.d.ts → image-formatter.adapter.d.ts} +5 -9
  16. package/dist/image-formatter.adapter.d.ts.map +1 -0
  17. package/dist/image-formatter.adapter.js +22 -0
  18. package/dist/image-formatter.adapter.js.map +1 -0
  19. package/dist/{image-grayscale-sharp.adapter.d.ts → image-grayscale.adapter.d.ts} +5 -9
  20. package/dist/image-grayscale.adapter.d.ts.map +1 -0
  21. package/dist/image-grayscale.adapter.js +22 -0
  22. package/dist/image-grayscale.adapter.js.map +1 -0
  23. package/dist/image-info.adapter.d.ts +12 -0
  24. package/dist/image-info.adapter.d.ts.map +1 -0
  25. package/dist/image-info.adapter.js +21 -0
  26. package/dist/image-info.adapter.js.map +1 -0
  27. package/dist/{image-processor-sharp.adapter.d.ts → image-processor.adapter.d.ts} +5 -9
  28. package/dist/image-processor.adapter.d.ts.map +1 -0
  29. package/dist/image-processor.adapter.js +28 -0
  30. package/dist/image-processor.adapter.js.map +1 -0
  31. package/dist/image-processor.port.d.ts +0 -8
  32. package/dist/image-processor.port.d.ts.map +1 -1
  33. package/dist/{image-resizer-sharp.adapter.d.ts → image-resizer.adapter.d.ts} +5 -9
  34. package/dist/image-resizer.adapter.d.ts.map +1 -0
  35. package/dist/image-resizer.adapter.js +21 -0
  36. package/dist/image-resizer.adapter.js.map +1 -0
  37. package/dist/image.types.d.ts +2 -0
  38. package/dist/image.types.d.ts.map +1 -0
  39. package/dist/image.types.js +2 -0
  40. package/dist/image.types.js.map +1 -0
  41. package/dist/index.d.ts +9 -12
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +9 -12
  44. package/dist/index.js.map +1 -1
  45. package/dist/mailer-file.adapter.d.ts.map +1 -1
  46. package/dist/mailer-file.adapter.js +1 -0
  47. package/dist/mailer-file.adapter.js.map +1 -1
  48. package/dist/request-context-hono.adapter.d.ts.map +1 -1
  49. package/dist/request-context-hono.adapter.js +1 -0
  50. package/dist/request-context-hono.adapter.js.map +1 -1
  51. package/package.json +10 -15
  52. package/readme.md +9 -12
  53. package/src/image-blur.adapter.ts +29 -0
  54. package/src/image-blur.port.ts +0 -2
  55. package/src/image-compressor.adapter.ts +31 -0
  56. package/src/image-exif-clear.adapter.ts +26 -0
  57. package/src/image-formatter.adapter.ts +39 -0
  58. package/src/image-grayscale.adapter.ts +33 -0
  59. package/src/image-info.adapter.ts +26 -0
  60. package/src/image-processor.adapter.ts +46 -0
  61. package/src/image-processor.port.ts +0 -3
  62. package/src/image-resizer.adapter.ts +31 -0
  63. package/src/image.types.ts +1 -0
  64. package/src/index.ts +9 -12
  65. package/src/mailer-file.adapter.ts +1 -0
  66. package/src/request-context-hono.adapter.ts +1 -0
  67. package/dist/image-alpha-noop.adapter.d.ts +0 -6
  68. package/dist/image-alpha-noop.adapter.d.ts.map +0 -1
  69. package/dist/image-alpha-noop.adapter.js +0 -6
  70. package/dist/image-alpha-noop.adapter.js.map +0 -1
  71. package/dist/image-alpha-sharp.adapter.d.ts +0 -19
  72. package/dist/image-alpha-sharp.adapter.d.ts.map +0 -1
  73. package/dist/image-alpha-sharp.adapter.js +0 -96
  74. package/dist/image-alpha-sharp.adapter.js.map +0 -1
  75. package/dist/image-alpha-with-semaphore.adapter.d.ts +0 -14
  76. package/dist/image-alpha-with-semaphore.adapter.d.ts.map +0 -1
  77. package/dist/image-alpha-with-semaphore.adapter.js +0 -10
  78. package/dist/image-alpha-with-semaphore.adapter.js.map +0 -1
  79. package/dist/image-alpha.port.d.ts +0 -24
  80. package/dist/image-alpha.port.d.ts.map +0 -1
  81. package/dist/image-alpha.port.js +0 -2
  82. package/dist/image-alpha.port.js.map +0 -1
  83. package/dist/image-blur-sharp.adapter.d.ts.map +0 -1
  84. package/dist/image-blur-sharp.adapter.js +0 -92
  85. package/dist/image-blur-sharp.adapter.js.map +0 -1
  86. package/dist/image-compressor-sharp.adapter.d.ts.map +0 -1
  87. package/dist/image-compressor-sharp.adapter.js +0 -94
  88. package/dist/image-compressor-sharp.adapter.js.map +0 -1
  89. package/dist/image-exif-clear-sharp.adapter.d.ts +0 -19
  90. package/dist/image-exif-clear-sharp.adapter.d.ts.map +0 -1
  91. package/dist/image-exif-clear-sharp.adapter.js +0 -90
  92. package/dist/image-exif-clear-sharp.adapter.js.map +0 -1
  93. package/dist/image-formatter-sharp.adapter.d.ts.map +0 -1
  94. package/dist/image-formatter-sharp.adapter.js +0 -96
  95. package/dist/image-formatter-sharp.adapter.js.map +0 -1
  96. package/dist/image-grayscale-sharp.adapter.d.ts.map +0 -1
  97. package/dist/image-grayscale-sharp.adapter.js +0 -90
  98. package/dist/image-grayscale-sharp.adapter.js.map +0 -1
  99. package/dist/image-info-sharp.adapter.d.ts +0 -20
  100. package/dist/image-info-sharp.adapter.d.ts.map +0 -1
  101. package/dist/image-info-sharp.adapter.js +0 -95
  102. package/dist/image-info-sharp.adapter.js.map +0 -1
  103. package/dist/image-processor-sharp.adapter.d.ts.map +0 -1
  104. package/dist/image-processor-sharp.adapter.js +0 -109
  105. package/dist/image-processor-sharp.adapter.js.map +0 -1
  106. package/dist/image-resizer-sharp.adapter.d.ts.map +0 -1
  107. package/dist/image-resizer-sharp.adapter.js +0 -101
  108. package/dist/image-resizer-sharp.adapter.js.map +0 -1
  109. package/src/image-alpha-noop.adapter.ts +0 -8
  110. package/src/image-alpha-sharp.adapter.ts +0 -53
  111. package/src/image-alpha-with-semaphore.adapter.ts +0 -13
  112. package/src/image-alpha.port.ts +0 -19
  113. package/src/image-blur-sharp.adapter.ts +0 -48
  114. package/src/image-compressor-sharp.adapter.ts +0 -50
  115. package/src/image-exif-clear-sharp.adapter.ts +0 -45
  116. package/src/image-formatter-sharp.adapter.ts +0 -55
  117. package/src/image-grayscale-sharp.adapter.ts +0 -45
  118. package/src/image-info-sharp.adapter.ts +0 -49
  119. package/src/image-processor-sharp.adapter.ts +0 -73
  120. package/src/image-resizer-sharp.adapter.ts +0 -56
@@ -1 +0,0 @@
1
- {"version":3,"file":"image-resizer-sharp.adapter.js","sourceRoot":"","sources":["../src/image-resizer-sharp.adapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAIzD,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,iBAAiB,EAAE,sDAAsD;CAC1E,CAAC;AAKF,MAAM,OAAO,wBAAwB;IAOhB,KAAK;IACL,IAAI;IAPf,MAAM,CAAU,QAAQ,GAAG,aAAa,CAAC,GAAG,CAClD,OAAO,EACP,6BAA6B,CAAC,iBAAiB,CAChD,CAAC;IAEF,YACmB,KAAY,EACZ,IAAkB;qBADlB,KAAK;oBACL,IAAI;IACpB,CAAC;IAEJ,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAkB;QACnC,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QAElE,OAAO,IAAI,wBAAwB,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAA4B;;;YACvC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/E,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;YAEtE,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,YAAY,EAAE,CAAC;YACrD,MAAM,MAAM,GAAG,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAqC,CAAC;YAE9F,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YAChD,MAAM,OAAO,kCAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,QAAA,CAAC;YAE/D,MAAM,QAAQ;iBACX,MAAM,EAAE;iBACR,MAAM,CAAC;gBACN,KAAK,EAAE,MAAM,CAAC,OAAO;gBACrB,MAAM,EAAE,MAAM,CAAC,OAAO;gBACtB,GAAG,EAAE,QAAQ;gBACb,kBAAkB,EAAE,IAAI;aACzB,CAAC;iBACD,QAAQ,CAAC,MAAM,CAAC;iBAChB,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC;YAC3B,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAErD,OAAO,KAAK,CAAC;;;;;;;;;IACf,CAAC;CACF"}
@@ -1,8 +0,0 @@
1
- import type * as tools from "@bgord/tools";
2
- import type { ImageAlphaPort, ImageAlphaStrategy } from "./image-alpha.port";
3
-
4
- export class ImageAlphaNoopAdapter implements ImageAlphaPort {
5
- async flatten(recipe: ImageAlphaStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
6
- return recipe.strategy === "output_path" ? recipe.output : recipe.input;
7
- }
8
- }
@@ -1,53 +0,0 @@
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 { ImageAlphaPort, ImageAlphaStrategy } from "./image-alpha.port";
6
-
7
- export const ImageAlphaSharpAdapterError = {
8
- MissingDependency: "image.alpha.sharp.adapter.error.missing.dependency",
9
- };
10
-
11
- type Dependencies = { FileRenamer: FileRenamerPort };
12
- type Sharp = typeof sharp;
13
-
14
- export class ImageAlphaSharpAdapter implements ImageAlphaPort {
15
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
- "sharp",
17
- ImageAlphaSharpAdapterError.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<ImageAlphaSharpAdapter> {
26
- const library = await ImageAlphaSharpAdapter.importer.resolve();
27
-
28
- return new ImageAlphaSharpAdapter(library.default, deps);
29
- }
30
-
31
- async flatten(recipe: ImageAlphaStrategy): 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("-flattened"));
36
-
37
- const extension = final.getFilename().getExtension();
38
- const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
39
-
40
- const pipeline = this.sharp(recipe.input.get());
41
- using _pipeline_dispose = { [Symbol.dispose]: () => pipeline.destroy() };
42
-
43
- await pipeline
44
- .rotate()
45
- .flatten({ background: recipe.background })
46
- .toFormat(format)
47
- .toFile(temporary.get());
48
-
49
- await this.deps.FileRenamer.rename(temporary, final);
50
-
51
- return final;
52
- }
53
- }
@@ -1,13 +0,0 @@
1
- import type * as tools from "@bgord/tools";
2
- import type { ImageAlphaPort, ImageAlphaStrategy } from "./image-alpha.port";
3
- import type { Semaphore } from "./semaphore.service";
4
-
5
- type Dependencies = { inner: ImageAlphaPort; semaphore: Semaphore };
6
-
7
- export class ImageAlphaWithSemaphoreAdapter implements ImageAlphaPort {
8
- constructor(private readonly deps: Dependencies) {}
9
-
10
- async flatten(recipe: ImageAlphaStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
11
- return this.deps.semaphore.run(() => this.deps.inner.flatten(recipe));
12
- }
13
- }
@@ -1,19 +0,0 @@
1
- import type * as tools from "@bgord/tools";
2
-
3
- type ImageAlphaBackground = string | { r: number; g: number; b: number; alpha?: number };
4
- type ImageAlphaOutputPathStrategy = {
5
- strategy: "output_path";
6
- input: tools.FilePathRelative | tools.FilePathAbsolute;
7
- output: tools.FilePathRelative | tools.FilePathAbsolute;
8
- background: ImageAlphaBackground;
9
- };
10
- type ImageAlphaInPlaceStrategy = {
11
- strategy: "in_place";
12
- input: tools.FilePathRelative | tools.FilePathAbsolute;
13
- background: ImageAlphaBackground;
14
- };
15
- export type ImageAlphaStrategy = ImageAlphaInPlaceStrategy | ImageAlphaOutputPathStrategy;
16
-
17
- export interface ImageAlphaPort {
18
- flatten(recipe: ImageAlphaStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute>;
19
- }
@@ -1,48 +0,0 @@
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 { ImageBlurPort, ImageBlurStrategy } from "./image-blur.port";
6
-
7
- export const ImageBlurSharpAdapterError = {
8
- MissingDependency: "image.blur.sharp.adapter.error.missing.dependency",
9
- };
10
-
11
- type Dependencies = { FileRenamer: FileRenamerPort };
12
- type Sharp = typeof sharp;
13
-
14
- export class ImageBlurSharpAdapter implements ImageBlurPort {
15
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
- "sharp",
17
- ImageBlurSharpAdapterError.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<ImageBlurSharpAdapter> {
26
- const library = await ImageBlurSharpAdapter.importer.resolve();
27
-
28
- return new ImageBlurSharpAdapter(library.default, deps);
29
- }
30
-
31
- async blur(recipe: ImageBlurStrategy): 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("-blurred"));
36
-
37
- const extension = final.getFilename().getExtension();
38
- const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
39
-
40
- const pipeline = this.sharp(recipe.input.get());
41
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
42
-
43
- await pipeline.rotate().blur(recipe.sigma).toFormat(format).toFile(temporary.get());
44
- await this.deps.FileRenamer.rename(temporary, final);
45
-
46
- return final;
47
- }
48
- }
@@ -1,50 +0,0 @@
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 { ImageCompressorPort, ImageCompressorStrategy } from "./image-compressor.port";
6
-
7
- export const ImageCompressorSharpAdapterError = {
8
- MissingDependency: "image.compressor.sharp.adapter.error.missing.dependency",
9
- };
10
-
11
- type Dependencies = { FileRenamer: FileRenamerPort };
12
- type Sharp = typeof sharp;
13
-
14
- export class ImageCompressorSharpAdapter implements ImageCompressorPort {
15
- private static readonly DEFAULT_QUALITY = 85;
16
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
17
- "sharp",
18
- ImageCompressorSharpAdapterError.MissingDependency,
19
- );
20
-
21
- private constructor(
22
- private readonly sharp: Sharp,
23
- private readonly deps: Dependencies,
24
- ) {}
25
-
26
- static async build(deps: Dependencies): Promise<ImageCompressorSharpAdapter> {
27
- const library = await ImageCompressorSharpAdapter.importer.resolve();
28
-
29
- return new ImageCompressorSharpAdapter(library.default, deps);
30
- }
31
-
32
- async compress(recipe: ImageCompressorStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
33
- const quality = recipe.quality ?? ImageCompressorSharpAdapter.DEFAULT_QUALITY;
34
-
35
- const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
36
- const filename = final.getFilename();
37
- const temporary = final.withFilename(filename.withSuffix("-compressed"));
38
-
39
- const extension = final.getFilename().getExtension();
40
- const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
41
-
42
- const pipeline = this.sharp(recipe.input.get());
43
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
44
-
45
- await pipeline.rotate().toFormat(format, { quality }).toFile(temporary.get());
46
- await this.deps.FileRenamer.rename(temporary, final);
47
-
48
- return final;
49
- }
50
- }
@@ -1,45 +0,0 @@
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 { ImageExifClearPort, ImageExifClearStrategy } from "./image-exif-clear.port";
6
-
7
- export const ImageExifClearSharpAdapterError = {
8
- MissingDependency: "image.exif.clear.sharp.adapter.error.missing.dependency",
9
- };
10
-
11
- type Dependencies = { FileRenamer: FileRenamerPort };
12
- type Sharp = typeof sharp;
13
-
14
- export class ImageExifClearSharpAdapter implements ImageExifClearPort {
15
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
- "sharp",
17
- ImageExifClearSharpAdapterError.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<ImageExifClearSharpAdapter> {
26
- const library = await ImageExifClearSharpAdapter.importer.resolve();
27
-
28
- return new ImageExifClearSharpAdapter(library.default, deps);
29
- }
30
-
31
- async clear(recipe: ImageExifClearStrategy): 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("-exif-cleared"));
36
-
37
- const pipeline = this.sharp(recipe.input.get());
38
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
39
-
40
- await pipeline.rotate().toFile(temporary.get());
41
- await this.deps.FileRenamer.rename(temporary, final);
42
-
43
- return final;
44
- }
45
- }
@@ -1,55 +0,0 @@
1
- import type * as tools from "@bgord/tools";
2
- import type sharp from "sharp";
3
- import { DynamicImport } from "./dynamic-import.service";
4
- import type { FileCleanerPort } from "./file-cleaner.port";
5
- import type { FileRenamerPort } from "./file-renamer.port";
6
- import type { ImageFormatterPort, ImageFormatterStrategy } from "./image-formatter.port";
7
-
8
- export const ImageFormatterSharpAdapterError = {
9
- MissingDependency: "image.formatter.sharp.adapter.error.missing.dependency",
10
- };
11
-
12
- type Dependencies = { FileCleaner: FileCleanerPort; FileRenamer: FileRenamerPort };
13
- type Sharp = typeof sharp;
14
-
15
- export class ImageFormatterSharpAdapter implements ImageFormatterPort {
16
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
17
- "sharp",
18
- ImageFormatterSharpAdapterError.MissingDependency,
19
- );
20
-
21
- private constructor(
22
- private readonly sharp: Sharp,
23
- private readonly deps: Dependencies,
24
- ) {}
25
-
26
- static async build(deps: Dependencies): Promise<ImageFormatterSharpAdapter> {
27
- const library = await ImageFormatterSharpAdapter.importer.resolve();
28
-
29
- return new ImageFormatterSharpAdapter(library.default, deps);
30
- }
31
-
32
- async format(recipe: ImageFormatterStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
33
- const final =
34
- recipe.strategy === "output_path"
35
- ? recipe.output
36
- : recipe.input.withFilename(recipe.input.getFilename().withExtension(recipe.to));
37
-
38
- const temporary = final.withFilename(final.getFilename().withSuffix("-formatted"));
39
-
40
- const extension = final.getFilename().getExtension();
41
- const encoder = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
42
-
43
- const pipeline = this.sharp(recipe.input.get());
44
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
45
-
46
- await pipeline.toFormat(encoder).toFile(temporary.get());
47
- await this.deps.FileRenamer.rename(temporary, final);
48
-
49
- if (recipe.strategy === "in_place" && final.get() !== recipe.input.get()) {
50
- await this.deps.FileCleaner.delete(recipe.input.get());
51
- }
52
-
53
- return final;
54
- }
55
- }
@@ -1,45 +0,0 @@
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
- }
@@ -1,49 +0,0 @@
1
- import * as tools from "@bgord/tools";
2
- import type sharp from "sharp";
3
- import * as v from "valibot";
4
- import { DynamicImport } from "./dynamic-import.service";
5
- import type { FileInspectionPort } from "./file-inspection.port";
6
- import type { ImageInfoPort, ImageInfoType } from "./image-info.port";
7
-
8
- export const ImageInfoSharpAdapterError = {
9
- MissingDependency: "image.info.sharp.adapter.error.missing.dependency",
10
- };
11
-
12
- type Dependencies = { FileInspection: FileInspectionPort; MimeRegistry: tools.MimeRegistry };
13
- type Sharp = typeof sharp;
14
-
15
- export class ImageInfoSharpAdapter implements ImageInfoPort {
16
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
17
- "sharp",
18
- ImageInfoSharpAdapterError.MissingDependency,
19
- );
20
-
21
- private constructor(
22
- private readonly sharp: Sharp,
23
- private readonly deps: Dependencies,
24
- ) {}
25
-
26
- static async build(deps: Dependencies): Promise<ImageInfoSharpAdapter> {
27
- const library = await ImageInfoSharpAdapter.importer.resolve();
28
-
29
- return new ImageInfoSharpAdapter(library.default, deps);
30
- }
31
-
32
- async inspect(input: tools.FilePathRelative | tools.FilePathAbsolute): Promise<ImageInfoType> {
33
- const size = await this.deps.FileInspection.size(input);
34
-
35
- const pipeline = this.sharp(input.get());
36
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
37
-
38
- const metadata = await pipeline.metadata();
39
-
40
- const width = v.parse(tools.ImageWidth, metadata.width);
41
- const height = v.parse(tools.ImageHeight, metadata.height);
42
- const extension = v.parse(tools.Extension, metadata.format);
43
- const mime = this.deps.MimeRegistry.fromExtension(extension);
44
-
45
- if (!mime) throw new Error(tools.MimeRegistryError.MimeNotFound);
46
-
47
- return { width, height, mime, size };
48
- }
49
- }
@@ -1,73 +0,0 @@
1
- import type * as tools from "@bgord/tools";
2
- import type sharp from "sharp";
3
- import { DynamicImport } from "./dynamic-import.service";
4
- import type { FileCleanerPort } from "./file-cleaner.port";
5
- import type { FileRenamerPort } from "./file-renamer.port";
6
- import type { ImageProcessorPort, ImageProcessorStrategy } from "./image-processor.port";
7
-
8
- export const ImageProcessorSharpAdapterError = {
9
- MissingDependency: "image.processor.sharp.adapter.error.missing.dependency",
10
- };
11
-
12
- type Dependencies = { FileCleaner: FileCleanerPort; FileRenamer: FileRenamerPort };
13
- type Sharp = typeof sharp;
14
-
15
- export class ImageProcessorSharpAdapter implements ImageProcessorPort {
16
- private static readonly DEFAULT_QUALITY = 85;
17
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
18
- "sharp",
19
- ImageProcessorSharpAdapterError.MissingDependency,
20
- );
21
-
22
- private constructor(
23
- private readonly sharp: Sharp,
24
- private readonly deps: Dependencies,
25
- ) {}
26
-
27
- static async build(deps: Dependencies): Promise<ImageProcessorSharpAdapter> {
28
- const library = await ImageProcessorSharpAdapter.importer.resolve();
29
-
30
- return new ImageProcessorSharpAdapter(library.default, deps);
31
- }
32
-
33
- async process(recipe: ImageProcessorStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
34
- const final =
35
- recipe.strategy === "output_path"
36
- ? recipe.output
37
- : recipe.input.withFilename(recipe.input.getFilename().withExtension(recipe.to));
38
-
39
- const temporary = final.withFilename(final.getFilename().withSuffix("-processed"));
40
-
41
- const extension = final.getFilename().getExtension();
42
- const encoder = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
43
-
44
- const quality = recipe.quality ?? ImageProcessorSharpAdapter.DEFAULT_QUALITY;
45
-
46
- const pipeline = this.sharp(recipe.input.get());
47
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
48
-
49
- let processor = pipeline.rotate();
50
-
51
- if (recipe.background) {
52
- processor = processor.flatten({ background: recipe.background });
53
- }
54
-
55
- processor = processor.resize({
56
- width: recipe.maxSide,
57
- height: recipe.maxSide,
58
- fit: "inside",
59
- withoutEnlargement: true,
60
- });
61
-
62
- processor = processor.toFormat(encoder, { quality });
63
-
64
- await processor.toFile(temporary.get());
65
- await this.deps.FileRenamer.rename(temporary, final);
66
-
67
- if (recipe.strategy === "in_place" && final.get() !== recipe.input.get()) {
68
- await this.deps.FileCleaner.delete(recipe.input.get());
69
- }
70
-
71
- return final;
72
- }
73
- }
@@ -1,56 +0,0 @@
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 { ImageResizerPort, ImageResizerStrategy } from "./image-resizer.port";
6
-
7
- export const ImageResizerSharpAdapterError = {
8
- MissingDependency: "image.resizer.sharp.adapter.error.missing.dependency",
9
- };
10
-
11
- type Dependencies = { FileRenamer: FileRenamerPort };
12
- type Sharp = typeof sharp;
13
-
14
- export class ImageResizerSharpAdapter implements ImageResizerPort {
15
- private static readonly importer = DynamicImport.for<{ default: Sharp }>(
16
- "sharp",
17
- ImageResizerSharpAdapterError.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<ImageResizerSharpAdapter> {
26
- const library = await ImageResizerSharpAdapter.importer.resolve();
27
-
28
- return new ImageResizerSharpAdapter(library.default, deps);
29
- }
30
-
31
- async resize(recipe: ImageResizerStrategy): Promise<tools.FilePathRelative | tools.FilePathAbsolute> {
32
- const final = recipe.strategy === "output_path" ? recipe.output : recipe.input;
33
- const filename = final.getFilename();
34
- const temporary = final.withFilename(filename.withSuffix("-resized"));
35
-
36
- const extension = final.getFilename().getExtension();
37
- const format = (extension === "jpg" ? "jpeg" : extension) as keyof import("sharp").FormatEnum;
38
-
39
- const pipeline = this.sharp(recipe.input.get());
40
- using _sharp_ = { [Symbol.dispose]: () => pipeline.destroy() };
41
-
42
- await pipeline
43
- .rotate()
44
- .resize({
45
- width: recipe.maxSide,
46
- height: recipe.maxSide,
47
- fit: "inside",
48
- withoutEnlargement: true,
49
- })
50
- .toFormat(format)
51
- .toFile(temporary.get());
52
- await this.deps.FileRenamer.rename(temporary, final);
53
-
54
- return final;
55
- }
56
- }