@payloadcms/transformer-sharp 4.0.0-internal.58473b9

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 (75) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +105 -0
  3. package/dist/canResizeImage.d.ts +2 -0
  4. package/dist/canResizeImage.d.ts.map +1 -0
  5. package/dist/canResizeImage.js +12 -0
  6. package/dist/canResizeImage.js.map +1 -0
  7. package/dist/createSharpFromFile.d.ts +8 -0
  8. package/dist/createSharpFromFile.d.ts.map +1 -0
  9. package/dist/createSharpFromFile.js +31 -0
  10. package/dist/createSharpFromFile.js.map +1 -0
  11. package/dist/createSharpFromFile.spec.js +120 -0
  12. package/dist/createSharpFromFile.spec.js.map +1 -0
  13. package/dist/getImageResizeAction.d.ts +24 -0
  14. package/dist/getImageResizeAction.d.ts.map +1 -0
  15. package/dist/getImageResizeAction.js +56 -0
  16. package/dist/getImageResizeAction.js.map +1 -0
  17. package/dist/handleRequest.d.ts +14 -0
  18. package/dist/handleRequest.d.ts.map +1 -0
  19. package/dist/handleRequest.js +143 -0
  20. package/dist/handleRequest.js.map +1 -0
  21. package/dist/handleRequest.spec.js +188 -0
  22. package/dist/handleRequest.spec.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +3 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/initSharpCollections.d.ts +14 -0
  28. package/dist/initSharpCollections.d.ts.map +1 -0
  29. package/dist/initSharpCollections.js +84 -0
  30. package/dist/initSharpCollections.js.map +1 -0
  31. package/dist/initSharpCollections.spec.js +185 -0
  32. package/dist/initSharpCollections.spec.js.map +1 -0
  33. package/dist/mapWithBoundedConcurrency.d.ts +7 -0
  34. package/dist/mapWithBoundedConcurrency.d.ts.map +1 -0
  35. package/dist/mapWithBoundedConcurrency.js +26 -0
  36. package/dist/mapWithBoundedConcurrency.js.map +1 -0
  37. package/dist/mapWithBoundedConcurrency.spec.js +35 -0
  38. package/dist/mapWithBoundedConcurrency.spec.js.map +1 -0
  39. package/dist/optionallyAppendMetadata.d.ts +9 -0
  40. package/dist/optionallyAppendMetadata.d.ts.map +1 -0
  41. package/dist/optionallyAppendMetadata.js +17 -0
  42. package/dist/optionallyAppendMetadata.js.map +1 -0
  43. package/dist/parseDynamicResize.d.ts +17 -0
  44. package/dist/parseDynamicResize.d.ts.map +1 -0
  45. package/dist/parseDynamicResize.js +117 -0
  46. package/dist/parseDynamicResize.js.map +1 -0
  47. package/dist/parseDynamicResize.spec.js +123 -0
  48. package/dist/parseDynamicResize.spec.js.map +1 -0
  49. package/dist/prepareLegacyUpload.d.ts +14 -0
  50. package/dist/prepareLegacyUpload.d.ts.map +1 -0
  51. package/dist/prepareLegacyUpload.js +147 -0
  52. package/dist/prepareLegacyUpload.js.map +1 -0
  53. package/dist/prepareLegacyUpload.spec.js +95 -0
  54. package/dist/prepareLegacyUpload.spec.js.map +1 -0
  55. package/dist/sanitizeResizeConfig.d.ts +11 -0
  56. package/dist/sanitizeResizeConfig.d.ts.map +1 -0
  57. package/dist/sanitizeResizeConfig.js +20 -0
  58. package/dist/sanitizeResizeConfig.js.map +1 -0
  59. package/dist/sharpTransformer.d.ts +10 -0
  60. package/dist/sharpTransformer.d.ts.map +1 -0
  61. package/dist/sharpTransformer.js +122 -0
  62. package/dist/sharpTransformer.js.map +1 -0
  63. package/dist/sharpTransformer.spec.js +117 -0
  64. package/dist/sharpTransformer.spec.js.map +1 -0
  65. package/dist/transformFile.d.ts +12 -0
  66. package/dist/transformFile.d.ts.map +1 -0
  67. package/dist/transformFile.js +264 -0
  68. package/dist/transformFile.js.map +1 -0
  69. package/dist/transformFile.spec.js +259 -0
  70. package/dist/transformFile.spec.js.map +1 -0
  71. package/dist/types.d.ts +150 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +7 -0
  74. package/dist/types.js.map +1 -0
  75. package/package.json +56 -0
@@ -0,0 +1,10 @@
1
+ import type { UploadTransformer } from 'payload';
2
+ import type { TransformerWithInternalBridge } from 'payload/internal';
3
+ import type { SharpDynamicDefaults, SharpTransformerOptions } from './types.js';
4
+ export declare function resolveSharpDynamicDefaults(overrides?: SharpDynamicDefaults): Required<SharpDynamicDefaults>;
5
+ /**
6
+ * Payload's official Sharp-based file transformer: upload-time image processing,
7
+ * plus opt-in (`dynamic`) request-time width/height/`withoutEnlargement` resizing.
8
+ */
9
+ export declare function sharpTransformer(options?: SharpTransformerOptions): TransformerWithInternalBridge & UploadTransformer;
10
+ //# sourceMappingURL=sharpTransformer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharpTransformer.d.ts","sourceRoot":"","sources":["../src/sharpTransformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkD,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAChG,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAA;AAKrE,OAAO,KAAK,EAAE,oBAAoB,EAAuB,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAiBpG,wBAAgB,2BAA2B,CACzC,SAAS,CAAC,EAAE,oBAAoB,GAC/B,QAAQ,CAAC,oBAAoB,CAAC,CAShC;AAgBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,uBAA4B,GACpC,6BAA6B,GAAG,iBAAiB,CAmDnD"}
@@ -0,0 +1,122 @@
1
+ import { getUploadTransformerInternal, uploadTransformerInternal } from 'payload/internal';
2
+ import bundledSharp from 'sharp';
3
+ import { createHandleRequest } from './handleRequest.js';
4
+ import { initSharpCollections } from './initSharpCollections.js';
5
+ import { parseDynamicResize } from './parseDynamicResize.js';
6
+ import { createPrepareLegacyUpload } from './prepareLegacyUpload.js';
7
+ import { createTransformFile } from './transformFile.js';
8
+ const DEFAULT_MIME_TYPES = [
9
+ 'image/jpeg',
10
+ 'image/png',
11
+ 'image/gif',
12
+ 'image/webp',
13
+ 'image/tiff',
14
+ 'image/avif'
15
+ ];
16
+ export function resolveSharpDynamicDefaults(overrides) {
17
+ return {
18
+ fit: overrides?.fit ?? 'cover',
19
+ maxHeight: overrides?.maxHeight ?? 4096,
20
+ maxPixels: overrides?.maxPixels ?? 16_777_216,
21
+ maxWidth: overrides?.maxWidth ?? 4096,
22
+ position: overrides?.position ?? 'center',
23
+ withoutEnlargement: overrides?.withoutEnlargement ?? false
24
+ };
25
+ }
26
+ /**
27
+ * Normalizes the `dynamic` option: `false`/omitted disables request-time
28
+ * resizing, `true` enables it with defaults for every upload collection.
29
+ */ function resolveSharpDynamicOptions(dynamic) {
30
+ if (!dynamic) {
31
+ return false;
32
+ }
33
+ return dynamic === true ? {} : dynamic;
34
+ }
35
+ /**
36
+ * Payload's official Sharp-based file transformer: upload-time image processing,
37
+ * plus opt-in (`dynamic`) request-time width/height/`withoutEnlargement` resizing.
38
+ */ export function sharpTransformer(options = {}) {
39
+ const dynamicOptions = resolveSharpDynamicOptions(options.dynamic);
40
+ const dynamicDefaults = resolveSharpDynamicDefaults(dynamicOptions || undefined);
41
+ const sharpDependency = options.sharp ?? bundledSharp;
42
+ const collections = options.collections ?? {};
43
+ const handlesCollection = ({ collectionSlug })=>Boolean(collections[collectionSlug]);
44
+ const transformer = {
45
+ slug: options.slug ?? 'sharp',
46
+ canTransform: (args)=>{
47
+ // Upload-time eligibility is already decided by the MIME match that ran
48
+ // before `canTransform`; only dynamic request routing needs the query.
49
+ if (args.operation === 'upload') {
50
+ return true;
51
+ }
52
+ if (!dynamicOptions || dynamicOptions.collections && !dynamicOptions.collections.includes(args.collectionSlug)) {
53
+ return false;
54
+ }
55
+ const result = parseDynamicResize({
56
+ limits: dynamicDefaults,
57
+ searchParams: args.req.searchParams ?? new URLSearchParams()
58
+ });
59
+ return result.isRouted;
60
+ },
61
+ handleRequest: createHandleRequest({
62
+ dynamicDefaults,
63
+ sharpDependency
64
+ }),
65
+ init: (config)=>{
66
+ assertDynamicCollectionsExist({
67
+ config,
68
+ dynamicOptions
69
+ });
70
+ assertCollectionsOwnedOnce({
71
+ collections,
72
+ config,
73
+ transformer
74
+ });
75
+ return initSharpCollections({
76
+ collections,
77
+ config
78
+ });
79
+ },
80
+ mimeTypes: DEFAULT_MIME_TYPES,
81
+ [uploadTransformerInternal]: {
82
+ handlesCollection,
83
+ prepareUpload: createPrepareLegacyUpload({
84
+ collections,
85
+ sharpDependency
86
+ })
87
+ },
88
+ // `options` here is always what this transformer computed via `prepareUpload`'s
89
+ // `transform` callback; the public contract's `unknown` just reflects that core never inspects it.
90
+ transformFile: createTransformFile({
91
+ sharpDependency
92
+ })
93
+ };
94
+ return transformer;
95
+ }
96
+ /**
97
+ * Core drives each upload through a single Sharp instance, so a collection's upload settings
98
+ * split across instances would silently lose all but one of them.
99
+ */ function assertCollectionsOwnedOnce({ collections, config, transformer }) {
100
+ for (const [collectionSlug, collectionConfig] of Object.entries(collections)){
101
+ if (!collectionConfig) {
102
+ continue;
103
+ }
104
+ const owners = (config.upload?.transformers ?? []).filter((candidate)=>candidate === transformer || getUploadTransformerInternal(candidate)?.handlesCollection?.({
105
+ collectionSlug
106
+ }));
107
+ if (owners.length > 1) {
108
+ throw new Error(`Invalid \`sharpTransformer({ collections })\` configuration: collection "${collectionSlug}" has upload settings on more than one Sharp transformer: ${owners.map((owner)=>`"${owner.slug}"`).join(', ')}. Configure each collection on exactly one instance.`);
109
+ }
110
+ }
111
+ }
112
+ function assertDynamicCollectionsExist({ config, dynamicOptions }) {
113
+ if (!dynamicOptions || !dynamicOptions.collections) {
114
+ return;
115
+ }
116
+ const invalidSlugs = dynamicOptions.collections.filter((slug)=>!config.collections?.some((collection)=>collection.slug === slug && collection.upload));
117
+ if (invalidSlugs.length > 0) {
118
+ throw new Error(`Invalid \`sharpTransformer({ dynamic: { collections } })\` configuration: not an upload-enabled collection: ${invalidSlugs.map((slug)=>`"${slug}"`).join(', ')}.`);
119
+ }
120
+ }
121
+
122
+ //# sourceMappingURL=sharpTransformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sharpTransformer.ts"],"sourcesContent":["import type { Config, TransformFileArgs, TransformFileResult, UploadTransformer } from 'payload'\nimport type { TransformerWithInternalBridge } from 'payload/internal'\n\nimport { getUploadTransformerInternal, uploadTransformerInternal } from 'payload/internal'\nimport bundledSharp from 'sharp'\n\nimport type { SharpDynamicDefaults, SharpDynamicOptions, SharpTransformerOptions } from './types.js'\n\nimport { createHandleRequest } from './handleRequest.js'\nimport { initSharpCollections } from './initSharpCollections.js'\nimport { parseDynamicResize } from './parseDynamicResize.js'\nimport { createPrepareLegacyUpload } from './prepareLegacyUpload.js'\nimport { createTransformFile } from './transformFile.js'\n\nconst DEFAULT_MIME_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/tiff',\n 'image/avif',\n]\n\nexport function resolveSharpDynamicDefaults(\n overrides?: SharpDynamicDefaults,\n): Required<SharpDynamicDefaults> {\n return {\n fit: overrides?.fit ?? 'cover',\n maxHeight: overrides?.maxHeight ?? 4096,\n maxPixels: overrides?.maxPixels ?? 16_777_216,\n maxWidth: overrides?.maxWidth ?? 4096,\n position: overrides?.position ?? 'center',\n withoutEnlargement: overrides?.withoutEnlargement ?? false,\n }\n}\n\n/**\n * Normalizes the `dynamic` option: `false`/omitted disables request-time\n * resizing, `true` enables it with defaults for every upload collection.\n */\nfunction resolveSharpDynamicOptions(\n dynamic: SharpTransformerOptions['dynamic'],\n): false | SharpDynamicOptions {\n if (!dynamic) {\n return false\n }\n\n return dynamic === true ? {} : dynamic\n}\n\n/**\n * Payload's official Sharp-based file transformer: upload-time image processing,\n * plus opt-in (`dynamic`) request-time width/height/`withoutEnlargement` resizing.\n */\nexport function sharpTransformer(\n options: SharpTransformerOptions = {},\n): TransformerWithInternalBridge & UploadTransformer {\n const dynamicOptions = resolveSharpDynamicOptions(options.dynamic)\n const dynamicDefaults = resolveSharpDynamicDefaults(dynamicOptions || undefined)\n const sharpDependency = options.sharp ?? bundledSharp\n const collections = options.collections ?? {}\n const handlesCollection = ({ collectionSlug }: { collectionSlug: string }) =>\n Boolean(collections[collectionSlug])\n\n const transformer: TransformerWithInternalBridge & UploadTransformer = {\n slug: options.slug ?? 'sharp',\n canTransform: (args) => {\n // Upload-time eligibility is already decided by the MIME match that ran\n // before `canTransform`; only dynamic request routing needs the query.\n if (args.operation === 'upload') {\n return true\n }\n\n if (\n !dynamicOptions ||\n (dynamicOptions.collections && !dynamicOptions.collections.includes(args.collectionSlug))\n ) {\n return false\n }\n\n const result = parseDynamicResize({\n limits: dynamicDefaults,\n searchParams: args.req.searchParams ?? new URLSearchParams(),\n })\n\n return result.isRouted\n },\n handleRequest: createHandleRequest({ dynamicDefaults, sharpDependency }),\n init: (config) => {\n assertDynamicCollectionsExist({ config, dynamicOptions })\n assertCollectionsOwnedOnce({ collections, config, transformer })\n\n return initSharpCollections({ collections, config })\n },\n mimeTypes: DEFAULT_MIME_TYPES,\n [uploadTransformerInternal]: {\n handlesCollection,\n prepareUpload: createPrepareLegacyUpload({ collections, sharpDependency }),\n },\n // `options` here is always what this transformer computed via `prepareUpload`'s\n // `transform` callback; the public contract's `unknown` just reflects that core never inspects it.\n transformFile: createTransformFile({ sharpDependency }) as (\n args: TransformFileArgs,\n ) => Promise<TransformFileResult>,\n }\n\n return transformer\n}\n\n/**\n * Core drives each upload through a single Sharp instance, so a collection's upload settings\n * split across instances would silently lose all but one of them.\n */\nfunction assertCollectionsOwnedOnce({\n collections,\n config,\n transformer,\n}: {\n collections: NonNullable<SharpTransformerOptions['collections']>\n config: Config\n transformer: UploadTransformer\n}): void {\n for (const [collectionSlug, collectionConfig] of Object.entries(collections)) {\n if (!collectionConfig) {\n continue\n }\n\n const owners = (config.upload?.transformers ?? []).filter(\n (candidate) =>\n candidate === transformer ||\n getUploadTransformerInternal(candidate)?.handlesCollection?.({ collectionSlug }),\n )\n\n if (owners.length > 1) {\n throw new Error(\n `Invalid \\`sharpTransformer({ collections })\\` configuration: collection \"${collectionSlug}\" has upload settings on more than one Sharp transformer: ${owners.map((owner) => `\"${owner.slug}\"`).join(', ')}. Configure each collection on exactly one instance.`,\n )\n }\n }\n}\n\nfunction assertDynamicCollectionsExist({\n config,\n dynamicOptions,\n}: {\n config: Config\n dynamicOptions: false | SharpDynamicOptions\n}): void {\n if (!dynamicOptions || !dynamicOptions.collections) {\n return\n }\n\n const invalidSlugs = dynamicOptions.collections.filter(\n (slug) =>\n !config.collections?.some((collection) => collection.slug === slug && collection.upload),\n )\n\n if (invalidSlugs.length > 0) {\n throw new Error(\n `Invalid \\`sharpTransformer({ dynamic: { collections } })\\` configuration: not an upload-enabled collection: ${invalidSlugs.map((slug) => `\"${slug}\"`).join(', ')}.`,\n )\n }\n}\n"],"names":["getUploadTransformerInternal","uploadTransformerInternal","bundledSharp","createHandleRequest","initSharpCollections","parseDynamicResize","createPrepareLegacyUpload","createTransformFile","DEFAULT_MIME_TYPES","resolveSharpDynamicDefaults","overrides","fit","maxHeight","maxPixels","maxWidth","position","withoutEnlargement","resolveSharpDynamicOptions","dynamic","sharpTransformer","options","dynamicOptions","dynamicDefaults","undefined","sharpDependency","sharp","collections","handlesCollection","collectionSlug","Boolean","transformer","slug","canTransform","args","operation","includes","result","limits","searchParams","req","URLSearchParams","isRouted","handleRequest","init","config","assertDynamicCollectionsExist","assertCollectionsOwnedOnce","mimeTypes","prepareUpload","transformFile","collectionConfig","Object","entries","owners","upload","transformers","filter","candidate","length","Error","map","owner","join","invalidSlugs","some","collection"],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolveSharpDynamicDefaults, sharpTransformer } from './sharpTransformer.js';
3
+ describe('sharpTransformer', ()=>{
4
+ it("should default mimeTypes to canResizeImage's allow-list exactly, excluding jxl", ()=>{
5
+ expect(sharpTransformer().mimeTypes).toEqual([
6
+ 'image/jpeg',
7
+ 'image/png',
8
+ 'image/gif',
9
+ 'image/webp',
10
+ 'image/tiff',
11
+ 'image/avif'
12
+ ]);
13
+ });
14
+ it('should reject dynamic.collections that are unknown or not upload-enabled on init', ()=>{
15
+ const config = {
16
+ collections: [
17
+ {
18
+ slug: 'media',
19
+ fields: [],
20
+ upload: true
21
+ },
22
+ {
23
+ slug: 'posts',
24
+ fields: []
25
+ }
26
+ ]
27
+ };
28
+ const transformer = sharpTransformer({
29
+ dynamic: {
30
+ collections: [
31
+ 'posts',
32
+ 'missing'
33
+ ]
34
+ }
35
+ });
36
+ expect(()=>transformer.init(config)).toThrow(/not an upload-enabled collection: "posts", "missing"/);
37
+ });
38
+ it('should reject a collection whose upload settings are configured on more than one instance on init', ()=>{
39
+ const first = sharpTransformer({
40
+ collections: {
41
+ media: {
42
+ variants: []
43
+ }
44
+ }
45
+ });
46
+ const second = sharpTransformer({
47
+ slug: 'sharp-second',
48
+ collections: {
49
+ media: {}
50
+ }
51
+ });
52
+ const config = {
53
+ collections: [
54
+ {
55
+ slug: 'media',
56
+ fields: [],
57
+ upload: true
58
+ }
59
+ ],
60
+ upload: {
61
+ transformers: [
62
+ first,
63
+ second
64
+ ]
65
+ }
66
+ };
67
+ expect(()=>second.init(config)).toThrow(/"media" has upload settings on more than one Sharp transformer: "sharp", "sharp-second"/);
68
+ });
69
+ it('should allow different collections configured on different instances', ()=>{
70
+ const first = sharpTransformer({
71
+ collections: {
72
+ media: {}
73
+ }
74
+ });
75
+ const second = sharpTransformer({
76
+ slug: 'sharp-second',
77
+ collections: {
78
+ photos: {}
79
+ }
80
+ });
81
+ const config = {
82
+ collections: [
83
+ {
84
+ slug: 'media',
85
+ fields: [],
86
+ upload: true
87
+ },
88
+ {
89
+ slug: 'photos',
90
+ fields: [],
91
+ upload: true
92
+ }
93
+ ],
94
+ upload: {
95
+ transformers: [
96
+ first,
97
+ second
98
+ ]
99
+ }
100
+ };
101
+ expect(()=>second.init(config)).not.toThrow();
102
+ });
103
+ });
104
+ describe('resolveSharpDynamicDefaults', ()=>{
105
+ it('should default to fit=cover, position=center, maxWidth=4096, maxHeight=4096, maxPixels=16_777_216, withoutEnlargement=false', ()=>{
106
+ expect(resolveSharpDynamicDefaults()).toEqual({
107
+ fit: 'cover',
108
+ maxHeight: 4096,
109
+ maxPixels: 16_777_216,
110
+ maxWidth: 4096,
111
+ position: 'center',
112
+ withoutEnlargement: false
113
+ });
114
+ });
115
+ });
116
+
117
+ //# sourceMappingURL=sharpTransformer.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sharpTransformer.spec.ts"],"sourcesContent":["import type { Config, UploadCollectionSlug } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { resolveSharpDynamicDefaults, sharpTransformer } from './sharpTransformer.js'\n\ndescribe('sharpTransformer', () => {\n it(\"should default mimeTypes to canResizeImage's allow-list exactly, excluding jxl\", () => {\n expect(sharpTransformer().mimeTypes).toEqual([\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/tiff',\n 'image/avif',\n ])\n })\n\n it('should reject dynamic.collections that are unknown or not upload-enabled on init', () => {\n const config = {\n collections: [\n { slug: 'media', fields: [], upload: true },\n { slug: 'posts', fields: [] },\n ],\n } as unknown as Config\n const transformer = sharpTransformer({\n dynamic: { collections: ['posts', 'missing'] as unknown as UploadCollectionSlug[] },\n })\n\n expect(() => transformer.init!(config)).toThrow(\n /not an upload-enabled collection: \"posts\", \"missing\"/,\n )\n })\n\n it('should reject a collection whose upload settings are configured on more than one instance on init', () => {\n const first = sharpTransformer({ collections: { media: { variants: [] } } })\n const second = sharpTransformer({ slug: 'sharp-second', collections: { media: {} } })\n const config = {\n collections: [{ slug: 'media', fields: [], upload: true }],\n upload: { transformers: [first, second] },\n } as unknown as Config\n\n expect(() => second.init!(config)).toThrow(\n /\"media\" has upload settings on more than one Sharp transformer: \"sharp\", \"sharp-second\"/,\n )\n })\n\n it('should allow different collections configured on different instances', () => {\n const first = sharpTransformer({ collections: { media: {} } })\n const second = sharpTransformer({ slug: 'sharp-second', collections: { photos: {} } })\n const config = {\n collections: [\n { slug: 'media', fields: [], upload: true },\n { slug: 'photos', fields: [], upload: true },\n ],\n upload: { transformers: [first, second] },\n } as unknown as Config\n\n expect(() => second.init!(config)).not.toThrow()\n })\n})\n\ndescribe('resolveSharpDynamicDefaults', () => {\n it('should default to fit=cover, position=center, maxWidth=4096, maxHeight=4096, maxPixels=16_777_216, withoutEnlargement=false', () => {\n expect(resolveSharpDynamicDefaults()).toEqual({\n fit: 'cover',\n maxHeight: 4096,\n maxPixels: 16_777_216,\n maxWidth: 4096,\n position: 'center',\n withoutEnlargement: false,\n })\n })\n})\n"],"names":["describe","expect","it","resolveSharpDynamicDefaults","sharpTransformer","mimeTypes","toEqual","config","collections","slug","fields","upload","transformer","dynamic","init","toThrow","first","media","variants","second","transformers","photos","not","fit","maxHeight","maxPixels","maxWidth","position","withoutEnlargement"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,2BAA2B,EAAEC,gBAAgB,QAAQ,wBAAuB;AAErFJ,SAAS,oBAAoB;IAC3BE,GAAG,kFAAkF;QACnFD,OAAOG,mBAAmBC,SAAS,EAAEC,OAAO,CAAC;YAC3C;YACA;YACA;YACA;YACA;YACA;SACD;IACH;IAEAJ,GAAG,oFAAoF;QACrF,MAAMK,SAAS;YACbC,aAAa;gBACX;oBAAEC,MAAM;oBAASC,QAAQ,EAAE;oBAAEC,QAAQ;gBAAK;gBAC1C;oBAAEF,MAAM;oBAASC,QAAQ,EAAE;gBAAC;aAC7B;QACH;QACA,MAAME,cAAcR,iBAAiB;YACnCS,SAAS;gBAAEL,aAAa;oBAAC;oBAAS;iBAAU;YAAsC;QACpF;QAEAP,OAAO,IAAMW,YAAYE,IAAI,CAAEP,SAASQ,OAAO,CAC7C;IAEJ;IAEAb,GAAG,qGAAqG;QACtG,MAAMc,QAAQZ,iBAAiB;YAAEI,aAAa;gBAAES,OAAO;oBAAEC,UAAU,EAAE;gBAAC;YAAE;QAAE;QAC1E,MAAMC,SAASf,iBAAiB;YAAEK,MAAM;YAAgBD,aAAa;gBAAES,OAAO,CAAC;YAAE;QAAE;QACnF,MAAMV,SAAS;YACbC,aAAa;gBAAC;oBAAEC,MAAM;oBAASC,QAAQ,EAAE;oBAAEC,QAAQ;gBAAK;aAAE;YAC1DA,QAAQ;gBAAES,cAAc;oBAACJ;oBAAOG;iBAAO;YAAC;QAC1C;QAEAlB,OAAO,IAAMkB,OAAOL,IAAI,CAAEP,SAASQ,OAAO,CACxC;IAEJ;IAEAb,GAAG,wEAAwE;QACzE,MAAMc,QAAQZ,iBAAiB;YAAEI,aAAa;gBAAES,OAAO,CAAC;YAAE;QAAE;QAC5D,MAAME,SAASf,iBAAiB;YAAEK,MAAM;YAAgBD,aAAa;gBAAEa,QAAQ,CAAC;YAAE;QAAE;QACpF,MAAMd,SAAS;YACbC,aAAa;gBACX;oBAAEC,MAAM;oBAASC,QAAQ,EAAE;oBAAEC,QAAQ;gBAAK;gBAC1C;oBAAEF,MAAM;oBAAUC,QAAQ,EAAE;oBAAEC,QAAQ;gBAAK;aAC5C;YACDA,QAAQ;gBAAES,cAAc;oBAACJ;oBAAOG;iBAAO;YAAC;QAC1C;QAEAlB,OAAO,IAAMkB,OAAOL,IAAI,CAAEP,SAASe,GAAG,CAACP,OAAO;IAChD;AACF;AAEAf,SAAS,+BAA+B;IACtCE,GAAG,+HAA+H;QAChID,OAAOE,+BAA+BG,OAAO,CAAC;YAC5CiB,KAAK;YACLC,WAAW;YACXC,WAAW;YACXC,UAAU;YACVC,UAAU;YACVC,oBAAoB;QACtB;IACF;AACF"}
@@ -0,0 +1,12 @@
1
+ import type { TransformFileArgs, TransformFileResult } from 'payload';
2
+ import type { SharpDependency, SharpUploadTaskOptions } from './types.js';
3
+ /**
4
+ * The Sharp package's public one-file-in/one-file-out upload primitive.
5
+ * `options.kind` selects which legacy task to run: the main file (optionally
6
+ * cropped, with `resizeOptions` re-applied to the crop output), or one named
7
+ * legacy image size. Never writes to storage.
8
+ */
9
+ export declare function createTransformFile({ sharpDependency }: {
10
+ sharpDependency: SharpDependency;
11
+ }): ({ file, options, req, }: TransformFileArgs<SharpUploadTaskOptions>) => Promise<TransformFileResult>;
12
+ //# sourceMappingURL=transformFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformFile.d.ts","sourceRoot":"","sources":["../src/transformFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAGrF,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AASzE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,eAAe,EAAE,EAAE;IAAE,eAAe,EAAE,eAAe,CAAA;CAAE,6BAKxF,iBAAiB,CAAC,sBAAsB,CAAC,KAAG,OAAO,CAAC,mBAAmB,CAAC,CAO5E"}
@@ -0,0 +1,264 @@
1
+ import { createSharpFromFile } from './createSharpFromFile.js';
2
+ import { optionallyAppendMetadata } from './optionallyAppendMetadata.js';
3
+ const ANIMATED_MIME_TYPES = [
4
+ 'image/avif',
5
+ 'image/gif',
6
+ 'image/webp'
7
+ ];
8
+ const percentToPixel = (value, dimension)=>Math.floor(value / 100 * dimension);
9
+ /**
10
+ * The Sharp package's public one-file-in/one-file-out upload primitive.
11
+ * `options.kind` selects which legacy task to run: the main file (optionally
12
+ * cropped, with `resizeOptions` re-applied to the crop output), or one named
13
+ * legacy image size. Never writes to storage.
14
+ */ export function createTransformFile({ sharpDependency }) {
15
+ return async function transformFile({ file, options, req }) {
16
+ if (options.kind === 'main') {
17
+ return transformMain({
18
+ file,
19
+ options,
20
+ req,
21
+ sharpDependency
22
+ });
23
+ }
24
+ return transformSize({
25
+ file,
26
+ options,
27
+ req,
28
+ sharpDependency
29
+ });
30
+ };
31
+ }
32
+ async function transformMain({ file, options, req, sharpDependency }) {
33
+ const { collectionUpload, crop } = options;
34
+ const { constructorOptions, formatOptions, resizeOptions, trimOptions, withMetadata } = collectionUpload;
35
+ const fileIsAnimatedType = ANIMATED_MIME_TYPES.includes(file.type);
36
+ const fileHasAdjustments = Boolean(resizeOptions || formatOptions || trimOptions || constructorOptions);
37
+ if (crop) {
38
+ return transformCrop({
39
+ crop,
40
+ file,
41
+ fileIsAnimatedType,
42
+ req,
43
+ resizeOptions,
44
+ sharpDependency,
45
+ withMetadata
46
+ });
47
+ }
48
+ if (!fileIsAnimatedType && !fileHasAdjustments) {
49
+ return {
50
+ status: 'continue'
51
+ };
52
+ }
53
+ const sharpOptions = {
54
+ ...constructorOptions
55
+ };
56
+ if (fileIsAnimatedType) {
57
+ sharpOptions.animated = true;
58
+ }
59
+ // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
60
+ let sharpFile = (await createSharpFromFile({
61
+ file,
62
+ options: sharpOptions,
63
+ sharpDependency
64
+ })).rotate();
65
+ if (resizeOptions) {
66
+ sharpFile = sharpFile.resize(resizeOptions);
67
+ }
68
+ if (formatOptions) {
69
+ sharpFile = sharpFile.toFormat(formatOptions.format, formatOptions.options);
70
+ }
71
+ if (trimOptions) {
72
+ sharpFile = sharpFile.trim(trimOptions);
73
+ }
74
+ sharpFile = await optionallyAppendMetadata({
75
+ req,
76
+ sharpFile,
77
+ withMetadata
78
+ });
79
+ const { data: outputData } = await sharpFile.toBuffer({
80
+ resolveWithObject: true
81
+ });
82
+ return {
83
+ file: new File([
84
+ outputData
85
+ ], file.name, {
86
+ type: file.type
87
+ }),
88
+ status: 'continue'
89
+ };
90
+ }
91
+ async function transformCrop({ crop, file, fileIsAnimatedType, req, resizeOptions, sharpDependency, withMetadata }) {
92
+ const { cropData, heightInPixels, originalDimensions, widthInPixels } = crop;
93
+ const sharpOptions = fileIsAnimatedType ? {
94
+ animated: true
95
+ } : {};
96
+ const newWidth = Number(widthInPixels);
97
+ const newHeight = Number(heightInPixels);
98
+ const dimensionsChanged = originalDimensions.width !== newWidth || originalDimensions.height !== newHeight;
99
+ let croppedFile = file;
100
+ if (dimensionsChanged) {
101
+ const formattedCropData = {
102
+ height: newHeight,
103
+ left: percentToPixel(cropData.x, originalDimensions.width),
104
+ top: percentToPixel(cropData.y, originalDimensions.height),
105
+ width: newWidth
106
+ };
107
+ let cropped = (await createSharpFromFile({
108
+ file,
109
+ options: sharpOptions,
110
+ sharpDependency
111
+ })).extract(formattedCropData);
112
+ cropped = await optionallyAppendMetadata({
113
+ req,
114
+ sharpFile: cropped,
115
+ withMetadata
116
+ });
117
+ const { data } = await cropped.toBuffer({
118
+ resolveWithObject: true
119
+ });
120
+ croppedFile = new File([
121
+ data
122
+ ], file.name, {
123
+ type: file.type
124
+ });
125
+ }
126
+ if (resizeOptions && !resizeOptions.withoutEnlargement) {
127
+ const sharpFile = await createSharpFromFile({
128
+ file: croppedFile,
129
+ sharpDependency
130
+ });
131
+ const { data: resizedBuffer } = await sharpFile.resize({
132
+ fit: resizeOptions.fit || 'cover',
133
+ height: resizeOptions.height,
134
+ position: resizeOptions.position || 'center',
135
+ width: resizeOptions.width
136
+ }).toBuffer({
137
+ resolveWithObject: true
138
+ });
139
+ return {
140
+ file: new File([
141
+ resizedBuffer
142
+ ], file.name, {
143
+ type: file.type
144
+ }),
145
+ status: 'continue'
146
+ };
147
+ }
148
+ return {
149
+ file: croppedFile,
150
+ status: 'continue'
151
+ };
152
+ }
153
+ async function transformSize({ file, options, req, sharpDependency }) {
154
+ const { collectionUpload, focalPoint, imageResizeConfig, originalDimensions } = options;
155
+ const { withMetadata } = collectionUpload;
156
+ const fileIsAnimatedType = ANIMATED_MIME_TYPES.includes(file.type);
157
+ const sharpOptions = fileIsAnimatedType ? {
158
+ animated: true
159
+ } : {};
160
+ const sharpBase = (await createSharpFromFile({
161
+ file,
162
+ options: sharpOptions,
163
+ sharpDependency
164
+ })).rotate();
165
+ const originalImageMeta = await sharpBase.metadata();
166
+ let adjustedDimensions = {
167
+ ...originalDimensions
168
+ };
169
+ if ([
170
+ 5,
171
+ 6,
172
+ 7,
173
+ 8
174
+ ].includes(originalImageMeta.orientation)) {
175
+ adjustedDimensions = {
176
+ height: originalDimensions.width,
177
+ width: originalDimensions.height
178
+ };
179
+ }
180
+ let resized = sharpBase.clone();
181
+ if (focalPoint) {
182
+ let { height: resizeHeight, width: resizeWidth } = imageResizeConfig;
183
+ const originalAspectRatio = adjustedDimensions.width / adjustedDimensions.height;
184
+ if (resizeHeight && !resizeWidth) {
185
+ resizeWidth = Math.round(resizeHeight * originalAspectRatio);
186
+ }
187
+ if (resizeWidth && !resizeHeight) {
188
+ resizeHeight = Math.round(resizeWidth / originalAspectRatio);
189
+ }
190
+ if (!resizeHeight) {
191
+ resizeHeight = originalImageMeta.height;
192
+ }
193
+ if (!resizeWidth) {
194
+ resizeWidth = originalImageMeta.width;
195
+ }
196
+ const resizeAspectRatio = resizeWidth / resizeHeight;
197
+ const prioritizeHeight = resizeAspectRatio < originalAspectRatio;
198
+ resized = resized.resize({
199
+ fastShrinkOnLoad: false,
200
+ height: prioritizeHeight ? resizeHeight : undefined,
201
+ width: prioritizeHeight ? undefined : resizeWidth
202
+ });
203
+ const metadataAppended = await optionallyAppendMetadata({
204
+ req,
205
+ sharpFile: resized,
206
+ withMetadata
207
+ });
208
+ const { info } = await metadataAppended.toBuffer({
209
+ resolveWithObject: true
210
+ });
211
+ const resizedHeight = originalImageMeta.pages ? info.height / originalImageMeta.pages : info.height;
212
+ const resizedWidth = info.width;
213
+ const halfResizeX = resizeWidth / 2;
214
+ const xFocalCenter = resizedWidth * (focalPoint.x / 100);
215
+ let leftBound = xFocalCenter - halfResizeX;
216
+ if (xFocalCenter + halfResizeX > resizedWidth) {
217
+ leftBound = resizedWidth - resizeWidth;
218
+ }
219
+ if (leftBound < 0) {
220
+ leftBound = 0;
221
+ }
222
+ const halfResizeY = resizeHeight / 2;
223
+ const yFocalCenter = resizedHeight * (focalPoint.y / 100);
224
+ let topBound = yFocalCenter - halfResizeY;
225
+ if (yFocalCenter + halfResizeY > resizedHeight) {
226
+ topBound = resizedHeight - resizeHeight;
227
+ }
228
+ if (topBound < 0) {
229
+ topBound = 0;
230
+ }
231
+ resized = resized.extract({
232
+ height: resizeHeight,
233
+ left: Math.floor(leftBound),
234
+ top: Math.floor(topBound),
235
+ width: resizeWidth
236
+ });
237
+ } else {
238
+ resized = resized.resize(imageResizeConfig);
239
+ }
240
+ if (imageResizeConfig.formatOptions) {
241
+ resized = resized.toFormat(imageResizeConfig.formatOptions.format, imageResizeConfig.formatOptions.options);
242
+ }
243
+ if (imageResizeConfig.trimOptions) {
244
+ resized = resized.trim(imageResizeConfig.trimOptions);
245
+ }
246
+ resized = await optionallyAppendMetadata({
247
+ req,
248
+ sharpFile: resized,
249
+ withMetadata
250
+ });
251
+ const { data: outputData } = await resized.toBuffer({
252
+ resolveWithObject: true
253
+ });
254
+ return {
255
+ file: new File([
256
+ outputData
257
+ ], file.name, {
258
+ type: file.type
259
+ }),
260
+ status: 'continue'
261
+ };
262
+ }
263
+
264
+ //# sourceMappingURL=transformFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transformFile.ts"],"sourcesContent":["import type { PayloadRequest, TransformFileArgs, TransformFileResult } from 'payload'\nimport type { ResizeOptions, SharpOptions } from 'sharp'\n\nimport type { SharpDependency, SharpUploadTaskOptions } from './types.js'\n\nimport { createSharpFromFile } from './createSharpFromFile.js'\nimport { optionallyAppendMetadata } from './optionallyAppendMetadata.js'\n\nconst ANIMATED_MIME_TYPES = ['image/avif', 'image/gif', 'image/webp']\n\nconst percentToPixel = (value: number, dimension: number) => Math.floor((value / 100) * dimension)\n\n/**\n * The Sharp package's public one-file-in/one-file-out upload primitive.\n * `options.kind` selects which legacy task to run: the main file (optionally\n * cropped, with `resizeOptions` re-applied to the crop output), or one named\n * legacy image size. Never writes to storage.\n */\nexport function createTransformFile({ sharpDependency }: { sharpDependency: SharpDependency }) {\n return async function transformFile({\n file,\n options,\n req,\n }: TransformFileArgs<SharpUploadTaskOptions>): Promise<TransformFileResult> {\n if (options.kind === 'main') {\n return transformMain({ file, options, req, sharpDependency })\n }\n\n return transformSize({ file, options, req, sharpDependency })\n }\n}\n\nasync function transformMain({\n file,\n options,\n req,\n sharpDependency,\n}: {\n file: File\n options: Extract<SharpUploadTaskOptions, { kind: 'main' }>\n req: PayloadRequest\n sharpDependency: SharpDependency\n}): Promise<TransformFileResult> {\n const { collectionUpload, crop } = options\n const { constructorOptions, formatOptions, resizeOptions, trimOptions, withMetadata } =\n collectionUpload\n\n const fileIsAnimatedType = ANIMATED_MIME_TYPES.includes(file.type)\n const fileHasAdjustments = Boolean(\n resizeOptions || formatOptions || trimOptions || constructorOptions,\n )\n\n if (crop) {\n return transformCrop({\n crop,\n file,\n fileIsAnimatedType,\n req,\n resizeOptions,\n sharpDependency,\n withMetadata,\n })\n }\n\n if (!fileIsAnimatedType && !fileHasAdjustments) {\n return { status: 'continue' }\n }\n\n const sharpOptions: SharpOptions = { ...constructorOptions }\n\n if (fileIsAnimatedType) {\n sharpOptions.animated = true\n }\n\n // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081\n let sharpFile = (\n await createSharpFromFile({ file, options: sharpOptions, sharpDependency })\n ).rotate()\n\n if (resizeOptions) {\n sharpFile = sharpFile.resize(resizeOptions)\n }\n if (formatOptions) {\n sharpFile = sharpFile.toFormat(formatOptions.format, formatOptions.options)\n }\n if (trimOptions) {\n sharpFile = sharpFile.trim(trimOptions)\n }\n\n sharpFile = await optionallyAppendMetadata({ req, sharpFile, withMetadata })\n const { data: outputData } = await sharpFile.toBuffer({ resolveWithObject: true })\n\n return {\n file: new File([outputData], file.name, { type: file.type }),\n status: 'continue',\n }\n}\n\nasync function transformCrop({\n crop,\n file,\n fileIsAnimatedType,\n req,\n resizeOptions,\n sharpDependency,\n withMetadata,\n}: {\n crop: NonNullable<Extract<SharpUploadTaskOptions, { kind: 'main' }>['crop']>\n file: File\n fileIsAnimatedType: boolean\n req: PayloadRequest\n resizeOptions: ResizeOptions | undefined\n sharpDependency: SharpDependency\n withMetadata: SharpUploadTaskOptions['collectionUpload']['withMetadata']\n}): Promise<TransformFileResult> {\n const { cropData, heightInPixels, originalDimensions, widthInPixels } = crop\n\n const sharpOptions: SharpOptions = fileIsAnimatedType ? { animated: true } : {}\n\n const newWidth = Number(widthInPixels)\n const newHeight = Number(heightInPixels)\n const dimensionsChanged =\n originalDimensions.width !== newWidth || originalDimensions.height !== newHeight\n\n let croppedFile = file\n\n if (dimensionsChanged) {\n const formattedCropData = {\n height: newHeight,\n left: percentToPixel(cropData.x, originalDimensions.width),\n top: percentToPixel(cropData.y, originalDimensions.height),\n width: newWidth,\n }\n\n let cropped = (\n await createSharpFromFile({ file, options: sharpOptions, sharpDependency })\n ).extract(formattedCropData)\n cropped = await optionallyAppendMetadata({ req, sharpFile: cropped, withMetadata })\n const { data } = await cropped.toBuffer({ resolveWithObject: true })\n croppedFile = new File([data], file.name, { type: file.type })\n }\n\n if (resizeOptions && !resizeOptions.withoutEnlargement) {\n const sharpFile = await createSharpFromFile({ file: croppedFile, sharpDependency })\n const { data: resizedBuffer } = await sharpFile\n .resize({\n fit: resizeOptions.fit || 'cover',\n height: resizeOptions.height,\n position: resizeOptions.position || 'center',\n width: resizeOptions.width,\n })\n .toBuffer({ resolveWithObject: true })\n\n return {\n file: new File([resizedBuffer], file.name, { type: file.type }),\n status: 'continue',\n }\n }\n\n return {\n file: croppedFile,\n status: 'continue',\n }\n}\n\nasync function transformSize({\n file,\n options,\n req,\n sharpDependency,\n}: {\n file: File\n options: Extract<SharpUploadTaskOptions, { kind: 'size' }>\n req: PayloadRequest\n sharpDependency: SharpDependency\n}): Promise<TransformFileResult> {\n const { collectionUpload, focalPoint, imageResizeConfig, originalDimensions } = options\n const { withMetadata } = collectionUpload\n\n const fileIsAnimatedType = ANIMATED_MIME_TYPES.includes(file.type)\n const sharpOptions: SharpOptions = fileIsAnimatedType ? { animated: true } : {}\n\n const sharpBase = (\n await createSharpFromFile({ file, options: sharpOptions, sharpDependency })\n ).rotate()\n const originalImageMeta = await sharpBase.metadata()\n\n let adjustedDimensions = { ...originalDimensions }\n\n if ([5, 6, 7, 8].includes(originalImageMeta.orientation!)) {\n adjustedDimensions = {\n height: originalDimensions.width,\n width: originalDimensions.height,\n }\n }\n\n let resized = sharpBase.clone()\n\n if (focalPoint) {\n let { height: resizeHeight, width: resizeWidth } = imageResizeConfig\n\n const originalAspectRatio = adjustedDimensions.width / adjustedDimensions.height\n\n if (resizeHeight && !resizeWidth) {\n resizeWidth = Math.round(resizeHeight * originalAspectRatio)\n }\n if (resizeWidth && !resizeHeight) {\n resizeHeight = Math.round(resizeWidth / originalAspectRatio)\n }\n if (!resizeHeight) {\n resizeHeight = originalImageMeta.height\n }\n if (!resizeWidth) {\n resizeWidth = originalImageMeta.width\n }\n\n const resizeAspectRatio = resizeWidth / resizeHeight\n const prioritizeHeight = resizeAspectRatio < originalAspectRatio\n\n resized = resized.resize({\n fastShrinkOnLoad: false,\n height: prioritizeHeight ? resizeHeight : undefined,\n width: prioritizeHeight ? undefined : resizeWidth,\n })\n\n const metadataAppended = await optionallyAppendMetadata({\n req,\n sharpFile: resized,\n withMetadata,\n })\n const { info } = await metadataAppended.toBuffer({ resolveWithObject: true })\n\n const resizedHeight = originalImageMeta.pages\n ? info.height / originalImageMeta.pages\n : info.height\n const resizedWidth = info.width\n\n const halfResizeX = resizeWidth / 2\n const xFocalCenter = resizedWidth * (focalPoint.x / 100)\n let leftBound = xFocalCenter - halfResizeX\n if (xFocalCenter + halfResizeX > resizedWidth) {\n leftBound = resizedWidth - resizeWidth\n }\n if (leftBound < 0) {\n leftBound = 0\n }\n\n const halfResizeY = resizeHeight / 2\n const yFocalCenter = resizedHeight * (focalPoint.y / 100)\n let topBound = yFocalCenter - halfResizeY\n if (yFocalCenter + halfResizeY > resizedHeight) {\n topBound = resizedHeight - resizeHeight\n }\n if (topBound < 0) {\n topBound = 0\n }\n\n resized = resized.extract({\n height: resizeHeight,\n left: Math.floor(leftBound),\n top: Math.floor(topBound),\n width: resizeWidth,\n })\n } else {\n resized = resized.resize(imageResizeConfig)\n }\n\n if (imageResizeConfig.formatOptions) {\n resized = resized.toFormat(\n imageResizeConfig.formatOptions.format,\n imageResizeConfig.formatOptions.options,\n )\n }\n if (imageResizeConfig.trimOptions) {\n resized = resized.trim(imageResizeConfig.trimOptions)\n }\n\n resized = await optionallyAppendMetadata({ req, sharpFile: resized, withMetadata })\n const { data: outputData } = await resized.toBuffer({ resolveWithObject: true })\n\n return {\n file: new File([outputData], file.name, { type: file.type }),\n status: 'continue',\n }\n}\n"],"names":["createSharpFromFile","optionallyAppendMetadata","ANIMATED_MIME_TYPES","percentToPixel","value","dimension","Math","floor","createTransformFile","sharpDependency","transformFile","file","options","req","kind","transformMain","transformSize","collectionUpload","crop","constructorOptions","formatOptions","resizeOptions","trimOptions","withMetadata","fileIsAnimatedType","includes","type","fileHasAdjustments","Boolean","transformCrop","status","sharpOptions","animated","sharpFile","rotate","resize","toFormat","format","trim","data","outputData","toBuffer","resolveWithObject","File","name","cropData","heightInPixels","originalDimensions","widthInPixels","newWidth","Number","newHeight","dimensionsChanged","width","height","croppedFile","formattedCropData","left","x","top","y","cropped","extract","withoutEnlargement","resizedBuffer","fit","position","focalPoint","imageResizeConfig","sharpBase","originalImageMeta","metadata","adjustedDimensions","orientation","resized","clone","resizeHeight","resizeWidth","originalAspectRatio","round","resizeAspectRatio","prioritizeHeight","fastShrinkOnLoad","undefined","metadataAppended","info","resizedHeight","pages","resizedWidth","halfResizeX","xFocalCenter","leftBound","halfResizeY","yFocalCenter","topBound"],"mappings":"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"}