@webiny/api-file-manager 6.4.11 → 6.6.0-alpha.1
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.
- package/FileManagerAppFeature.d.ts +5 -0
- package/FileManagerAppFeature.js +21 -0
- package/FileManagerAppFeature.js.map +1 -0
- package/delivery/AssetDelivery/AssetRequest.d.ts +1 -1
- package/delivery/AssetDelivery/AssetRequest.js.map +1 -1
- package/delivery/AssetDeliveryRoute.d.ts +23 -0
- package/delivery/AssetDeliveryRoute.js +58 -0
- package/delivery/AssetDeliveryRoute.js.map +1 -0
- package/delivery/setupAssetDelivery.d.ts +1 -1
- package/delivery/setupAssetDelivery.js +1 -66
- package/delivery/setupAssetDelivery.js.map +1 -1
- package/domain/file/abstractions.d.ts +13 -5
- package/domain/file/abstractions.js +2 -2
- package/domain/file/abstractions.js.map +1 -1
- package/domain/file/file.model.d.ts +3 -3
- package/domain/file/file.model.js +10 -6
- package/domain/file/file.model.js.map +1 -1
- package/domain/settings/validation.js +1 -1
- package/domain/settings/validation.js.map +1 -1
- package/exports/api/file-manager/assetDelivery.d.ts +10 -0
- package/exports/api/file-manager/assetDelivery.js +8 -0
- package/exports/api/file-manager/file.d.ts +1 -0
- package/exports/api/file-manager/file.js +1 -0
- package/features/FileManagerFeature.js +6 -0
- package/features/FileManagerFeature.js.map +1 -1
- package/features/assetDelivery/Asset/Asset.d.ts +8 -0
- package/features/assetDelivery/Asset/Asset.js +14 -0
- package/features/assetDelivery/Asset/Asset.js.map +1 -0
- package/features/assetDelivery/Asset/abstractions.d.ts +10 -0
- package/features/assetDelivery/Asset/abstractions.js +5 -0
- package/features/assetDelivery/Asset/abstractions.js.map +1 -0
- package/features/assetDelivery/Asset/index.d.ts +1 -0
- package/features/assetDelivery/Asset/index.js +1 -0
- package/features/assetDelivery/FilesAssetRequestResolver.js +2 -2
- package/features/assetDelivery/FilesAssetRequestResolver.js.map +1 -1
- package/features/assetDelivery/ObjectKey/ObjectKey.d.ts +8 -0
- package/features/assetDelivery/ObjectKey/ObjectKey.js +28 -0
- package/features/assetDelivery/ObjectKey/ObjectKey.js.map +1 -0
- package/features/assetDelivery/ObjectKey/abstractions.d.ts +12 -0
- package/features/assetDelivery/ObjectKey/abstractions.js +5 -0
- package/features/assetDelivery/ObjectKey/abstractions.js.map +1 -0
- package/features/assetDelivery/ObjectKey/index.d.ts +1 -0
- package/features/assetDelivery/ObjectKey/index.js +1 -0
- package/features/assetDelivery/StreamAssetReply/StreamAssetReply.d.ts +8 -0
- package/features/assetDelivery/StreamAssetReply/StreamAssetReply.js +22 -0
- package/features/assetDelivery/StreamAssetReply/StreamAssetReply.js.map +1 -0
- package/features/assetDelivery/StreamAssetReply/abstractions.d.ts +11 -0
- package/features/assetDelivery/StreamAssetReply/abstractions.js +5 -0
- package/features/assetDelivery/StreamAssetReply/abstractions.js.map +1 -0
- package/features/assetDelivery/StreamAssetReply/index.d.ts +1 -0
- package/features/assetDelivery/StreamAssetReply/index.js +1 -0
- package/features/assetDelivery/abstractions/AssetType.d.ts +17 -0
- package/features/assetDelivery/abstractions/AssetType.js +5 -0
- package/features/assetDelivery/abstractions/AssetType.js.map +1 -0
- package/features/assetDelivery/abstractions.d.ts +23 -12
- package/features/assetDelivery/abstractions.js.map +1 -1
- package/features/assetDelivery/assetTypes/image/AssetKeyGenerator.d.ts +12 -0
- package/features/assetDelivery/assetTypes/image/AssetKeyGenerator.js +30 -0
- package/features/assetDelivery/assetTypes/image/AssetKeyGenerator.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/ImageAssetType.d.ts +9 -0
- package/features/assetDelivery/assetTypes/image/ImageAssetType.js +18 -0
- package/features/assetDelivery/assetTypes/image/ImageAssetType.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/ImageAssetTypeHandler.d.ts +5 -0
- package/features/assetDelivery/assetTypes/image/ImageAssetTypeHandler.js +5 -0
- package/features/assetDelivery/assetTypes/image/ImageAssetTypeHandler.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/SharpTransformer.d.ts +18 -0
- package/features/assetDelivery/assetTypes/image/SharpTransformer.js +47 -0
- package/features/assetDelivery/assetTypes/image/SharpTransformer.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/WidthCollection.d.ts +8 -0
- package/features/assetDelivery/assetTypes/image/WidthCollection.js +22 -0
- package/features/assetDelivery/assetTypes/image/WidthCollection.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/imageFormat.d.ts +26 -0
- package/features/assetDelivery/assetTypes/image/imageFormat.js +51 -0
- package/features/assetDelivery/assetTypes/image/imageFormat.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/imageTypes.d.ts +39 -0
- package/features/assetDelivery/assetTypes/image/imageTypes.js +0 -0
- package/features/assetDelivery/assetTypes/image/index.d.ts +5 -0
- package/features/assetDelivery/assetTypes/image/index.js +4 -0
- package/features/assetDelivery/assetTypes/image/normalizeImageOptions.d.ts +6 -0
- package/features/assetDelivery/assetTypes/image/normalizeImageOptions.js +56 -0
- package/features/assetDelivery/assetTypes/image/normalizeImageOptions.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/transformImage.d.ts +59 -0
- package/features/assetDelivery/assetTypes/image/transformImage.js +146 -0
- package/features/assetDelivery/assetTypes/image/transformImage.js.map +1 -0
- package/features/assetDelivery/assetTypes/image/utils.d.ts +26 -0
- package/features/assetDelivery/assetTypes/image/utils.js +47 -0
- package/features/assetDelivery/assetTypes/image/utils.js.map +1 -0
- package/features/assetDelivery/feature.js +13 -7
- package/features/assetDelivery/feature.js.map +1 -1
- package/features/assetDelivery/privateFiles/PrivateFileAssetRequestResolver.js +2 -2
- package/features/assetDelivery/privateFiles/PrivateFileAssetRequestResolver.js.map +1 -1
- package/features/assetDelivery/privateFiles/PrivateFilesAssetProcessor.js.map +1 -1
- package/features/assetDelivery/transformation/CallableContentsReader.d.ts +11 -0
- package/features/assetDelivery/transformation/CallableContentsReader.js +14 -0
- package/features/assetDelivery/transformation/CallableContentsReader.js.map +1 -0
- package/features/assetDelivery/transformation/TransformationAssetProcessor.d.ts +4 -6
- package/features/assetDelivery/transformation/TransformationAssetProcessor.js +13 -11
- package/features/assetDelivery/transformation/TransformationAssetProcessor.js.map +1 -1
- package/features/assetDelivery/transformation/index.d.ts +5 -0
- package/features/assetDelivery/transformation/index.js +4 -0
- package/features/extractMetadata/ExtractMetadataHandler.d.ts +11 -0
- package/features/extractMetadata/ExtractMetadataHandler.js +25 -0
- package/features/extractMetadata/ExtractMetadataHandler.js.map +1 -0
- package/features/extractMetadata/ExtractMetadataInput.d.ts +3 -0
- package/features/extractMetadata/ExtractMetadataInput.js +0 -0
- package/features/file/CreateFile/CreateFileRepository.d.ts +3 -3
- package/features/file/CreateFile/CreateFileRepository.js +6 -5
- package/features/file/CreateFile/CreateFileRepository.js.map +1 -1
- package/features/file/DeleteFile/DeleteFileRepository.d.ts +3 -3
- package/features/file/DeleteFile/DeleteFileRepository.js +6 -5
- package/features/file/DeleteFile/DeleteFileRepository.js.map +1 -1
- package/features/file/FileModelProvider.d.ts +31 -0
- package/features/file/FileModelProvider.js +22 -0
- package/features/file/FileModelProvider.js.map +1 -0
- package/features/file/FileUrlGenerator/FileUrlGenerator.d.ts +14 -0
- package/features/file/FileUrlGenerator/FileUrlGenerator.js +25 -0
- package/features/file/FileUrlGenerator/FileUrlGenerator.js.map +1 -0
- package/features/file/FileUrlGenerator/abstractions.d.ts +3 -3
- package/features/file/FileUrlGenerator/abstractions.js +1 -1
- package/features/file/FileUrlGenerator/abstractions.js.map +1 -1
- package/features/file/FileUrlGenerator/feature.d.ts +4 -0
- package/features/file/FileUrlGenerator/feature.js +11 -0
- package/features/file/FileUrlGenerator/feature.js.map +1 -0
- package/features/file/FileUrlGenerator/index.d.ts +1 -0
- package/features/file/FileUrlGenerator/index.js +1 -0
- package/features/file/GetFile/GetFileRepository.d.ts +3 -3
- package/features/file/GetFile/GetFileRepository.js +6 -5
- package/features/file/GetFile/GetFileRepository.js.map +1 -1
- package/features/file/GetFileByUrl/GetFileByUrlUseCase.d.ts +15 -0
- package/features/file/GetFileByUrl/GetFileByUrlUseCase.js +37 -0
- package/features/file/GetFileByUrl/GetFileByUrlUseCase.js.map +1 -0
- package/features/file/GetFileByUrl/abstractions.d.ts +18 -0
- package/features/file/GetFileByUrl/abstractions.js +5 -0
- package/features/file/GetFileByUrl/abstractions.js.map +1 -0
- package/features/file/GetFileByUrl/feature.d.ts +4 -0
- package/features/file/GetFileByUrl/feature.js +11 -0
- package/features/file/GetFileByUrl/feature.js.map +1 -0
- package/features/file/GetFileByUrl/index.d.ts +1 -0
- package/features/file/GetFileByUrl/index.js +1 -0
- package/features/file/ListFiles/ListFilesRepository.d.ts +3 -3
- package/features/file/ListFiles/ListFilesRepository.js +8 -7
- package/features/file/ListFiles/ListFilesRepository.js.map +1 -1
- package/features/file/ListImagesByTagTool/ListImagesByTagTool.d.ts +5 -0
- package/features/file/ListImagesByTagTool/ListImagesByTagTool.js +5 -0
- package/features/file/ListImagesByTagTool/ListImagesByTagTool.js.map +1 -1
- package/features/file/ListTags/ListTagsRepository.d.ts +3 -3
- package/features/file/ListTags/ListTagsRepository.js +7 -6
- package/features/file/ListTags/ListTagsRepository.js.map +1 -1
- package/features/file/UpdateFile/UpdateFileRepository.d.ts +3 -3
- package/features/file/UpdateFile/UpdateFileRepository.js +6 -5
- package/features/file/UpdateFile/UpdateFileRepository.js.map +1 -1
- package/features/settings/SettingsInstaller/SettingsInstaller.d.ts +3 -1
- package/features/settings/SettingsInstaller/SettingsInstaller.js +7 -4
- package/features/settings/SettingsInstaller/SettingsInstaller.js.map +1 -1
- package/features/settings/UpdateSettings/UpdateSettingsUseCase.js +0 -1
- package/features/settings/UpdateSettings/UpdateSettingsUseCase.js.map +1 -1
- package/features/upload/CompleteMultiPartUpload/abstractions.d.ts +12 -0
- package/features/upload/CompleteMultiPartUpload/abstractions.js +5 -0
- package/features/upload/CompleteMultiPartUpload/abstractions.js.map +1 -0
- package/features/upload/CompleteMultiPartUpload/index.d.ts +1 -0
- package/features/upload/CompleteMultiPartUpload/index.js +1 -0
- package/features/upload/CreateMultiPartUpload/abstractions.d.ts +14 -0
- package/features/upload/CreateMultiPartUpload/abstractions.js +5 -0
- package/features/upload/CreateMultiPartUpload/abstractions.js.map +1 -0
- package/features/upload/CreateMultiPartUpload/index.d.ts +1 -0
- package/features/upload/CreateMultiPartUpload/index.js +1 -0
- package/features/upload/GetUploadPayload/abstractions.d.ts +10 -0
- package/features/upload/GetUploadPayload/abstractions.js +5 -0
- package/features/upload/GetUploadPayload/abstractions.js.map +1 -0
- package/features/upload/GetUploadPayload/index.d.ts +1 -0
- package/features/upload/GetUploadPayload/index.js +1 -0
- package/features/upload/ReadFileMetadata/MetadataReader.d.ts +11 -0
- package/features/upload/ReadFileMetadata/MetadataReader.js +21 -0
- package/features/upload/ReadFileMetadata/MetadataReader.js.map +1 -0
- package/features/upload/ReadFileMetadata/abstractions.d.ts +15 -0
- package/features/upload/ReadFileMetadata/abstractions.js +5 -0
- package/features/upload/ReadFileMetadata/abstractions.js.map +1 -0
- package/features/upload/ReadFileMetadata/feature.d.ts +4 -0
- package/features/upload/ReadFileMetadata/feature.js +11 -0
- package/features/upload/ReadFileMetadata/feature.js.map +1 -0
- package/features/upload/ReadFileMetadata/index.d.ts +1 -0
- package/features/upload/ReadFileMetadata/index.js +1 -0
- package/features/upload/WriteFileMetadata/MetadataWriter.d.ts +14 -0
- package/features/upload/WriteFileMetadata/MetadataWriter.js +36 -0
- package/features/upload/WriteFileMetadata/MetadataWriter.js.map +1 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterBatchCreateHandler.d.ts +11 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterBatchCreateHandler.js +20 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterBatchCreateHandler.js.map +1 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterCreateHandler.d.ts +11 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterCreateHandler.js +22 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterCreateHandler.js.map +1 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterUpdateHandler.d.ts +11 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterUpdateHandler.js +22 -0
- package/features/upload/WriteFileMetadata/WriteMetadataAfterUpdateHandler.js.map +1 -0
- package/features/upload/WriteFileMetadata/abstractions.d.ts +9 -0
- package/features/upload/WriteFileMetadata/abstractions.js +5 -0
- package/features/upload/WriteFileMetadata/abstractions.js.map +1 -0
- package/features/upload/WriteFileMetadata/feature.d.ts +4 -0
- package/features/upload/WriteFileMetadata/feature.js +17 -0
- package/features/upload/WriteFileMetadata/feature.js.map +1 -0
- package/features/upload/index.d.ts +17 -0
- package/features/upload/index.js +10 -0
- package/features/upload/types.d.ts +28 -0
- package/features/upload/types.js +0 -0
- package/features/upload/utils/FileExtension.d.ts +6 -0
- package/features/upload/utils/FileExtension.js +16 -0
- package/features/upload/utils/FileExtension.js.map +1 -0
- package/features/upload/utils/FileKey.d.ts +11 -0
- package/features/upload/utils/FileKey.js +33 -0
- package/features/upload/utils/FileKey.js.map +1 -0
- package/features/upload/utils/FileNormalizer.d.ts +13 -0
- package/features/upload/utils/FileNormalizer.js +41 -0
- package/features/upload/utils/FileNormalizer.js.map +1 -0
- package/features/upload/utils/FileUploadModifier.d.ts +30 -0
- package/features/upload/utils/FileUploadModifier.js +39 -0
- package/features/upload/utils/FileUploadModifier.js.map +1 -0
- package/features/upload/utils/checkPermissions.d.ts +5 -0
- package/features/upload/utils/checkPermissions.js +22 -0
- package/features/upload/utils/checkPermissions.js.map +1 -0
- package/features/upload/utils/createFileNormalizerFromContext.d.ts +3 -0
- package/features/upload/utils/createFileNormalizerFromContext.js +9 -0
- package/features/upload/utils/createFileNormalizerFromContext.js.map +1 -0
- package/features/upload/utils/mimeTypes.d.ts +1 -0
- package/features/upload/utils/mimeTypes.js +9 -0
- package/features/upload/utils/mimeTypes.js.map +1 -0
- package/graphql/FmGraphQLSchema.d.ts +25 -0
- package/graphql/FmGraphQLSchema.js +452 -0
- package/graphql/FmGraphQLSchema.js.map +1 -0
- package/index.d.ts +3 -6
- package/index.js +3 -33
- package/index.js.map +1 -1
- package/package.json +23 -17
- package/features/assetDelivery/transformation/PassthroughAssetTransformationStrategy.d.ts +0 -9
- package/features/assetDelivery/transformation/PassthroughAssetTransformationStrategy.js +0 -13
- package/features/assetDelivery/transformation/PassthroughAssetTransformationStrategy.js.map +0 -1
- package/graphql/baseSchema.d.ts +0 -3
- package/graphql/baseSchema.js +0 -100
- package/graphql/baseSchema.js.map +0 -1
- package/graphql/createFilesTypeDefs.d.ts +0 -8
- package/graphql/createFilesTypeDefs.js +0 -191
- package/graphql/createFilesTypeDefs.js.map +0 -1
- package/graphql/filesSchema.d.ts +0 -4
- package/graphql/filesSchema.js +0 -97
- package/graphql/filesSchema.js.map +0 -1
- package/graphql/getFileByUrl.d.ts +0 -3
- package/graphql/getFileByUrl.js +0 -64
- package/graphql/getFileByUrl.js.map +0 -1
- package/graphql/index.d.ts +0 -4
- package/graphql/index.js +0 -48
- package/graphql/index.js.map +0 -1
- package/graphql/utils.d.ts +0 -11
- package/graphql/utils.js +0 -12
- package/graphql/utils.js.map +0 -1
- package/modelModifier/CmsModelModifier.d.ts +0 -26
- package/modelModifier/CmsModelModifier.js +0 -51
- package/modelModifier/CmsModelModifier.js.map +0 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ImageAssetType } from "./ImageAssetType.js";
|
|
2
|
+
export { ImageAssetTypeHandler } from "./ImageAssetTypeHandler.js";
|
|
3
|
+
export { AssetKeyGenerator } from "./AssetKeyGenerator.js";
|
|
4
|
+
export { SUPPORTED_TRANSFORMABLE_IMAGES, getImageKey, getCropSignature, getFramingSignature, getOptimizedImageKeyPrefix, getOptimizedTransformedImageKeyPrefix } from "./utils.js";
|
|
5
|
+
export type { AssetCrop, AssetImageEdit, ImageRequestOptions, Framing } from "./imageTypes.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ImageAssetType } from "./ImageAssetType.js";
|
|
2
|
+
export { ImageAssetTypeHandler } from "./ImageAssetTypeHandler.js";
|
|
3
|
+
export { AssetKeyGenerator } from "./AssetKeyGenerator.js";
|
|
4
|
+
export { SUPPORTED_TRANSFORMABLE_IMAGES, getCropSignature, getFramingSignature, getImageKey, getOptimizedImageKeyPrefix, getOptimizedTransformedImageKeyPrefix } from "./utils.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ImageRequestOptions } from "./imageTypes.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse raw query params into typed image request options.
|
|
4
|
+
* Returns a new `ImageRequestOptions` object; does not mutate the input.
|
|
5
|
+
*/
|
|
6
|
+
export declare const normalizeImageOptions: (query: Record<string, any>, acceptHeader: string | undefined) => ImageRequestOptions;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { clampQuality, resolveRequestedFormat } from "./imageFormat.js";
|
|
2
|
+
const normalizeImageOptions = (query, acceptHeader)=>{
|
|
3
|
+
const result = {
|
|
4
|
+
original: "original" in query
|
|
5
|
+
};
|
|
6
|
+
const width = query.width ? parseInt(query.width, 10) : 0 / 0;
|
|
7
|
+
if (!Number.isNaN(width) && width > 0) result.width = width;
|
|
8
|
+
const quality = void 0 !== query.quality ? clampQuality(parseInt(query.quality, 10)) : void 0;
|
|
9
|
+
if (void 0 !== quality) result.quality = quality;
|
|
10
|
+
const format = resolveRequestedFormat(query.format, acceptHeader);
|
|
11
|
+
if (format) result.format = format;
|
|
12
|
+
const crop = parseCrop(query.crop);
|
|
13
|
+
if (crop) result.crop = crop;
|
|
14
|
+
const aspectRatio = parseAspectRatio(query.aspectRatio);
|
|
15
|
+
if (aspectRatio) result.aspectRatio = aspectRatio;
|
|
16
|
+
const focal = parseFocal(query.focal);
|
|
17
|
+
if (focal) result.focal = focal;
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
const parseAspectRatio = (raw)=>{
|
|
21
|
+
if ("string" != typeof raw) return;
|
|
22
|
+
if (raw.includes(":")) {
|
|
23
|
+
const [w, h] = raw.split(":").map((v)=>parseFloat(v));
|
|
24
|
+
return w > 0 && h > 0 ? w / h : void 0;
|
|
25
|
+
}
|
|
26
|
+
const value = parseFloat(raw);
|
|
27
|
+
return !Number.isNaN(value) && value > 0 ? value : void 0;
|
|
28
|
+
};
|
|
29
|
+
const parseFocal = (raw)=>{
|
|
30
|
+
if ("string" != typeof raw) return;
|
|
31
|
+
const parts = raw.split(",").map((v)=>parseFloat(v));
|
|
32
|
+
if (2 !== parts.length || parts.some((n)=>Number.isNaN(n))) return;
|
|
33
|
+
const clamp01 = (n)=>Math.min(1, Math.max(0, n));
|
|
34
|
+
return {
|
|
35
|
+
x: clamp01(parts[0]),
|
|
36
|
+
y: clamp01(parts[1])
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const parseCrop = (raw)=>{
|
|
40
|
+
if ("string" != typeof raw) return;
|
|
41
|
+
const parts = raw.split(",").map((v)=>parseFloat(v));
|
|
42
|
+
if (4 !== parts.length || parts.some((n)=>Number.isNaN(n))) return;
|
|
43
|
+
const clamp01 = (n)=>Math.min(1, Math.max(0, n));
|
|
44
|
+
const [top, left, bottom, right] = parts.map(clamp01);
|
|
45
|
+
if (0 === top && 0 === left && 0 === bottom && 0 === right) return;
|
|
46
|
+
if (top + bottom >= 1 || left + right >= 1) return;
|
|
47
|
+
return {
|
|
48
|
+
top,
|
|
49
|
+
left,
|
|
50
|
+
bottom,
|
|
51
|
+
right
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export { normalizeImageOptions };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=normalizeImageOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/assetTypes/image/normalizeImageOptions.js","sources":["../../../../../src/features/assetDelivery/assetTypes/image/normalizeImageOptions.ts"],"sourcesContent":["import type { ImageRequestOptions } from \"./imageTypes.js\";\nimport { clampQuality, resolveRequestedFormat } from \"./imageFormat.js\";\n\n/**\n * Parse raw query params into typed image request options.\n * Returns a new `ImageRequestOptions` object; does not mutate the input.\n */\nexport const normalizeImageOptions = (\n query: Record<string, any>,\n acceptHeader: string | undefined\n): ImageRequestOptions => {\n const result: ImageRequestOptions = {\n original: \"original\" in query\n };\n\n const width = query.width ? parseInt(query.width, 10) : NaN;\n if (!Number.isNaN(width) && width > 0) {\n result.width = width;\n }\n\n const quality =\n query.quality !== undefined ? clampQuality(parseInt(query.quality, 10)) : undefined;\n if (quality !== undefined) {\n result.quality = quality;\n }\n\n const format = resolveRequestedFormat(query.format, acceptHeader);\n if (format) {\n result.format = format;\n }\n\n const crop = parseCrop(query.crop);\n if (crop) {\n result.crop = crop;\n }\n\n const aspectRatio = parseAspectRatio(query.aspectRatio);\n if (aspectRatio) {\n result.aspectRatio = aspectRatio;\n }\n\n const focal = parseFocal(query.focal);\n if (focal) {\n result.focal = focal;\n }\n\n return result;\n};\n\nconst parseAspectRatio = (raw: unknown): number | undefined => {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n if (raw.includes(\":\")) {\n const [w, h] = raw.split(\":\").map(v => parseFloat(v));\n return w > 0 && h > 0 ? w / h : undefined;\n }\n const value = parseFloat(raw);\n return !Number.isNaN(value) && value > 0 ? value : undefined;\n};\n\nconst parseFocal = (raw: unknown): { x: number; y: number } | undefined => {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n const parts = raw.split(\",\").map(v => parseFloat(v));\n if (parts.length !== 2 || parts.some(n => Number.isNaN(n))) {\n return undefined;\n }\n const clamp01 = (n: number) => Math.min(1, Math.max(0, n));\n return { x: clamp01(parts[0]), y: clamp01(parts[1]) };\n};\n\nconst parseCrop = (\n raw: unknown\n): { top: number; left: number; bottom: number; right: number } | undefined => {\n if (typeof raw !== \"string\") {\n return undefined;\n }\n const parts = raw.split(\",\").map(v => parseFloat(v));\n if (parts.length !== 4 || parts.some(n => Number.isNaN(n))) {\n return undefined;\n }\n const clamp01 = (n: number) => Math.min(1, Math.max(0, n));\n const [top, left, bottom, right] = parts.map(clamp01);\n if (top === 0 && left === 0 && bottom === 0 && right === 0) {\n return undefined;\n }\n if (top + bottom >= 1 || left + right >= 1) {\n return undefined;\n }\n return { top, left, bottom, right };\n};\n"],"names":["normalizeImageOptions","query","acceptHeader","result","width","parseInt","NaN","Number","quality","undefined","clampQuality","format","resolveRequestedFormat","crop","parseCrop","aspectRatio","parseAspectRatio","focal","parseFocal","raw","w","h","v","parseFloat","value","parts","n","clamp01","Math","top","left","bottom","right"],"mappings":";AAOO,MAAMA,wBAAwB,CACjCC,OACAC;IAEA,MAAMC,SAA8B;QAChC,UAAU,cAAcF;IAC5B;IAEA,MAAMG,QAAQH,MAAM,KAAK,GAAGI,SAASJ,MAAM,KAAK,EAAE,MAAMK,KAAG;IAC3D,IAAI,CAACC,OAAO,KAAK,CAACH,UAAUA,QAAQ,GAChCD,OAAO,KAAK,GAAGC;IAGnB,MAAMI,UACFP,AAAkBQ,WAAlBR,MAAM,OAAO,GAAiBS,aAAaL,SAASJ,MAAM,OAAO,EAAE,OAAOQ;IAC9E,IAAID,AAAYC,WAAZD,SACAL,OAAO,OAAO,GAAGK;IAGrB,MAAMG,SAASC,uBAAuBX,MAAM,MAAM,EAAEC;IACpD,IAAIS,QACAR,OAAO,MAAM,GAAGQ;IAGpB,MAAME,OAAOC,UAAUb,MAAM,IAAI;IACjC,IAAIY,MACAV,OAAO,IAAI,GAAGU;IAGlB,MAAME,cAAcC,iBAAiBf,MAAM,WAAW;IACtD,IAAIc,aACAZ,OAAO,WAAW,GAAGY;IAGzB,MAAME,QAAQC,WAAWjB,MAAM,KAAK;IACpC,IAAIgB,OACAd,OAAO,KAAK,GAAGc;IAGnB,OAAOd;AACX;AAEA,MAAMa,mBAAmB,CAACG;IACtB,IAAI,AAAe,YAAf,OAAOA,KACP;IAEJ,IAAIA,IAAI,QAAQ,CAAC,MAAM;QACnB,MAAM,CAACC,GAAGC,EAAE,GAAGF,IAAI,KAAK,CAAC,KAAK,GAAG,CAACG,CAAAA,IAAKC,WAAWD;QAClD,OAAOF,IAAI,KAAKC,IAAI,IAAID,IAAIC,IAAIZ;IACpC;IACA,MAAMe,QAAQD,WAAWJ;IACzB,OAAO,CAACZ,OAAO,KAAK,CAACiB,UAAUA,QAAQ,IAAIA,QAAQf;AACvD;AAEA,MAAMS,aAAa,CAACC;IAChB,IAAI,AAAe,YAAf,OAAOA,KACP;IAEJ,MAAMM,QAAQN,IAAI,KAAK,CAAC,KAAK,GAAG,CAACG,CAAAA,IAAKC,WAAWD;IACjD,IAAIG,AAAiB,MAAjBA,MAAM,MAAM,IAAUA,MAAM,IAAI,CAACC,CAAAA,IAAKnB,OAAO,KAAK,CAACmB,KACnD;IAEJ,MAAMC,UAAU,CAACD,IAAcE,KAAK,GAAG,CAAC,GAAGA,KAAK,GAAG,CAAC,GAAGF;IACvD,OAAO;QAAE,GAAGC,QAAQF,KAAK,CAAC,EAAE;QAAG,GAAGE,QAAQF,KAAK,CAAC,EAAE;IAAE;AACxD;AAEA,MAAMX,YAAY,CACdK;IAEA,IAAI,AAAe,YAAf,OAAOA,KACP;IAEJ,MAAMM,QAAQN,IAAI,KAAK,CAAC,KAAK,GAAG,CAACG,CAAAA,IAAKC,WAAWD;IACjD,IAAIG,AAAiB,MAAjBA,MAAM,MAAM,IAAUA,MAAM,IAAI,CAACC,CAAAA,IAAKnB,OAAO,KAAK,CAACmB,KACnD;IAEJ,MAAMC,UAAU,CAACD,IAAcE,KAAK,GAAG,CAAC,GAAGA,KAAK,GAAG,CAAC,GAAGF;IACvD,MAAM,CAACG,KAAKC,MAAMC,QAAQC,MAAM,GAAGP,MAAM,GAAG,CAACE;IAC7C,IAAIE,AAAQ,MAARA,OAAaC,AAAS,MAATA,QAAcC,AAAW,MAAXA,UAAgBC,AAAU,MAAVA,OAC3C;IAEJ,IAAIH,MAAME,UAAU,KAAKD,OAAOE,SAAS,GACrC;IAEJ,OAAO;QAAEH;QAAKC;QAAMC;QAAQC;IAAM;AACtC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ImageFormat } from "./imageFormat.js";
|
|
2
|
+
export interface TransformImageParams {
|
|
3
|
+
buffer: Buffer;
|
|
4
|
+
animated: boolean;
|
|
5
|
+
sourceContentType: string;
|
|
6
|
+
widths: number[];
|
|
7
|
+
options: {
|
|
8
|
+
width?: number;
|
|
9
|
+
format?: ImageFormat;
|
|
10
|
+
quality?: number;
|
|
11
|
+
};
|
|
12
|
+
qualityDefaults?: Record<ImageFormat, number>;
|
|
13
|
+
}
|
|
14
|
+
export interface TransformImageResult {
|
|
15
|
+
buffer: Buffer;
|
|
16
|
+
contentType: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Single sharp pipeline shared by the S3 and local transform strategies: optional
|
|
20
|
+
* resize (snapped to the configured width ladder) → optional format conversion +
|
|
21
|
+
* quality. Returns the resulting buffer and its content type.
|
|
22
|
+
*/
|
|
23
|
+
interface CropInsets {
|
|
24
|
+
top: number;
|
|
25
|
+
left: number;
|
|
26
|
+
bottom: number;
|
|
27
|
+
right: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract the crop region (edge insets, 0..1) from an image buffer. Returns the
|
|
31
|
+
* original buffer unchanged if the crop is empty/full or the dimensions are unknown.
|
|
32
|
+
*/
|
|
33
|
+
export declare const cropImageBuffer: (buffer: Buffer, crop: CropInsets) => Promise<Buffer>;
|
|
34
|
+
interface NormalizedRect {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The visible region (normalized 0–1) for the given framing. The largest rectangle
|
|
42
|
+
* of `aspectRatio` that fits inside the crop, centered on the focal point.
|
|
43
|
+
*/
|
|
44
|
+
export declare const getVisibleRect: (imageWidth: number, imageHeight: number, crop?: CropInsets, focal?: {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
}, aspectRatio?: number) => NormalizedRect;
|
|
48
|
+
export interface Framing {
|
|
49
|
+
crop?: CropInsets;
|
|
50
|
+
focal?: {
|
|
51
|
+
x: number;
|
|
52
|
+
y: number;
|
|
53
|
+
};
|
|
54
|
+
aspectRatio?: number;
|
|
55
|
+
}
|
|
56
|
+
export declare const hasFraming: (framing: Framing) => boolean;
|
|
57
|
+
export declare const extractFramedRegion: (buffer: Buffer, framing: Framing) => Promise<Buffer>;
|
|
58
|
+
export declare const transformImageBuffer: (params: TransformImageParams) => Promise<TransformImageResult>;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
2
|
+
import { WidthCollection } from "./WidthCollection.js";
|
|
3
|
+
import { DEFAULT_IMAGE_QUALITY, contentTypeForFormat, formatFromContentType } from "./imageFormat.js";
|
|
4
|
+
const applyOutputFormat = (pipeline, sourceContentType, options, qualityDefaults)=>{
|
|
5
|
+
const targetFormat = options.format ?? formatFromContentType(sourceContentType);
|
|
6
|
+
if (!targetFormat || !options.format && void 0 === options.quality) return {
|
|
7
|
+
pipeline,
|
|
8
|
+
contentType: sourceContentType
|
|
9
|
+
};
|
|
10
|
+
const quality = options.quality ?? qualityDefaults[targetFormat];
|
|
11
|
+
switch(targetFormat){
|
|
12
|
+
case "jpeg":
|
|
13
|
+
return {
|
|
14
|
+
pipeline: pipeline.jpeg({
|
|
15
|
+
quality
|
|
16
|
+
}),
|
|
17
|
+
contentType: contentTypeForFormat("jpeg")
|
|
18
|
+
};
|
|
19
|
+
case "webp":
|
|
20
|
+
return {
|
|
21
|
+
pipeline: pipeline.webp({
|
|
22
|
+
quality
|
|
23
|
+
}),
|
|
24
|
+
contentType: contentTypeForFormat("webp")
|
|
25
|
+
};
|
|
26
|
+
case "avif":
|
|
27
|
+
return {
|
|
28
|
+
pipeline: pipeline.avif({
|
|
29
|
+
quality
|
|
30
|
+
}),
|
|
31
|
+
contentType: contentTypeForFormat("avif")
|
|
32
|
+
};
|
|
33
|
+
case "png":
|
|
34
|
+
return {
|
|
35
|
+
pipeline: pipeline.png({
|
|
36
|
+
quality,
|
|
37
|
+
compressionLevel: 9,
|
|
38
|
+
adaptiveFiltering: true
|
|
39
|
+
}),
|
|
40
|
+
contentType: contentTypeForFormat("png")
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const clamp01 = (n)=>Math.min(1, Math.max(0, n));
|
|
45
|
+
const cropImageBuffer = async (buffer, crop)=>{
|
|
46
|
+
const image = sharp(buffer);
|
|
47
|
+
const meta = await image.metadata();
|
|
48
|
+
const w = meta.width ?? 0;
|
|
49
|
+
const h = meta.height ?? 0;
|
|
50
|
+
if (!w || !h) return buffer;
|
|
51
|
+
const left = Math.max(0, Math.min(w - 1, Math.round(clamp01(crop.left) * w)));
|
|
52
|
+
const top = Math.max(0, Math.min(h - 1, Math.round(clamp01(crop.top) * h)));
|
|
53
|
+
const width = Math.max(1, Math.min(w - left, Math.round((1 - clamp01(crop.left) - clamp01(crop.right)) * w)));
|
|
54
|
+
const height = Math.max(1, Math.min(h - top, Math.round((1 - clamp01(crop.top) - clamp01(crop.bottom)) * h)));
|
|
55
|
+
if (0 === left && 0 === top && width === w && height === h) return buffer;
|
|
56
|
+
return image.extract({
|
|
57
|
+
left,
|
|
58
|
+
top,
|
|
59
|
+
width,
|
|
60
|
+
height
|
|
61
|
+
}).toBuffer();
|
|
62
|
+
};
|
|
63
|
+
const cropToRect = (crop)=>{
|
|
64
|
+
const left = clamp01(crop?.left ?? 0);
|
|
65
|
+
const top = clamp01(crop?.top ?? 0);
|
|
66
|
+
return {
|
|
67
|
+
x: left,
|
|
68
|
+
y: top,
|
|
69
|
+
width: Math.max(0, 1 - left - clamp01(crop?.right ?? 0)),
|
|
70
|
+
height: Math.max(0, 1 - top - clamp01(crop?.bottom ?? 0))
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
const getVisibleRect = (imageWidth, imageHeight, crop, focal, aspectRatio)=>{
|
|
74
|
+
const cr = cropToRect(crop);
|
|
75
|
+
if (void 0 === aspectRatio || aspectRatio <= 0 || cr.width <= 0 || cr.height <= 0) return cr;
|
|
76
|
+
const iw = imageWidth > 0 ? imageWidth : 1;
|
|
77
|
+
const ih = imageHeight > 0 ? imageHeight : 1;
|
|
78
|
+
const cropAR = cr.width * iw / (cr.height * ih);
|
|
79
|
+
let w;
|
|
80
|
+
let h;
|
|
81
|
+
if (aspectRatio > cropAR) {
|
|
82
|
+
w = cr.width;
|
|
83
|
+
h = cr.width * iw / aspectRatio / ih;
|
|
84
|
+
} else {
|
|
85
|
+
h = cr.height;
|
|
86
|
+
w = cr.height * ih * aspectRatio / iw;
|
|
87
|
+
}
|
|
88
|
+
const fx = clamp01(focal?.x ?? 0.5);
|
|
89
|
+
const fy = clamp01(focal?.y ?? 0.5);
|
|
90
|
+
const maxX = cr.x + cr.width - w;
|
|
91
|
+
const maxY = cr.y + cr.height - h;
|
|
92
|
+
const x = Math.min(Math.max(fx - w / 2, cr.x), Math.max(cr.x, maxX));
|
|
93
|
+
const y = Math.min(Math.max(fy - h / 2, cr.y), Math.max(cr.y, maxY));
|
|
94
|
+
return {
|
|
95
|
+
x,
|
|
96
|
+
y,
|
|
97
|
+
width: w,
|
|
98
|
+
height: h
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const hasFraming = (framing)=>{
|
|
102
|
+
const c = framing.crop;
|
|
103
|
+
const cropped = !!c && !(0 === c.top && 0 === c.left && 0 === c.bottom && 0 === c.right);
|
|
104
|
+
return cropped || void 0 !== framing.aspectRatio;
|
|
105
|
+
};
|
|
106
|
+
const extractFramedRegion = async (buffer, framing)=>{
|
|
107
|
+
if (!hasFraming(framing)) return buffer;
|
|
108
|
+
const image = sharp(buffer);
|
|
109
|
+
const meta = await image.metadata();
|
|
110
|
+
const w = meta.width ?? 0;
|
|
111
|
+
const h = meta.height ?? 0;
|
|
112
|
+
if (!w || !h) return buffer;
|
|
113
|
+
const rect = getVisibleRect(w, h, framing.crop, framing.focal, framing.aspectRatio);
|
|
114
|
+
const left = Math.max(0, Math.min(w - 1, Math.round(rect.x * w)));
|
|
115
|
+
const top = Math.max(0, Math.min(h - 1, Math.round(rect.y * h)));
|
|
116
|
+
const width = Math.max(1, Math.min(w - left, Math.round(rect.width * w)));
|
|
117
|
+
const height = Math.max(1, Math.min(h - top, Math.round(rect.height * h)));
|
|
118
|
+
if (0 === left && 0 === top && width === w && height === h) return buffer;
|
|
119
|
+
return image.extract({
|
|
120
|
+
left,
|
|
121
|
+
top,
|
|
122
|
+
width,
|
|
123
|
+
height
|
|
124
|
+
}).toBuffer();
|
|
125
|
+
};
|
|
126
|
+
const transformImageBuffer = async (params)=>{
|
|
127
|
+
let pipeline = sharp(params.buffer, {
|
|
128
|
+
animated: params.animated
|
|
129
|
+
}).withMetadata();
|
|
130
|
+
if (params.options.width) {
|
|
131
|
+
const width = WidthCollection.create(params.widths).getClosestOrMax(params.options.width);
|
|
132
|
+
pipeline = pipeline.resize({
|
|
133
|
+
width,
|
|
134
|
+
withoutEnlargement: true
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const { pipeline: encoded, contentType } = applyOutputFormat(pipeline, params.sourceContentType, params.options, params.qualityDefaults ?? DEFAULT_IMAGE_QUALITY);
|
|
138
|
+
const buffer = await encoded.toBuffer();
|
|
139
|
+
return {
|
|
140
|
+
buffer,
|
|
141
|
+
contentType
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
export { cropImageBuffer, extractFramedRegion, getVisibleRect, hasFraming, transformImageBuffer };
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=transformImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/assetTypes/image/transformImage.js","sources":["../../../../../src/features/assetDelivery/assetTypes/image/transformImage.ts"],"sourcesContent":["/**\n * The sharp-backed image pipeline. This module imports `sharp` (a native module),\n * so it must ONLY be imported by the transform strategies (the image Lambda) —\n * never from the barrel (`index.ts`) or from anything the GraphQL API handler\n * loads. See `imageFormat.ts` for the reasoning.\n */\nimport sharp from \"sharp\";\nimport type { Sharp } from \"sharp\";\nimport { WidthCollection } from \"./WidthCollection.js\";\nimport {\n contentTypeForFormat,\n DEFAULT_IMAGE_QUALITY,\n formatFromContentType,\n type ImageFormat\n} from \"./imageFormat.js\";\n\ninterface ApplyOutputFormatResult {\n pipeline: Sharp;\n contentType: string;\n}\n\n/**\n * Apply the target format + quality to a sharp pipeline. When no `format` is given,\n * re-encodes in the source format only if a `quality` was requested; otherwise the\n * pipeline is left untouched.\n */\nconst applyOutputFormat = (\n pipeline: Sharp,\n sourceContentType: string,\n options: { format?: ImageFormat; quality?: number },\n qualityDefaults: Record<ImageFormat, number>\n): ApplyOutputFormatResult => {\n const targetFormat = options.format ?? formatFromContentType(sourceContentType);\n\n // Nothing to change: unknown source format, or neither a conversion nor a quality.\n if (!targetFormat || (!options.format && options.quality === undefined)) {\n return { pipeline, contentType: sourceContentType };\n }\n\n const quality = options.quality ?? qualityDefaults[targetFormat];\n\n switch (targetFormat) {\n case \"jpeg\":\n return {\n pipeline: pipeline.jpeg({ quality }),\n contentType: contentTypeForFormat(\"jpeg\")\n };\n case \"webp\":\n return {\n pipeline: pipeline.webp({ quality }),\n contentType: contentTypeForFormat(\"webp\")\n };\n case \"avif\":\n return {\n pipeline: pipeline.avif({ quality }),\n contentType: contentTypeForFormat(\"avif\")\n };\n case \"png\":\n return {\n pipeline: pipeline.png({ quality, compressionLevel: 9, adaptiveFiltering: true }),\n contentType: contentTypeForFormat(\"png\")\n };\n }\n};\n\nexport interface TransformImageParams {\n buffer: Buffer;\n animated: boolean;\n sourceContentType: string;\n widths: number[];\n options: { width?: number; format?: ImageFormat; quality?: number };\n qualityDefaults?: Record<ImageFormat, number>;\n}\n\nexport interface TransformImageResult {\n buffer: Buffer;\n contentType: string;\n}\n\n/**\n * Single sharp pipeline shared by the S3 and local transform strategies: optional\n * resize (snapped to the configured width ladder) → optional format conversion +\n * quality. Returns the resulting buffer and its content type.\n */\ninterface CropInsets {\n top: number;\n left: number;\n bottom: number;\n right: number;\n}\n\nconst clamp01 = (n: number) => Math.min(1, Math.max(0, n));\n\n/**\n * Extract the crop region (edge insets, 0..1) from an image buffer. Returns the\n * original buffer unchanged if the crop is empty/full or the dimensions are unknown.\n */\nexport const cropImageBuffer = async (buffer: Buffer, crop: CropInsets): Promise<Buffer> => {\n const image = sharp(buffer);\n const meta = await image.metadata();\n const w = meta.width ?? 0;\n const h = meta.height ?? 0;\n if (!w || !h) {\n return buffer;\n }\n\n const left = Math.max(0, Math.min(w - 1, Math.round(clamp01(crop.left) * w)));\n const top = Math.max(0, Math.min(h - 1, Math.round(clamp01(crop.top) * h)));\n const width = Math.max(\n 1,\n Math.min(w - left, Math.round((1 - clamp01(crop.left) - clamp01(crop.right)) * w))\n );\n const height = Math.max(\n 1,\n Math.min(h - top, Math.round((1 - clamp01(crop.top) - clamp01(crop.bottom)) * h))\n );\n\n if (left === 0 && top === 0 && width === w && height === h) {\n return buffer;\n }\n\n return image.extract({ left, top, width, height }).toBuffer();\n};\n\ninterface NormalizedRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nconst cropToRect = (crop?: CropInsets): NormalizedRect => {\n const left = clamp01(crop?.left ?? 0);\n const top = clamp01(crop?.top ?? 0);\n return {\n x: left,\n y: top,\n width: Math.max(0, 1 - left - clamp01(crop?.right ?? 0)),\n height: Math.max(0, 1 - top - clamp01(crop?.bottom ?? 0))\n };\n};\n\n/**\n * The visible region (normalized 0–1) for the given framing. The largest rectangle\n * of `aspectRatio` that fits inside the crop, centered on the focal point.\n */\nexport const getVisibleRect = (\n imageWidth: number,\n imageHeight: number,\n crop?: CropInsets,\n focal?: { x: number; y: number },\n aspectRatio?: number\n): NormalizedRect => {\n const cr = cropToRect(crop);\n if (aspectRatio === undefined || aspectRatio <= 0 || cr.width <= 0 || cr.height <= 0) {\n return cr;\n }\n const iw = imageWidth > 0 ? imageWidth : 1;\n const ih = imageHeight > 0 ? imageHeight : 1;\n const cropAR = (cr.width * iw) / (cr.height * ih);\n\n let w: number;\n let h: number;\n if (aspectRatio > cropAR) {\n w = cr.width;\n h = (cr.width * iw) / aspectRatio / ih;\n } else {\n h = cr.height;\n w = (cr.height * ih * aspectRatio) / iw;\n }\n\n const fx = clamp01(focal?.x ?? 0.5);\n const fy = clamp01(focal?.y ?? 0.5);\n const maxX = cr.x + cr.width - w;\n const maxY = cr.y + cr.height - h;\n const x = Math.min(Math.max(fx - w / 2, cr.x), Math.max(cr.x, maxX));\n const y = Math.min(Math.max(fy - h / 2, cr.y), Math.max(cr.y, maxY));\n return { x, y, width: w, height: h };\n};\n\nexport interface Framing {\n crop?: CropInsets;\n focal?: { x: number; y: number };\n aspectRatio?: number;\n}\n\nexport const hasFraming = (framing: Framing): boolean => {\n const c = framing.crop;\n const cropped = !!c && !(c.top === 0 && c.left === 0 && c.bottom === 0 && c.right === 0);\n return cropped || framing.aspectRatio !== undefined;\n};\n\nexport const extractFramedRegion = async (buffer: Buffer, framing: Framing): Promise<Buffer> => {\n if (!hasFraming(framing)) {\n return buffer;\n }\n const image = sharp(buffer);\n const meta = await image.metadata();\n const w = meta.width ?? 0;\n const h = meta.height ?? 0;\n if (!w || !h) {\n return buffer;\n }\n\n const rect = getVisibleRect(w, h, framing.crop, framing.focal, framing.aspectRatio);\n const left = Math.max(0, Math.min(w - 1, Math.round(rect.x * w)));\n const top = Math.max(0, Math.min(h - 1, Math.round(rect.y * h)));\n const width = Math.max(1, Math.min(w - left, Math.round(rect.width * w)));\n const height = Math.max(1, Math.min(h - top, Math.round(rect.height * h)));\n\n if (left === 0 && top === 0 && width === w && height === h) {\n return buffer;\n }\n return image.extract({ left, top, width, height }).toBuffer();\n};\n\nexport const transformImageBuffer = async (\n params: TransformImageParams\n): Promise<TransformImageResult> => {\n let pipeline = sharp(params.buffer, { animated: params.animated }).withMetadata();\n\n if (params.options.width) {\n const width = WidthCollection.create(params.widths).getClosestOrMax(params.options.width);\n pipeline = pipeline.resize({ width, withoutEnlargement: true });\n }\n\n const { pipeline: encoded, contentType } = applyOutputFormat(\n pipeline,\n params.sourceContentType,\n params.options,\n params.qualityDefaults ?? DEFAULT_IMAGE_QUALITY\n );\n\n const buffer = await encoded.toBuffer();\n\n return { buffer, contentType };\n};\n"],"names":["applyOutputFormat","pipeline","sourceContentType","options","qualityDefaults","targetFormat","formatFromContentType","undefined","quality","contentTypeForFormat","clamp01","n","Math","cropImageBuffer","buffer","crop","image","sharp","meta","w","h","left","top","width","height","cropToRect","getVisibleRect","imageWidth","imageHeight","focal","aspectRatio","cr","iw","ih","cropAR","fx","fy","maxX","maxY","x","y","hasFraming","framing","c","cropped","extractFramedRegion","rect","transformImageBuffer","params","WidthCollection","encoded","contentType","DEFAULT_IMAGE_QUALITY"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Framing } from "./imageTypes.js";
|
|
2
|
+
declare const SUPPORTED_TRANSFORMABLE_IMAGES: string[];
|
|
3
|
+
declare const getOptimizedImageKeyPrefix: (key: string, cropSignature?: string) => string;
|
|
4
|
+
declare const getOptimizedTransformedImageKeyPrefix: (key: string, transformationsHash: string, cropSignature?: string) => string;
|
|
5
|
+
interface Crop {
|
|
6
|
+
top: number;
|
|
7
|
+
left: number;
|
|
8
|
+
bottom: number;
|
|
9
|
+
right: number;
|
|
10
|
+
}
|
|
11
|
+
/** Short, stable signature of a crop; `undefined` for no/empty crop (so keys are unchanged). */
|
|
12
|
+
declare const getCropSignature: (crop?: Crop) => string | undefined;
|
|
13
|
+
interface GetImageKeyParams {
|
|
14
|
+
key: string;
|
|
15
|
+
transformations?: any;
|
|
16
|
+
cropSignature?: string;
|
|
17
|
+
}
|
|
18
|
+
declare const getImageKey: ({ key, transformations, cropSignature }: GetImageKeyParams) => string;
|
|
19
|
+
/**
|
|
20
|
+
* Signature for a full framing (crop + focal + aspect ratio) used to namespace
|
|
21
|
+
* cached derivatives. `undefined` when the framing is a no-op. A crop-only framing
|
|
22
|
+
* intentionally hashes to the same value as `getCropSignature`, so existing
|
|
23
|
+
* asset-level-crop derivatives keep their cache keys (backward compatible).
|
|
24
|
+
*/
|
|
25
|
+
declare const getFramingSignature: (framing: Framing) => string | undefined;
|
|
26
|
+
export { SUPPORTED_TRANSFORMABLE_IMAGES, getImageKey, getCropSignature, getFramingSignature, getOptimizedImageKeyPrefix, getOptimizedTransformedImageKeyPrefix };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import object_hash from "object-hash";
|
|
2
|
+
import node_path from "node:path";
|
|
3
|
+
const SUPPORTED_TRANSFORMABLE_IMAGES = [
|
|
4
|
+
"jpg",
|
|
5
|
+
"jpeg",
|
|
6
|
+
"png",
|
|
7
|
+
"webp"
|
|
8
|
+
];
|
|
9
|
+
const parseKey = (key)=>{
|
|
10
|
+
const filename = node_path.basename(key);
|
|
11
|
+
const dirname = node_path.dirname(key);
|
|
12
|
+
return {
|
|
13
|
+
filename,
|
|
14
|
+
dirname
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const cropSegment = (cropSignature)=>cropSignature ? `${cropSignature}/` : "";
|
|
18
|
+
const getOptimizedImageKeyPrefix = (key, cropSignature)=>{
|
|
19
|
+
const { filename, dirname } = parseKey(key);
|
|
20
|
+
return `${dirname}/optimized/${cropSegment(cropSignature)}${filename}`;
|
|
21
|
+
};
|
|
22
|
+
const getOptimizedTransformedImageKeyPrefix = (key, transformationsHash, cropSignature)=>{
|
|
23
|
+
const { filename, dirname } = parseKey(key);
|
|
24
|
+
return `${dirname}/optimized/${cropSegment(cropSignature)}${transformationsHash}-${filename}`;
|
|
25
|
+
};
|
|
26
|
+
const getCropSignature = (crop)=>{
|
|
27
|
+
if (!crop || 0 === crop.top && 0 === crop.left && 0 === crop.bottom && 0 === crop.right) return;
|
|
28
|
+
return object_hash(crop).slice(0, 16);
|
|
29
|
+
};
|
|
30
|
+
const getImageKey = ({ key, transformations, cropSignature })=>{
|
|
31
|
+
if (!transformations) return getOptimizedImageKeyPrefix(key, cropSignature);
|
|
32
|
+
return getOptimizedTransformedImageKeyPrefix(key, object_hash(transformations), cropSignature);
|
|
33
|
+
};
|
|
34
|
+
const getFramingSignature = (framing)=>{
|
|
35
|
+
const { crop, focal, aspectRatio } = framing;
|
|
36
|
+
const cropped = !!crop && !(0 === crop.top && 0 === crop.left && 0 === crop.bottom && 0 === crop.right);
|
|
37
|
+
if (!cropped && void 0 === aspectRatio) return;
|
|
38
|
+
if (cropped && void 0 === focal && void 0 === aspectRatio) return object_hash(crop).slice(0, 16);
|
|
39
|
+
return object_hash({
|
|
40
|
+
crop: cropped ? crop : void 0,
|
|
41
|
+
focal,
|
|
42
|
+
aspectRatio
|
|
43
|
+
}).slice(0, 16);
|
|
44
|
+
};
|
|
45
|
+
export { SUPPORTED_TRANSFORMABLE_IMAGES, getCropSignature, getFramingSignature, getImageKey, getOptimizedImageKeyPrefix, getOptimizedTransformedImageKeyPrefix };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/assetTypes/image/utils.js","sources":["../../../../../src/features/assetDelivery/assetTypes/image/utils.ts"],"sourcesContent":["import objectHash from \"object-hash\";\nimport path from \"node:path\";\nimport type { Framing } from \"./imageTypes.js\";\n\nconst SUPPORTED_TRANSFORMABLE_IMAGES = [\"jpg\", \"jpeg\", \"png\", \"webp\"];\n\nconst parseKey = (key: string) => {\n const filename = path.basename(key);\n const dirname = path.dirname(key);\n\n return { filename, dirname };\n};\n\n// An optional per-file crop signature segment. Keeps un-cropped keys unchanged\n// (backward compatible) while giving cropped files their own cache namespace, so\n// changing a file's crop invalidates its derivatives.\nconst cropSegment = (cropSignature?: string): string => {\n return cropSignature ? `${cropSignature}/` : \"\";\n};\n\nconst getOptimizedImageKeyPrefix = (key: string, cropSignature?: string): string => {\n const { filename, dirname } = parseKey(key);\n\n return `${dirname}/optimized/${cropSegment(cropSignature)}${filename}`;\n};\n\nconst getOptimizedTransformedImageKeyPrefix = (\n key: string,\n transformationsHash: string,\n cropSignature?: string\n): string => {\n const { filename, dirname } = parseKey(key);\n\n return `${dirname}/optimized/${cropSegment(cropSignature)}${transformationsHash}-${filename}`;\n};\n\ninterface Crop {\n top: number;\n left: number;\n bottom: number;\n right: number;\n}\n\n/** Short, stable signature of a crop; `undefined` for no/empty crop (so keys are unchanged). */\nconst getCropSignature = (crop?: Crop): string | undefined => {\n if (!crop || (crop.top === 0 && crop.left === 0 && crop.bottom === 0 && crop.right === 0)) {\n return undefined;\n }\n return objectHash(crop).slice(0, 16);\n};\n\ninterface GetImageKeyParams {\n key: string;\n transformations?: any;\n cropSignature?: string;\n}\n\nconst getImageKey = ({ key, transformations, cropSignature }: GetImageKeyParams): string => {\n if (!transformations) {\n return getOptimizedImageKeyPrefix(key, cropSignature);\n }\n\n return getOptimizedTransformedImageKeyPrefix(key, objectHash(transformations), cropSignature);\n};\n\n/**\n * Signature for a full framing (crop + focal + aspect ratio) used to namespace\n * cached derivatives. `undefined` when the framing is a no-op. A crop-only framing\n * intentionally hashes to the same value as `getCropSignature`, so existing\n * asset-level-crop derivatives keep their cache keys (backward compatible).\n */\nconst getFramingSignature = (framing: Framing): string | undefined => {\n const { crop, focal, aspectRatio } = framing;\n const cropped =\n !!crop && !(crop.top === 0 && crop.left === 0 && crop.bottom === 0 && crop.right === 0);\n\n if (!cropped && aspectRatio === undefined) {\n return undefined;\n }\n if (cropped && focal === undefined && aspectRatio === undefined) {\n return objectHash(crop).slice(0, 16);\n }\n return objectHash({ crop: cropped ? crop : undefined, focal, aspectRatio }).slice(0, 16);\n};\n\nexport {\n SUPPORTED_TRANSFORMABLE_IMAGES,\n getImageKey,\n getCropSignature,\n getFramingSignature,\n getOptimizedImageKeyPrefix,\n getOptimizedTransformedImageKeyPrefix\n};\n"],"names":["SUPPORTED_TRANSFORMABLE_IMAGES","parseKey","key","filename","path","dirname","cropSegment","cropSignature","getOptimizedImageKeyPrefix","getOptimizedTransformedImageKeyPrefix","transformationsHash","getCropSignature","crop","objectHash","getImageKey","transformations","getFramingSignature","framing","focal","aspectRatio","cropped","undefined"],"mappings":";;AAIA,MAAMA,iCAAiC;IAAC;IAAO;IAAQ;IAAO;CAAO;AAErE,MAAMC,WAAW,CAACC;IACd,MAAMC,WAAWC,UAAAA,QAAa,CAACF;IAC/B,MAAMG,UAAUD,UAAAA,OAAY,CAACF;IAE7B,OAAO;QAAEC;QAAUE;IAAQ;AAC/B;AAKA,MAAMC,cAAc,CAACC,gBACVA,gBAAgB,GAAGA,cAAc,CAAC,CAAC,GAAG;AAGjD,MAAMC,6BAA6B,CAACN,KAAaK;IAC7C,MAAM,EAAEJ,QAAQ,EAAEE,OAAO,EAAE,GAAGJ,SAASC;IAEvC,OAAO,GAAGG,QAAQ,WAAW,EAAEC,YAAYC,iBAAiBJ,UAAU;AAC1E;AAEA,MAAMM,wCAAwC,CAC1CP,KACAQ,qBACAH;IAEA,MAAM,EAAEJ,QAAQ,EAAEE,OAAO,EAAE,GAAGJ,SAASC;IAEvC,OAAO,GAAGG,QAAQ,WAAW,EAAEC,YAAYC,iBAAiBG,oBAAoB,CAAC,EAAEP,UAAU;AACjG;AAUA,MAAMQ,mBAAmB,CAACC;IACtB,IAAI,CAACA,QAASA,AAAa,MAAbA,KAAK,GAAG,IAAUA,AAAc,MAAdA,KAAK,IAAI,IAAUA,AAAgB,MAAhBA,KAAK,MAAM,IAAUA,AAAe,MAAfA,KAAK,KAAK,EAC9E;IAEJ,OAAOC,YAAWD,MAAM,KAAK,CAAC,GAAG;AACrC;AAQA,MAAME,cAAc,CAAC,EAAEZ,GAAG,EAAEa,eAAe,EAAER,aAAa,EAAqB;IAC3E,IAAI,CAACQ,iBACD,OAAOP,2BAA2BN,KAAKK;IAG3C,OAAOE,sCAAsCP,KAAKW,YAAWE,kBAAkBR;AACnF;AAQA,MAAMS,sBAAsB,CAACC;IACzB,MAAM,EAAEL,IAAI,EAAEM,KAAK,EAAEC,WAAW,EAAE,GAAGF;IACrC,MAAMG,UACF,CAAC,CAACR,QAAQ,CAAEA,CAAAA,AAAa,MAAbA,KAAK,GAAG,IAAUA,AAAc,MAAdA,KAAK,IAAI,IAAUA,AAAgB,MAAhBA,KAAK,MAAM,IAAUA,AAAe,MAAfA,KAAK,KAAK,AAAK;IAEzF,IAAI,CAACQ,WAAWD,AAAgBE,WAAhBF,aACZ;IAEJ,IAAIC,WAAWF,AAAUG,WAAVH,SAAuBC,AAAgBE,WAAhBF,aAClC,OAAON,YAAWD,MAAM,KAAK,CAAC,GAAG;IAErC,OAAOC,YAAW;QAAE,MAAMO,UAAUR,OAAOS;QAAWH;QAAOC;IAAY,GAAG,KAAK,CAAC,GAAG;AACzF"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { createFeature } from "@webiny/feature/api";
|
|
2
|
-
import {
|
|
2
|
+
import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
|
|
3
|
+
import { AssetProcessor } from "./abstractions.js";
|
|
3
4
|
import { FilesAssetRequestResolverImpl } from "./FilesAssetRequestResolver.js";
|
|
4
5
|
import { NullAssetResolverImpl } from "./NullAssetResolver.js";
|
|
5
6
|
import { NullAssetOutputStrategyImpl } from "./NullAssetOutputStrategy.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { AssetFactory } from "./Asset/Asset.js";
|
|
8
|
+
import { ObjectKey } from "./ObjectKey/ObjectKey.js";
|
|
9
|
+
import { StreamAssetReply } from "./StreamAssetReply/StreamAssetReply.js";
|
|
10
|
+
import { TransformationAssetProcessor } from "./transformation/TransformationAssetProcessor.js";
|
|
11
|
+
import { ImageAssetType } from "./assetTypes/image/ImageAssetType.js";
|
|
8
12
|
import { PrivateFileAssetRequestResolverDecorator } from "./privateFiles/PrivateFileAssetRequestResolver.js";
|
|
9
13
|
import { PrivateAuthenticatedAuthorizerImpl } from "./privateFiles/PrivateAuthenticatedAuthorizer.js";
|
|
10
14
|
import { PrivateFilesAssetProcessorDecorator } from "./privateFiles/PrivateFilesAssetProcessor.js";
|
|
@@ -14,11 +18,13 @@ const AssetDeliveryFeature = createFeature({
|
|
|
14
18
|
container.register(FilesAssetRequestResolverImpl);
|
|
15
19
|
container.register(NullAssetResolverImpl);
|
|
16
20
|
container.register(NullAssetOutputStrategyImpl);
|
|
17
|
-
container.register(
|
|
18
|
-
container.register(
|
|
21
|
+
container.register(AssetFactory).inSingletonScope();
|
|
22
|
+
container.register(ObjectKey).inSingletonScope();
|
|
23
|
+
container.register(StreamAssetReply).inSingletonScope();
|
|
24
|
+
container.register(ImageAssetType);
|
|
25
|
+
container.registerInstance(AssetProcessor, new TransformationAssetProcessor(container));
|
|
19
26
|
container.registerDecorator(PrivateFileAssetRequestResolverDecorator);
|
|
20
|
-
|
|
21
|
-
if (wcp.canUsePrivateFiles()) {
|
|
27
|
+
if (container.resolve(FeatureFlags).get().isEnabled("advancedAccessControlLayer.privateFiles")) {
|
|
22
28
|
container.register(PrivateAuthenticatedAuthorizerImpl);
|
|
23
29
|
container.registerDecorator(PrivateFilesAssetProcessorDecorator);
|
|
24
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/assetDelivery/feature.js","sources":["../../../src/features/assetDelivery/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport {
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/feature.js","sources":["../../../src/features/assetDelivery/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\nimport { AssetProcessor } from \"./abstractions.js\";\nimport { FilesAssetRequestResolverImpl } from \"./FilesAssetRequestResolver.js\";\nimport { NullAssetResolverImpl } from \"./NullAssetResolver.js\";\nimport { NullAssetOutputStrategyImpl } from \"./NullAssetOutputStrategy.js\";\nimport { AssetFactory } from \"./Asset/Asset.js\";\nimport { ObjectKey } from \"./ObjectKey/ObjectKey.js\";\nimport { StreamAssetReply } from \"./StreamAssetReply/StreamAssetReply.js\";\nimport { TransformationAssetProcessor } from \"./transformation/TransformationAssetProcessor.js\";\nimport { ImageAssetType } from \"./assetTypes/image/ImageAssetType.js\";\nimport { PrivateFileAssetRequestResolverDecorator } from \"./privateFiles/PrivateFileAssetRequestResolver.js\";\nimport { PrivateAuthenticatedAuthorizerImpl } from \"./privateFiles/PrivateAuthenticatedAuthorizer.js\";\nimport { PrivateFilesAssetProcessorDecorator } from \"./privateFiles/PrivateFilesAssetProcessor.js\";\n\nexport const AssetDeliveryFeature = createFeature({\n name: \"AssetDelivery\",\n register(container) {\n container.register(FilesAssetRequestResolverImpl);\n container.register(NullAssetResolverImpl);\n container.register(NullAssetOutputStrategyImpl);\n container.register(AssetFactory).inSingletonScope();\n container.register(ObjectKey).inSingletonScope();\n container.register(StreamAssetReply).inSingletonScope();\n container.register(ImageAssetType);\n container.registerInstance(AssetProcessor, new TransformationAssetProcessor(container));\n\n container.registerDecorator(PrivateFileAssetRequestResolverDecorator);\n\n // Register-time gate on the effective feature flags (userFlag && live WCP license). Valid at\n // register() time because the license is refreshed PRE-register (WcpLicenseLoader.load in\n // registerApiRequestStack) and FeatureFlags reads that process cache — and it re-evaluates per\n // request since the child re-registers, so a license change takes effect on the next request.\n if (\n container\n .resolve(FeatureFlags)\n .get()\n .isEnabled(\"advancedAccessControlLayer.privateFiles\")\n ) {\n container.register(PrivateAuthenticatedAuthorizerImpl);\n container.registerDecorator(PrivateFilesAssetProcessorDecorator);\n }\n }\n});\n"],"names":["AssetDeliveryFeature","createFeature","container","FilesAssetRequestResolverImpl","NullAssetResolverImpl","NullAssetOutputStrategyImpl","AssetFactory","ObjectKey","StreamAssetReply","ImageAssetType","AssetProcessor","TransformationAssetProcessor","PrivateFileAssetRequestResolverDecorator","FeatureFlags","PrivateAuthenticatedAuthorizerImpl","PrivateFilesAssetProcessorDecorator"],"mappings":";;;;;;;;;;;;;;AAeO,MAAMA,uBAAuBC,cAAc;IAC9C,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI,cAAc,gBAAgB;QACjDJ,UAAU,QAAQ,CAACK,WAAW,gBAAgB;QAC9CL,UAAU,QAAQ,CAACM,kBAAkB,gBAAgB;QACrDN,UAAU,QAAQ,CAACO;QACnBP,UAAU,gBAAgB,CAACQ,gBAAgB,IAAIC,6BAA6BT;QAE5EA,UAAU,iBAAiB,CAACU;QAM5B,IACIV,UACK,OAAO,CAACW,cACR,GAAG,GACH,SAAS,CAAC,4CACjB;YACEX,UAAU,QAAQ,CAACY;YACnBZ,UAAU,iBAAiB,CAACa;QAChC;IACJ;AACJ"}
|
|
@@ -5,14 +5,14 @@ class PrivateFileAssetRequestResolver {
|
|
|
5
5
|
this.resolver = resolver;
|
|
6
6
|
}
|
|
7
7
|
async resolve(request) {
|
|
8
|
-
if (!request.url
|
|
8
|
+
if (!request.url?.startsWith("/private/")) return this.resolver.resolve(request);
|
|
9
9
|
const params = request.params ?? {};
|
|
10
10
|
const query = request.query ?? {};
|
|
11
11
|
const path = params["*"];
|
|
12
12
|
return new AssetRequest({
|
|
13
13
|
key: decodeURI(path).replace("/private/", ""),
|
|
14
14
|
context: {
|
|
15
|
-
url: request.url,
|
|
15
|
+
url: request.url ?? "",
|
|
16
16
|
private: true
|
|
17
17
|
},
|
|
18
18
|
options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/assetDelivery/privateFiles/PrivateFileAssetRequestResolver.js","sources":["../../../../src/features/assetDelivery/privateFiles/PrivateFileAssetRequestResolver.ts"],"sourcesContent":["import type { Request } from \"@webiny/handler/types.js\";\nimport { AssetRequest } from \"~/delivery/AssetDelivery/AssetRequest.js\";\nimport { AssetRequestResolver, type IAssetRequestResolver } from \"../abstractions.js\";\n\nexport class PrivateFileAssetRequestResolver implements IAssetRequestResolver {\n private readonly resolver: IAssetRequestResolver;\n\n constructor(resolver: IAssetRequestResolver) {\n this.resolver = resolver;\n }\n\n async resolve(request: Request): Promise<AssetRequest | undefined> {\n if (!request.url
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/privateFiles/PrivateFileAssetRequestResolver.js","sources":["../../../../src/features/assetDelivery/privateFiles/PrivateFileAssetRequestResolver.ts"],"sourcesContent":["import type { Request } from \"@webiny/handler/types.js\";\nimport { AssetRequest } from \"~/delivery/AssetDelivery/AssetRequest.js\";\nimport { AssetRequestResolver, type IAssetRequestResolver } from \"../abstractions.js\";\n\nexport class PrivateFileAssetRequestResolver implements IAssetRequestResolver {\n private readonly resolver: IAssetRequestResolver;\n\n constructor(resolver: IAssetRequestResolver) {\n this.resolver = resolver;\n }\n\n async resolve(request: Request): Promise<AssetRequest | undefined> {\n if (!request.url?.startsWith(\"/private/\")) {\n return this.resolver.resolve(request);\n }\n\n const params = (request.params ?? {}) as Record<string, any>;\n const query = (request.query ?? {}) as Record<string, any>;\n\n const path = params[\"*\"];\n\n return new AssetRequest({\n key: decodeURI(path).replace(\"/private/\", \"\"),\n context: {\n url: request.url ?? \"\",\n private: true\n },\n options: {\n ...query,\n width: query.width ? parseInt(query.width) : undefined\n }\n });\n }\n}\n\nexport const PrivateFileAssetRequestResolverDecorator = AssetRequestResolver.createDecorator({\n decorator: PrivateFileAssetRequestResolver,\n dependencies: []\n});\n"],"names":["PrivateFileAssetRequestResolver","resolver","request","params","query","path","AssetRequest","decodeURI","parseInt","undefined","PrivateFileAssetRequestResolverDecorator","AssetRequestResolver"],"mappings":";;AAIO,MAAMA;IAGT,YAAYC,QAA+B,CAAE;QACzC,IAAI,CAAC,QAAQ,GAAGA;IACpB;IAEA,MAAM,QAAQC,OAAgB,EAAqC;QAC/D,IAAI,CAACA,QAAQ,GAAG,EAAE,WAAW,cACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAACA;QAGjC,MAAMC,SAAUD,QAAQ,MAAM,IAAI,CAAC;QACnC,MAAME,QAASF,QAAQ,KAAK,IAAI,CAAC;QAEjC,MAAMG,OAAOF,MAAM,CAAC,IAAI;QAExB,OAAO,IAAIG,aAAa;YACpB,KAAKC,UAAUF,MAAM,OAAO,CAAC,aAAa;YAC1C,SAAS;gBACL,KAAKH,QAAQ,GAAG,IAAI;gBACpB,SAAS;YACb;YACA,SAAS;gBACL,GAAGE,KAAK;gBACR,OAAOA,MAAM,KAAK,GAAGI,SAASJ,MAAM,KAAK,IAAIK;YACjD;QACJ;IACJ;AACJ;AAEO,MAAMC,2CAA2CC,qBAAqB,eAAe,CAAC;IACzF,WAAWX;IACX,cAAc,EAAE;AACpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/assetDelivery/privateFiles/PrivateFilesAssetProcessor.js","sources":["../../../../src/features/assetDelivery/privateFiles/PrivateFilesAssetProcessor.ts"],"sourcesContent":["import type { File } from \"~/domain/file/types.js\";\nimport type { Asset } from \"~/delivery/AssetDelivery/Asset.js\";\nimport type { AssetRequest } from \"~/delivery/AssetDelivery/AssetRequest.js\";\nimport { IdentityContext } from \"@webiny/api-core/features/security/IdentityContext/index.js\";\nimport { GetFileUseCase } from \"~/features/file/GetFile/index.js\";\nimport { NotAuthorizedOutputStrategy } from \"./NotAuthorizedOutputStrategy.js\";\nimport { RedirectToPublicUrlOutputStrategy } from \"./RedirectToPublicUrlOutputStrategy.js\";\nimport { RedirectToPrivateUrlOutputStrategy } from \"./RedirectToPrivateUrlOutputStrategy.js\";\nimport { PrivateCache } from \"./PrivateCache.js\";\nimport { PublicCache } from \"./PublicCache.js\";\nimport {\n AssetProcessor,\n AssetAuthorizer,\n type IAssetProcessor,\n type IAssetAuthorizer\n} from \"../abstractions.js\";\n\ninterface MaybePrivate {\n private?: boolean;\n}\n\nexport class PrivateFilesAssetProcessor implements IAssetProcessor {\n private readonly identityContext: IdentityContext.Interface;\n private readonly getFile: GetFileUseCase.Interface;\n private readonly assetAuthorizer: IAssetAuthorizer;\n private readonly assetProcessor: IAssetProcessor;\n\n constructor(\n identityContext: IdentityContext.Interface,\n getFile: GetFileUseCase.Interface,\n assetAuthorizer: IAssetAuthorizer,\n assetProcessor: IAssetProcessor\n ) {\n this.identityContext = identityContext;\n this.getFile = getFile;\n this.assetAuthorizer = assetAuthorizer;\n this.assetProcessor = assetProcessor;\n }\n\n async process(assetRequest: AssetRequest, asset: Asset): Promise<Asset> {\n const id = asset.getId();\n\n const file = await this.identityContext.withoutAuthorization(async () => {\n const fileResult = await this.getFile.execute(id);\n if (fileResult.isFail()) {\n throw fileResult.error;\n }\n return fileResult.value;\n });\n\n const isPrivateFile = this.isPrivate(file);\n\n if (!isPrivateFile && this.requestedViaPrivateEndpoint(assetRequest)) {\n asset.setOutputStrategy(new RedirectToPublicUrlOutputStrategy(assetRequest));\n return asset;\n }\n\n if (isPrivateFile && this.requestedViaPublicEndpoint(assetRequest)) {\n asset.setOutputStrategy(new RedirectToPrivateUrlOutputStrategy(assetRequest));\n return asset;\n }\n\n try {\n await this.assetAuthorizer.authorize(file);\n } catch {\n asset.setOutputStrategy(new NotAuthorizedOutputStrategy());\n return asset;\n }\n\n const processedAsset = await this.assetProcessor.process(assetRequest, asset);\n\n processedAsset.setOutputStrategy(strategy => {\n if (!strategy) {\n throw Error(`No asset output strategy is configured!`);\n }\n return isPrivateFile ? new PrivateCache(strategy) : new PublicCache(strategy);\n });\n\n return processedAsset;\n }\n\n private isPrivate(file: File) {\n return file.accessControl && file.accessControl.type.startsWith(\"private-\");\n }\n\n private requestedViaPrivateEndpoint(assetRequest: AssetRequest) {\n return assetRequest.getContext<MaybePrivate>().private;\n }\n\n private requestedViaPublicEndpoint(assetRequest: AssetRequest) {\n return !this.requestedViaPrivateEndpoint(assetRequest);\n }\n}\n\nexport const PrivateFilesAssetProcessorDecorator = AssetProcessor.createDecorator({\n decorator: PrivateFilesAssetProcessor,\n dependencies: [IdentityContext, GetFileUseCase, AssetAuthorizer]\n});\n"],"names":["PrivateFilesAssetProcessor","identityContext","getFile","assetAuthorizer","assetProcessor","assetRequest","asset","id","file","fileResult","isPrivateFile","RedirectToPublicUrlOutputStrategy","RedirectToPrivateUrlOutputStrategy","NotAuthorizedOutputStrategy","processedAsset","strategy","Error","PrivateCache","PublicCache","PrivateFilesAssetProcessorDecorator","AssetProcessor","IdentityContext","GetFileUseCase","AssetAuthorizer"],"mappings":";;;;;;;;AAqBO,MAAMA;IAMT,YACIC,eAA0C,EAC1CC,OAAiC,EACjCC,eAAiC,EACjCC,cAA+B,CACjC;QACE,IAAI,CAAC,eAAe,GAAGH;QACvB,IAAI,CAAC,OAAO,GAAGC;QACf,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,cAAc,GAAGC;IAC1B;IAEA,MAAM,QAAQC,YAA0B,EAAEC,KAAY,EAAkB;
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/privateFiles/PrivateFilesAssetProcessor.js","sources":["../../../../src/features/assetDelivery/privateFiles/PrivateFilesAssetProcessor.ts"],"sourcesContent":["import type { File } from \"~/domain/file/types.js\";\nimport type { Asset } from \"~/delivery/AssetDelivery/Asset.js\";\nimport type { AssetRequest } from \"~/delivery/AssetDelivery/AssetRequest.js\";\nimport { IdentityContext } from \"@webiny/api-core/features/security/IdentityContext/index.js\";\nimport { GetFileUseCase } from \"~/features/file/GetFile/index.js\";\nimport { NotAuthorizedOutputStrategy } from \"./NotAuthorizedOutputStrategy.js\";\nimport { RedirectToPublicUrlOutputStrategy } from \"./RedirectToPublicUrlOutputStrategy.js\";\nimport { RedirectToPrivateUrlOutputStrategy } from \"./RedirectToPrivateUrlOutputStrategy.js\";\nimport { PrivateCache } from \"./PrivateCache.js\";\nimport { PublicCache } from \"./PublicCache.js\";\nimport {\n AssetProcessor,\n AssetAuthorizer,\n type IAssetProcessor,\n type IAssetAuthorizer\n} from \"../abstractions.js\";\n\ninterface MaybePrivate {\n private?: boolean;\n}\n\nexport class PrivateFilesAssetProcessor implements IAssetProcessor {\n private readonly identityContext: IdentityContext.Interface;\n private readonly getFile: GetFileUseCase.Interface;\n private readonly assetAuthorizer: IAssetAuthorizer;\n private readonly assetProcessor: IAssetProcessor;\n\n constructor(\n identityContext: IdentityContext.Interface,\n getFile: GetFileUseCase.Interface,\n assetAuthorizer: IAssetAuthorizer,\n assetProcessor: IAssetProcessor\n ) {\n this.identityContext = identityContext;\n this.getFile = getFile;\n this.assetAuthorizer = assetAuthorizer;\n this.assetProcessor = assetProcessor;\n }\n\n async process(assetRequest: AssetRequest, asset: Asset): Promise<Asset> {\n // Only registered when private files are licensed (register-time gate in AssetDeliveryFeature),\n // so no per-request license guard is needed here.\n const id = asset.getId();\n\n const file = await this.identityContext.withoutAuthorization(async () => {\n const fileResult = await this.getFile.execute(id);\n if (fileResult.isFail()) {\n throw fileResult.error;\n }\n return fileResult.value;\n });\n\n const isPrivateFile = this.isPrivate(file);\n\n if (!isPrivateFile && this.requestedViaPrivateEndpoint(assetRequest)) {\n asset.setOutputStrategy(new RedirectToPublicUrlOutputStrategy(assetRequest));\n return asset;\n }\n\n if (isPrivateFile && this.requestedViaPublicEndpoint(assetRequest)) {\n asset.setOutputStrategy(new RedirectToPrivateUrlOutputStrategy(assetRequest));\n return asset;\n }\n\n try {\n await this.assetAuthorizer.authorize(file);\n } catch {\n asset.setOutputStrategy(new NotAuthorizedOutputStrategy());\n return asset;\n }\n\n const processedAsset = await this.assetProcessor.process(assetRequest, asset);\n\n processedAsset.setOutputStrategy(strategy => {\n if (!strategy) {\n throw Error(`No asset output strategy is configured!`);\n }\n return isPrivateFile ? new PrivateCache(strategy) : new PublicCache(strategy);\n });\n\n return processedAsset;\n }\n\n private isPrivate(file: File) {\n return file.accessControl && file.accessControl.type.startsWith(\"private-\");\n }\n\n private requestedViaPrivateEndpoint(assetRequest: AssetRequest) {\n return assetRequest.getContext<MaybePrivate>().private;\n }\n\n private requestedViaPublicEndpoint(assetRequest: AssetRequest) {\n return !this.requestedViaPrivateEndpoint(assetRequest);\n }\n}\n\nexport const PrivateFilesAssetProcessorDecorator = AssetProcessor.createDecorator({\n decorator: PrivateFilesAssetProcessor,\n dependencies: [IdentityContext, GetFileUseCase, AssetAuthorizer]\n});\n"],"names":["PrivateFilesAssetProcessor","identityContext","getFile","assetAuthorizer","assetProcessor","assetRequest","asset","id","file","fileResult","isPrivateFile","RedirectToPublicUrlOutputStrategy","RedirectToPrivateUrlOutputStrategy","NotAuthorizedOutputStrategy","processedAsset","strategy","Error","PrivateCache","PublicCache","PrivateFilesAssetProcessorDecorator","AssetProcessor","IdentityContext","GetFileUseCase","AssetAuthorizer"],"mappings":";;;;;;;;AAqBO,MAAMA;IAMT,YACIC,eAA0C,EAC1CC,OAAiC,EACjCC,eAAiC,EACjCC,cAA+B,CACjC;QACE,IAAI,CAAC,eAAe,GAAGH;QACvB,IAAI,CAAC,OAAO,GAAGC;QACf,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,cAAc,GAAGC;IAC1B;IAEA,MAAM,QAAQC,YAA0B,EAAEC,KAAY,EAAkB;QAGpE,MAAMC,KAAKD,MAAM,KAAK;QAEtB,MAAME,OAAO,MAAM,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC;YACzD,MAAMC,aAAa,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAACF;YAC9C,IAAIE,WAAW,MAAM,IACjB,MAAMA,WAAW,KAAK;YAE1B,OAAOA,WAAW,KAAK;QAC3B;QAEA,MAAMC,gBAAgB,IAAI,CAAC,SAAS,CAACF;QAErC,IAAI,CAACE,iBAAiB,IAAI,CAAC,2BAA2B,CAACL,eAAe;YAClEC,MAAM,iBAAiB,CAAC,IAAIK,kCAAkCN;YAC9D,OAAOC;QACX;QAEA,IAAII,iBAAiB,IAAI,CAAC,0BAA0B,CAACL,eAAe;YAChEC,MAAM,iBAAiB,CAAC,IAAIM,mCAAmCP;YAC/D,OAAOC;QACX;QAEA,IAAI;YACA,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,CAACE;QACzC,EAAE,OAAM;YACJF,MAAM,iBAAiB,CAAC,IAAIO;YAC5B,OAAOP;QACX;QAEA,MAAMQ,iBAAiB,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAACT,cAAcC;QAEvEQ,eAAe,iBAAiB,CAACC,CAAAA;YAC7B,IAAI,CAACA,UACD,MAAMC,MAAM;YAEhB,OAAON,gBAAgB,IAAIO,aAAaF,YAAY,IAAIG,YAAYH;QACxE;QAEA,OAAOD;IACX;IAEQ,UAAUN,IAAU,EAAE;QAC1B,OAAOA,KAAK,aAAa,IAAIA,KAAK,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC;IACpE;IAEQ,4BAA4BH,YAA0B,EAAE;QAC5D,OAAOA,aAAa,UAAU,GAAiB,OAAO;IAC1D;IAEQ,2BAA2BA,YAA0B,EAAE;QAC3D,OAAO,CAAC,IAAI,CAAC,2BAA2B,CAACA;IAC7C;AACJ;AAEO,MAAMc,sCAAsCC,eAAe,eAAe,CAAC;IAC9E,WAAWpB;IACX,cAAc;QAACqB;QAAiBC;QAAgBC;KAAgB;AACpE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AssetContentsReader } from "../../../features/assetDelivery/abstractions.js";
|
|
2
|
+
interface ContentsCallable {
|
|
3
|
+
(): Promise<Buffer> | Buffer;
|
|
4
|
+
}
|
|
5
|
+
export declare class CallableContentsReader implements AssetContentsReader.Interface {
|
|
6
|
+
private readonly callable;
|
|
7
|
+
static create(callable: ContentsCallable): CallableContentsReader;
|
|
8
|
+
private constructor();
|
|
9
|
+
read(): Promise<Buffer>;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CallableContentsReader {
|
|
2
|
+
static create(callable) {
|
|
3
|
+
return new CallableContentsReader(callable);
|
|
4
|
+
}
|
|
5
|
+
constructor(callable){
|
|
6
|
+
this.callable = callable;
|
|
7
|
+
}
|
|
8
|
+
async read() {
|
|
9
|
+
return this.callable();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export { CallableContentsReader };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=CallableContentsReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/assetDelivery/transformation/CallableContentsReader.js","sources":["../../../../src/features/assetDelivery/transformation/CallableContentsReader.ts"],"sourcesContent":["import { AssetContentsReader } from \"~/features/assetDelivery/abstractions.js\";\n\ninterface ContentsCallable {\n (): Promise<Buffer> | Buffer;\n}\n\nexport class CallableContentsReader implements AssetContentsReader.Interface {\n private readonly callable: ContentsCallable;\n\n public static create(callable: ContentsCallable) {\n return new CallableContentsReader(callable);\n }\n\n private constructor(callable: ContentsCallable) {\n this.callable = callable;\n }\n\n public async read(): Promise<Buffer> {\n return this.callable();\n }\n}\n"],"names":["CallableContentsReader","callable"],"mappings":"AAMO,MAAMA;IAGT,OAAc,OAAOC,QAA0B,EAAE;QAC7C,OAAO,IAAID,uBAAuBC;IACtC;IAEA,YAAoBA,QAA0B,CAAE;QAC5C,IAAI,CAAC,QAAQ,GAAGA;IACpB;IAEA,MAAa,OAAwB;QACjC,OAAO,IAAI,CAAC,QAAQ;IACxB;AACJ"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import type { Container } from "@webiny/di";
|
|
1
2
|
import type { Asset } from "../../../delivery/AssetDelivery/Asset.js";
|
|
2
3
|
import type { AssetRequest } from "../../../delivery/AssetDelivery/AssetRequest.js";
|
|
3
|
-
import {
|
|
4
|
+
import type { IAssetProcessor } from "../abstractions.js";
|
|
4
5
|
export declare class TransformationAssetProcessor implements IAssetProcessor {
|
|
5
|
-
private
|
|
6
|
-
constructor(
|
|
6
|
+
private readonly container;
|
|
7
|
+
constructor(container: Container);
|
|
7
8
|
process(assetRequest: AssetRequest, asset: Asset): Promise<Asset>;
|
|
8
9
|
}
|
|
9
|
-
export declare const TransformationAssetProcessorImpl: typeof TransformationAssetProcessor & {
|
|
10
|
-
__abstraction: import("@webiny/di").Abstraction<IAssetProcessor>;
|
|
11
|
-
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AssetType } from "../abstractions/AssetType.js";
|
|
2
2
|
class TransformationAssetProcessor {
|
|
3
|
-
constructor(
|
|
4
|
-
this.
|
|
3
|
+
constructor(container){
|
|
4
|
+
this.container = container;
|
|
5
5
|
}
|
|
6
6
|
async process(assetRequest, asset) {
|
|
7
7
|
const { original } = assetRequest.getOptions();
|
|
@@ -9,15 +9,17 @@ class TransformationAssetProcessor {
|
|
|
9
9
|
console.log("Skip transformations; original asset was requested.");
|
|
10
10
|
return asset;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
const assetTypes = this.container.resolveAll(AssetType);
|
|
13
|
+
const match = assetTypes.find((assetType)=>assetType.canHandle(asset));
|
|
14
|
+
if (!match) return asset;
|
|
15
|
+
try {
|
|
16
|
+
const handler = this.container.resolve(match.getHandlerAbstraction());
|
|
17
|
+
return handler.handle(assetRequest, asset);
|
|
18
|
+
} catch {
|
|
19
|
+
return asset;
|
|
20
|
+
}
|
|
13
21
|
}
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
implementation: TransformationAssetProcessor,
|
|
17
|
-
dependencies: [
|
|
18
|
-
AssetTransformationStrategy
|
|
19
|
-
]
|
|
20
|
-
});
|
|
21
|
-
export { TransformationAssetProcessor, TransformationAssetProcessorImpl };
|
|
23
|
+
export { TransformationAssetProcessor };
|
|
22
24
|
|
|
23
25
|
//# sourceMappingURL=TransformationAssetProcessor.js.map
|