diff --git a/.changeset/fresh-bushes-lick.md b/.changeset/fresh-bushes-lick.md new file mode 100644 index 00000000..f3e4a447 --- /dev/null +++ b/.changeset/fresh-bushes-lick.md @@ -0,0 +1,5 @@ +--- +"@imgproxy/imgproxy-js-core": minor +--- + +Add support for [max_result_dimension](https://docs.imgproxy.net/latest/usage/processing#max-result-dimension) option diff --git a/src/options/index.ts b/src/options/index.ts index 7ff969b6..d19be2b0 100644 --- a/src/options/index.ts +++ b/src/options/index.ts @@ -33,6 +33,7 @@ export * as keepCopyright from "./keepCopyright"; export * as maxAnimationFrames from "./maxAnimationFrames"; export * as maxAnimationFrameResolution from "./maxAnimationFrameResolution"; export * as maxBytes from "./maxBytes"; +export * as maxResultDimension from "./maxResultDimension"; export * as objectPosition from "./objectsPosition"; export * as maxSrcFileSize from "../optionsShared/maxSrcFileSize"; export * as maxSrcResolution from "../optionsShared/maxSrcResolution"; diff --git a/src/options/maxResultDimension.ts b/src/options/maxResultDimension.ts new file mode 100644 index 00000000..8324d0b5 --- /dev/null +++ b/src/options/maxResultDimension.ts @@ -0,0 +1,27 @@ +import type { + MaxResultDimension, + MaxResultDimensionOptionsPartial, +} from "../types/maxResultDimension"; +import { guardIsUndef, guardIsNotNum } from "../utils"; + +const getOpt = ( + options: MaxResultDimensionOptionsPartial +): MaxResultDimension | undefined => { + return options.max_result_dimension ?? options.mrd; +}; + +const test = (options: MaxResultDimensionOptionsPartial): boolean => + getOpt(options) !== undefined; + +const build = (options: MaxResultDimensionOptionsPartial): string => { + const maxResultDimension = getOpt(options); + + guardIsUndef(maxResultDimension, "max_result_dimension"); + guardIsNotNum(maxResultDimension, "max_result_dimension", { + addParam: { min: 0 }, + }); + + return `mrd:${maxResultDimension}`; +}; + +export { test, build }; diff --git a/src/types/index.ts b/src/types/index.ts index 1190f73f..16b263fb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -33,6 +33,7 @@ import type { KeepCopyrightOptionsPartial } from "./keepCopyright"; import type { MAFROptionsPartial } from "./maxAnimationFrameResolution"; import type { MaxAnimationFramesOptionsPartial } from "./maxAnimationFrames"; import type { MaxBytesOptionsPartial } from "./maxBytes"; +import type { MaxResultDimensionOptionsPartial } from "./maxResultDimension"; import type { MaxSrcFileSizeOptionsPartial } from "../typesShared/maxSrcFileSize"; import type { MaxSrcResolutionOptionsPartial } from "../typesShared/maxSrcResolution"; import type { MinHeightOptionsPartial } from "./minHeight"; @@ -110,6 +111,7 @@ export type Options = AdjustOptionsPartial & MAFROptionsPartial & MaxAnimationFramesOptionsPartial & MaxBytesOptionsPartial & + MaxResultDimensionOptionsPartial & MaxSrcFileSizeOptionsPartial & MaxSrcResolutionOptionsPartial & MinHeightOptionsPartial & diff --git a/src/types/maxResultDimension.ts b/src/types/maxResultDimension.ts new file mode 100644 index 00000000..93530710 --- /dev/null +++ b/src/types/maxResultDimension.ts @@ -0,0 +1,24 @@ +/** + * *Max result dimension* + * + * Allows redefining the `IMGPROXY_MAX_RESULT_DIMENSION` configuration. + * Specifies the maximum allowed dimension for the resulting image. + * + * @warning Since this option allows redefining a security restriction, + * its usage is not allowed unless the `IMGPROXY_ALLOW_SECURITY_OPTIONS` config is set to true. + * + * @see {@link https://docs.imgproxy.net/latest/usage/processing#max-result-dimension | max result dimension imgproxy docs} + */ +type MaxResultDimension = number; + +/** + * *Max result dimension* + * + * To describe the max result dimension option, you can use the keyword `max_result_dimension` or `mrd`. + */ +interface MaxResultDimensionOptionsPartial { + max_result_dimension?: MaxResultDimension; + mrd?: MaxResultDimension; +} + +export { MaxResultDimension, MaxResultDimensionOptionsPartial }; diff --git a/tests/optionsBasic/maxResultDimension.test.ts b/tests/optionsBasic/maxResultDimension.test.ts new file mode 100644 index 00000000..dbf3b1c7 --- /dev/null +++ b/tests/optionsBasic/maxResultDimension.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from "vitest"; +import { test, build } from "../../src/options/maxResultDimension"; + +describe("maxResultDimension", () => { + describe("test", () => { + it("should return true if max_result_dimension option is defined", () => { + expect(test({ max_result_dimension: 1920 })).toEqual(true); + }); + + it("should return true if mrd option is defined", () => { + expect(test({ mrd: 2560 })).toEqual(true); + }); + + it("should return false if max_result_dimension option is undefined", () => { + expect(test({})).toEqual(false); + }); + }); + + describe("build", () => { + it("should throw an error if max_result_dimension option is undefined", () => { + expect(() => build({})).toThrow( + "max_result_dimension option is undefined" + ); + }); + + it("should throw an error if max_result_dimension is not a number", () => { + // @ts-expect-error: Let's ignore an error (check for users with vanilla js). + expect(() => build({ max_result_dimension: "1920" })).toThrow( + "max_result_dimension option is not a number" + ); + }); + + it("should throw an error if max_result_dimension is less than 0", () => { + expect(() => build({ max_result_dimension: -1 })).toThrow( + "max_result_dimension option value can't be less then 0" + ); + }); + + it("should return mrd:0 if max_result_dimension option is 0", () => { + expect(build({ max_result_dimension: 0 })).toEqual("mrd:0"); + }); + + it("should return mrd:1920 if mrd option is 1920", () => { + expect(build({ mrd: 1920 })).toEqual("mrd:1920"); + }); + + it("should return mrd:2560 if max_result_dimension option is 2560", () => { + expect(build({ max_result_dimension: 2560 })).toEqual("mrd:2560"); + }); + + it("should work with 0", () => { + expect(build({ max_result_dimension: 0 })).toEqual("mrd:0"); + expect(build({ mrd: 0 })).toEqual("mrd:0"); + }); + }); +});