@webiny/api-file-manager-s3 6.6.0-alpha.1 → 6.6.0-alpha.3

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.
@@ -1,4 +1,4 @@
1
- import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
1
+ import { TaskDefinition, TaskHandler } from "@webiny/api-core/features/task/TaskDefinition/index.js";
2
2
  export interface DeleteS3FolderInput {
3
3
  /**
4
4
  * Caller of the task (e.g., `fm-after-delete`).
@@ -17,14 +17,19 @@ export interface DeleteS3FolderInput {
17
17
  */
18
18
  continuationToken?: string;
19
19
  }
20
- declare class DeleteS3FolderTask implements TaskDefinition.Interface<DeleteS3FolderInput> {
20
+ declare class DeleteS3FolderTaskHandlerImpl implements TaskHandler.Interface<DeleteS3FolderInput> {
21
+ run({ input, controller }: TaskHandler.RunParams<DeleteS3FolderInput>): Promise<TaskDefinition.Result<DeleteS3FolderInput>>;
22
+ }
23
+ declare class DeleteS3FolderTask implements TaskDefinition.Interface {
21
24
  id: string;
22
25
  title: string;
23
26
  description: string;
24
27
  maxIterations: number;
25
28
  isPrivate: boolean;
26
29
  readonly selfCleanup: ("onAbort" | "onSuccess")[];
27
- run({ input, controller }: TaskDefinition.RunParams<DeleteS3FolderInput>): Promise<TaskDefinition.Result<DeleteS3FolderInput>>;
30
+ handler: typeof DeleteS3FolderTaskHandlerImpl & {
31
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskHandler<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
32
+ };
28
33
  }
29
34
  export declare const DeleteS3FolderTaskDefinition: typeof DeleteS3FolderTask & {
30
35
  __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskDefinition<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
@@ -1,6 +1,6 @@
1
1
  import { S3 } from "@webiny/aws-sdk/client-s3/index.js";
2
- import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
3
- class DeleteS3FolderTask {
2
+ import { TaskDefinition, TaskHandler } from "@webiny/api-core/features/task/TaskDefinition/index.js";
3
+ class DeleteS3FolderTaskHandlerImpl {
4
4
  async run({ input, controller }) {
5
5
  if (controller.runtime.isAborted()) return controller.response.aborted();
6
6
  if (!input.bucket) return controller.response.error({
@@ -29,6 +29,12 @@ class DeleteS3FolderTask {
29
29
  });
30
30
  return controller.response.done();
31
31
  }
32
+ }
33
+ const DeleteS3FolderTaskHandler = TaskHandler.createImplementation({
34
+ implementation: DeleteS3FolderTaskHandlerImpl,
35
+ dependencies: []
36
+ });
37
+ class DeleteS3FolderTask {
32
38
  constructor(){
33
39
  this.id = "fileManagerFolderDelete";
34
40
  this.title = "Delete folder and all of its contents from the bucket.";
@@ -39,6 +45,7 @@ class DeleteS3FolderTask {
39
45
  "onSuccess",
40
46
  "onAbort"
41
47
  ];
48
+ this.handler = DeleteS3FolderTaskHandler;
42
49
  }
43
50
  }
44
51
  const DeleteS3FolderTaskDefinition = TaskDefinition.createImplementation({
@@ -1 +1 @@
1
- {"version":3,"file":"features/DeleteFileFromBucket/DeleteS3FolderTask.js","sources":["../../../src/features/DeleteFileFromBucket/DeleteS3FolderTask.ts"],"sourcesContent":["import { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\n\nexport interface DeleteS3FolderInput {\n /**\n * Caller of the task (e.g., `fm-after-delete`).\n */\n caller: string;\n /**\n * Cache paths to invalidate.\n */\n bucket: string;\n /**\n * FM file directory key to delete.\n */\n folderKey: string;\n /**\n * Continuation token for pagination.\n */\n continuationToken?: string;\n}\n\nclass DeleteS3FolderTask implements TaskDefinition.Interface<DeleteS3FolderInput> {\n id = \"fileManagerFolderDelete\";\n title = \"Delete folder and all of its contents from the bucket.\";\n description = \"A task to delete all files from a given folder.\";\n maxIterations = 5;\n isPrivate = true;\n\n public readonly selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n public async run({\n input,\n controller\n }: TaskDefinition.RunParams<DeleteS3FolderInput>): Promise<\n TaskDefinition.Result<DeleteS3FolderInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n if (!input.bucket) {\n return controller.response.error({ message: `Bucket is not defined.` });\n }\n\n const s3 = new S3();\n\n // List objects in the folder with pagination support.\n const filesList = await s3.listObjectsV2({\n Bucket: input.bucket,\n Prefix: `${input.folderKey}/`,\n ContinuationToken: input.continuationToken\n });\n\n // Delete all files in the folder using batch delete.\n if (filesList.Contents && filesList.Contents.length > 0) {\n const objectsToDelete = filesList.Contents.filter(file => file.Key).map(file => ({\n Key: file.Key!\n }));\n\n if (objectsToDelete.length > 0) {\n await s3.deleteObjects({\n Bucket: input.bucket,\n Delete: {\n Objects: objectsToDelete\n }\n });\n }\n }\n\n // If there are more objects to delete, continue in the next iteration.\n if (filesList.IsTruncated && filesList.NextContinuationToken) {\n return controller.response.continue({\n ...input,\n continuationToken: filesList.NextContinuationToken\n });\n }\n\n return controller.response.done();\n }\n}\n\nexport const DeleteS3FolderTaskDefinition = TaskDefinition.createImplementation({\n implementation: DeleteS3FolderTask,\n dependencies: []\n});\n"],"names":["DeleteS3FolderTask","input","controller","s3","S3","filesList","objectsToDelete","file","DeleteS3FolderTaskDefinition","TaskDefinition"],"mappings":";;AAsBA,MAAMA;IASF,MAAa,IAAI,EACbC,KAAK,EACLC,UAAU,EACkC,EAE9C;QACE,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,IAAI,CAACD,MAAM,MAAM,EACb,OAAOC,WAAW,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAAyB;QAGzE,MAAMC,KAAK,IAAIC;QAGf,MAAMC,YAAY,MAAMF,GAAG,aAAa,CAAC;YACrC,QAAQF,MAAM,MAAM;YACpB,QAAQ,GAAGA,MAAM,SAAS,CAAC,CAAC,CAAC;YAC7B,mBAAmBA,MAAM,iBAAiB;QAC9C;QAGA,IAAII,UAAU,QAAQ,IAAIA,UAAU,QAAQ,CAAC,MAAM,GAAG,GAAG;YACrD,MAAMC,kBAAkBD,UAAU,QAAQ,CAAC,MAAM,CAACE,CAAAA,OAAQA,KAAK,GAAG,EAAE,GAAG,CAACA,CAAAA,OAAS;oBAC7E,KAAKA,KAAK,GAAG;gBACjB;YAEA,IAAID,gBAAgB,MAAM,GAAG,GACzB,MAAMH,GAAG,aAAa,CAAC;gBACnB,QAAQF,MAAM,MAAM;gBACpB,QAAQ;oBACJ,SAASK;gBACb;YACJ;QAER;QAGA,IAAID,UAAU,WAAW,IAAIA,UAAU,qBAAqB,EACxD,OAAOH,WAAW,QAAQ,CAAC,QAAQ,CAAC;YAChC,GAAGD,KAAK;YACR,mBAAmBI,UAAU,qBAAqB;QACtD;QAGJ,OAAOH,WAAW,QAAQ,CAAC,IAAI;IACnC;;aAxDA,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aAEI,WAAW,GAAG;YAAC;YAAsB;SAAmB;;AAmD5E;AAEO,MAAMM,+BAA+BC,eAAe,oBAAoB,CAAC;IAC5E,gBAAgBT;IAChB,cAAc,EAAE;AACpB"}
1
+ {"version":3,"file":"features/DeleteFileFromBucket/DeleteS3FolderTask.js","sources":["../../../src/features/DeleteFileFromBucket/DeleteS3FolderTask.ts"],"sourcesContent":["import { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport {\n TaskDefinition,\n TaskHandler\n} from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\n\nexport interface DeleteS3FolderInput {\n /**\n * Caller of the task (e.g., `fm-after-delete`).\n */\n caller: string;\n /**\n * Cache paths to invalidate.\n */\n bucket: string;\n /**\n * FM file directory key to delete.\n */\n folderKey: string;\n /**\n * Continuation token for pagination.\n */\n continuationToken?: string;\n}\n\nclass DeleteS3FolderTaskHandlerImpl implements TaskHandler.Interface<DeleteS3FolderInput> {\n public async run({\n input,\n controller\n }: TaskHandler.RunParams<DeleteS3FolderInput>): Promise<\n TaskDefinition.Result<DeleteS3FolderInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n if (!input.bucket) {\n return controller.response.error({ message: `Bucket is not defined.` });\n }\n\n const s3 = new S3();\n\n // List objects in the folder with pagination support.\n const filesList = await s3.listObjectsV2({\n Bucket: input.bucket,\n Prefix: `${input.folderKey}/`,\n ContinuationToken: input.continuationToken\n });\n\n // Delete all files in the folder using batch delete.\n if (filesList.Contents && filesList.Contents.length > 0) {\n const objectsToDelete = filesList.Contents.filter(file => file.Key).map(file => ({\n Key: file.Key!\n }));\n\n if (objectsToDelete.length > 0) {\n await s3.deleteObjects({\n Bucket: input.bucket,\n Delete: {\n Objects: objectsToDelete\n }\n });\n }\n }\n\n // If there are more objects to delete, continue in the next iteration.\n if (filesList.IsTruncated && filesList.NextContinuationToken) {\n return controller.response.continue({\n ...input,\n continuationToken: filesList.NextContinuationToken\n });\n }\n\n return controller.response.done();\n }\n}\n\nconst DeleteS3FolderTaskHandler = TaskHandler.createImplementation({\n implementation: DeleteS3FolderTaskHandlerImpl,\n dependencies: []\n});\n\nclass DeleteS3FolderTask implements TaskDefinition.Interface {\n id = \"fileManagerFolderDelete\";\n title = \"Delete folder and all of its contents from the bucket.\";\n description = \"A task to delete all files from a given folder.\";\n maxIterations = 5;\n isPrivate = true;\n public readonly selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n handler = DeleteS3FolderTaskHandler;\n}\n\nexport const DeleteS3FolderTaskDefinition = TaskDefinition.createImplementation({\n implementation: DeleteS3FolderTask,\n dependencies: []\n});\n"],"names":["DeleteS3FolderTaskHandlerImpl","input","controller","s3","S3","filesList","objectsToDelete","file","DeleteS3FolderTaskHandler","TaskHandler","DeleteS3FolderTask","DeleteS3FolderTaskDefinition","TaskDefinition"],"mappings":";;AAyBA,MAAMA;IACF,MAAa,IAAI,EACbC,KAAK,EACLC,UAAU,EAC+B,EAE3C;QACE,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,IAAI,CAACD,MAAM,MAAM,EACb,OAAOC,WAAW,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAAyB;QAGzE,MAAMC,KAAK,IAAIC;QAGf,MAAMC,YAAY,MAAMF,GAAG,aAAa,CAAC;YACrC,QAAQF,MAAM,MAAM;YACpB,QAAQ,GAAGA,MAAM,SAAS,CAAC,CAAC,CAAC;YAC7B,mBAAmBA,MAAM,iBAAiB;QAC9C;QAGA,IAAII,UAAU,QAAQ,IAAIA,UAAU,QAAQ,CAAC,MAAM,GAAG,GAAG;YACrD,MAAMC,kBAAkBD,UAAU,QAAQ,CAAC,MAAM,CAACE,CAAAA,OAAQA,KAAK,GAAG,EAAE,GAAG,CAACA,CAAAA,OAAS;oBAC7E,KAAKA,KAAK,GAAG;gBACjB;YAEA,IAAID,gBAAgB,MAAM,GAAG,GACzB,MAAMH,GAAG,aAAa,CAAC;gBACnB,QAAQF,MAAM,MAAM;gBACpB,QAAQ;oBACJ,SAASK;gBACb;YACJ;QAER;QAGA,IAAID,UAAU,WAAW,IAAIA,UAAU,qBAAqB,EACxD,OAAOH,WAAW,QAAQ,CAAC,QAAQ,CAAC;YAChC,GAAGD,KAAK;YACR,mBAAmBI,UAAU,qBAAqB;QACtD;QAGJ,OAAOH,WAAW,QAAQ,CAAC,IAAI;IACnC;AACJ;AAEA,MAAMM,4BAA4BC,YAAY,oBAAoB,CAAC;IAC/D,gBAAgBT;IAChB,cAAc,EAAE;AACpB;AAEA,MAAMU;;aACF,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aACI,WAAW,GAAG;YAAC;YAAsB;SAAmB;aAExE,OAAO,GAAGF;;AACd;AAEO,MAAMG,+BAA+BC,eAAe,oBAAoB,CAAC;IAC5E,gBAAgBF;IAChB,cAAc,EAAE;AACpB"}
@@ -1,12 +1,17 @@
1
- import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
1
+ import { TaskDefinition, TaskHandler } from "@webiny/api-core/features/task/TaskDefinition/index.js";
2
2
  import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
3
3
  import { UpdateFileUseCase } from "@webiny/api-file-manager/features/file/UpdateFile/index.js";
4
4
  export interface ExtractMetadataInput {
5
5
  fileId: string;
6
6
  }
7
- declare class ExtractMetadataTask implements TaskDefinition.Interface<ExtractMetadataInput> {
7
+ declare class ExtractMetadataTaskHandlerImpl implements TaskHandler.Interface<ExtractMetadataInput> {
8
8
  private keyValueStore;
9
9
  private updateFileUseCase;
10
+ constructor(keyValueStore: GlobalKeyValueStore.Interface, updateFileUseCase: UpdateFileUseCase.Interface);
11
+ run({ input, controller }: TaskHandler.RunParams<ExtractMetadataInput>): Promise<TaskDefinition.Result<ExtractMetadataInput>>;
12
+ private cleanValues;
13
+ }
14
+ declare class ExtractMetadataTask implements TaskDefinition.Interface {
10
15
  id: string;
11
16
  title: string;
12
17
  description: string;
@@ -14,9 +19,9 @@ declare class ExtractMetadataTask implements TaskDefinition.Interface<ExtractMet
14
19
  isPrivate: boolean;
15
20
  databaseLogs: boolean;
16
21
  selfCleanup: ("onAbort" | "onSuccess")[];
17
- constructor(keyValueStore: GlobalKeyValueStore.Interface, updateFileUseCase: UpdateFileUseCase.Interface);
18
- run({ input, controller }: TaskDefinition.RunParams<ExtractMetadataInput>): Promise<TaskDefinition.Result<ExtractMetadataInput>>;
19
- private cleanValues;
22
+ handler: typeof ExtractMetadataTaskHandlerImpl & {
23
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions.js").ITaskHandler<import("@webiny/api-core/features/task/TaskDefinition/abstractions.js").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions.js").ITaskOutput>>;
24
+ };
20
25
  }
21
26
  export declare const ExtractMetadataTaskDefinition: typeof ExtractMetadataTask & {
22
27
  __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions.js").ITaskDefinition<import("@webiny/api-core/features/task/TaskDefinition/abstractions.js").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions.js").ITaskOutput>>;
@@ -1,22 +1,12 @@
1
1
  import { S3 } from "@webiny/aws-sdk/client-s3/index.js";
2
- import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
2
+ import { TaskDefinition, TaskHandler } from "@webiny/api-core/features/task/TaskDefinition/index.js";
3
3
  import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
4
4
  import { UpdateFileUseCase } from "@webiny/api-file-manager/features/file/UpdateFile/index.js";
5
5
  import { MetadataReader } from "../WriteFileMetadata/MetadataReader.js";
6
- class ExtractMetadataTask {
6
+ class ExtractMetadataTaskHandlerImpl {
7
7
  constructor(keyValueStore, updateFileUseCase){
8
8
  this.keyValueStore = keyValueStore;
9
9
  this.updateFileUseCase = updateFileUseCase;
10
- this.id = "fileManagerExtractMetadata";
11
- this.title = "Extract image metadata (dimensions, EXIF, IPTC)";
12
- this.description = "A task to extract metadata from uploaded image files";
13
- this.maxIterations = 1;
14
- this.isPrivate = true;
15
- this.databaseLogs = false;
16
- this.selfCleanup = [
17
- "onSuccess",
18
- "onAbort"
19
- ];
20
10
  }
21
11
  async run({ input, controller }) {
22
12
  if (controller.runtime.isAborted()) return controller.response.aborted();
@@ -78,13 +68,32 @@ class ExtractMetadataTask {
78
68
  return cleaned;
79
69
  }
80
70
  }
81
- const ExtractMetadataTaskDefinition = TaskDefinition.createImplementation({
82
- implementation: ExtractMetadataTask,
71
+ const ExtractMetadataTaskHandler = TaskHandler.createImplementation({
72
+ implementation: ExtractMetadataTaskHandlerImpl,
83
73
  dependencies: [
84
74
  GlobalKeyValueStore,
85
75
  UpdateFileUseCase
86
76
  ]
87
77
  });
78
+ class ExtractMetadataTask {
79
+ constructor(){
80
+ this.id = "fileManagerExtractMetadata";
81
+ this.title = "Extract image metadata (dimensions, EXIF, IPTC)";
82
+ this.description = "A task to extract metadata from uploaded image files";
83
+ this.maxIterations = 1;
84
+ this.isPrivate = true;
85
+ this.databaseLogs = false;
86
+ this.selfCleanup = [
87
+ "onSuccess",
88
+ "onAbort"
89
+ ];
90
+ this.handler = ExtractMetadataTaskHandler;
91
+ }
92
+ }
93
+ const ExtractMetadataTaskDefinition = TaskDefinition.createImplementation({
94
+ implementation: ExtractMetadataTask,
95
+ dependencies: []
96
+ });
88
97
  export { ExtractMetadataTaskDefinition };
89
98
 
90
99
  //# sourceMappingURL=ExtractMetadataTask.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"features/ExtractMetadata/ExtractMetadataTask.js","sources":["../../../src/features/ExtractMetadata/ExtractMetadataTask.ts"],"sourcesContent":["import type { ExifTags } from \"exifreader\";\nimport { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { UpdateFileUseCase } from \"@webiny/api-file-manager/features/file/UpdateFile/index.js\";\nimport { MetadataReader } from \"../WriteFileMetadata/MetadataReader.js\";\n\nexport interface ExtractMetadataInput {\n fileId: string;\n}\n\nclass ExtractMetadataTask implements TaskDefinition.Interface<ExtractMetadataInput> {\n id = \"fileManagerExtractMetadata\";\n title = \"Extract image metadata (dimensions, EXIF, IPTC)\";\n description = \"A task to extract metadata from uploaded image files\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n\n selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n constructor(\n private keyValueStore: GlobalKeyValueStore.Interface,\n private updateFileUseCase: UpdateFileUseCase.Interface\n ) {}\n\n public async run({\n input,\n controller\n }: TaskDefinition.RunParams<ExtractMetadataInput>): Promise<\n TaskDefinition.Result<ExtractMetadataInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n // Load file metadata\n const metadataReader = new MetadataReader(this.keyValueStore);\n const fileMetadata = await metadataReader.read(input.fileId);\n\n if (!fileMetadata) {\n return controller.response.error({\n message: `File metadata not found for file ID: ${input.fileId}`\n });\n }\n\n // Check if it's an image file\n if (!fileMetadata.contentType.startsWith(\"image/\")) {\n return controller.response.done();\n }\n\n // Fetch the image from S3\n const s3 = new S3();\n const bucket = String(process.env.S3_BUCKET);\n\n try {\n const s3Object = await s3.getObject({\n Bucket: bucket,\n Key: fileMetadata.bucketKey\n });\n\n if (!s3Object.Body) {\n return controller.response.error({\n message: `S3 object body is empty for file: ${input.fileId}`\n });\n }\n\n const sharp = await import(/* webpackChunkName: \"sharp\" */ \"sharp\").then(\n m => m.default\n );\n\n const ExifReader = await import(/* webpackChunkName: \"exifreader\" */ \"exifreader\").then(\n m => m.default\n );\n\n // Convert S3 body to buffer\n const buffer = Buffer.from(await s3Object.Body.transformToByteArray());\n\n // Extract metadata using Sharp for image dimensions\n const sharpInstance = sharp(buffer);\n const sharpMetadata = await sharpInstance.metadata();\n\n // Use ExifReader to extract EXIF and IPTC data\n const tags = ExifReader.load(buffer, { expanded: true });\n\n // Build metadata object\n const metadata: Record<string, any> = {\n image: {\n width: sharpMetadata.width,\n height: sharpMetadata.height,\n format: sharpMetadata.format,\n orientation: sharpMetadata.orientation ?? 1\n }\n };\n\n // Extract EXIF data if available\n if (tags.exif) {\n metadata.exif = this.cleanValues(tags.exif);\n }\n\n // Extract IPTC data if available\n if (tags.iptc) {\n metadata.iptc = this.cleanValues(tags.iptc);\n }\n\n // Update the file with extracted metadata\n const updateResult = await this.updateFileUseCase.execute({\n id: input.fileId,\n metadata\n });\n\n if (updateResult.isFail()) {\n return controller.response.error({\n message: `Failed to update file with metadata: ${updateResult.error.message}`\n });\n }\n\n return controller.response.done();\n } catch (error) {\n return controller.response.error({\n message: `Failed to extract metadata: ${\n error instanceof Error ? error.message : String(error)\n }`\n });\n }\n }\n\n private cleanValues(tags: ExifTags) {\n const cleaned: Record<string, any> = {};\n\n for (const [key, tag] of Object.entries(tags)) {\n if (!tag || typeof tag !== \"object\") {\n continue;\n }\n\n // Use description if available, otherwise value\n if (tag.description !== undefined && tag.description !== null) {\n cleaned[key] = tag.description;\n } else if (Array.isArray(tag.value) && tag.value.length > 20) {\n // Skip large byte arrays\n } else if (tag.value !== undefined) {\n cleaned[key] = tag.value;\n }\n }\n\n return cleaned;\n }\n}\n\nexport const ExtractMetadataTaskDefinition = TaskDefinition.createImplementation({\n implementation: ExtractMetadataTask,\n dependencies: [GlobalKeyValueStore, UpdateFileUseCase]\n});\n"],"names":["ExtractMetadataTask","keyValueStore","updateFileUseCase","input","controller","metadataReader","MetadataReader","fileMetadata","s3","S3","bucket","String","process","s3Object","sharp","m","ExifReader","buffer","Buffer","sharpInstance","sharpMetadata","tags","metadata","updateResult","error","Error","cleaned","key","tag","Object","undefined","Array","ExtractMetadataTaskDefinition","TaskDefinition","GlobalKeyValueStore","UpdateFileUseCase"],"mappings":"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"}
1
+ {"version":3,"file":"features/ExtractMetadata/ExtractMetadataTask.js","sources":["../../../src/features/ExtractMetadata/ExtractMetadataTask.ts"],"sourcesContent":["import type { ExifTags } from \"exifreader\";\nimport { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport {\n TaskDefinition,\n TaskHandler\n} from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { UpdateFileUseCase } from \"@webiny/api-file-manager/features/file/UpdateFile/index.js\";\nimport { MetadataReader } from \"../WriteFileMetadata/MetadataReader.js\";\n\nexport interface ExtractMetadataInput {\n fileId: string;\n}\n\nclass ExtractMetadataTaskHandlerImpl implements TaskHandler.Interface<ExtractMetadataInput> {\n constructor(\n private keyValueStore: GlobalKeyValueStore.Interface,\n private updateFileUseCase: UpdateFileUseCase.Interface\n ) {}\n\n public async run({\n input,\n controller\n }: TaskHandler.RunParams<ExtractMetadataInput>): Promise<\n TaskDefinition.Result<ExtractMetadataInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n // Load file metadata\n const metadataReader = new MetadataReader(this.keyValueStore);\n const fileMetadata = await metadataReader.read(input.fileId);\n\n if (!fileMetadata) {\n return controller.response.error({\n message: `File metadata not found for file ID: ${input.fileId}`\n });\n }\n\n // Check if it's an image file\n if (!fileMetadata.contentType.startsWith(\"image/\")) {\n return controller.response.done();\n }\n\n // Fetch the image from S3\n const s3 = new S3();\n const bucket = String(process.env.S3_BUCKET);\n\n try {\n const s3Object = await s3.getObject({\n Bucket: bucket,\n Key: fileMetadata.bucketKey\n });\n\n if (!s3Object.Body) {\n return controller.response.error({\n message: `S3 object body is empty for file: ${input.fileId}`\n });\n }\n\n const sharp = await import(/* webpackChunkName: \"sharp\" */ \"sharp\").then(\n m => m.default\n );\n\n const ExifReader = await import(/* webpackChunkName: \"exifreader\" */ \"exifreader\").then(\n m => m.default\n );\n\n // Convert S3 body to buffer\n const buffer = Buffer.from(await s3Object.Body.transformToByteArray());\n\n // Extract metadata using Sharp for image dimensions\n const sharpInstance = sharp(buffer);\n const sharpMetadata = await sharpInstance.metadata();\n\n // Use ExifReader to extract EXIF and IPTC data\n const tags = ExifReader.load(buffer, { expanded: true });\n\n // Build metadata object\n const metadata: Record<string, any> = {\n image: {\n width: sharpMetadata.width,\n height: sharpMetadata.height,\n format: sharpMetadata.format,\n orientation: sharpMetadata.orientation ?? 1\n }\n };\n\n // Extract EXIF data if available\n if (tags.exif) {\n metadata.exif = this.cleanValues(tags.exif);\n }\n\n // Extract IPTC data if available\n if (tags.iptc) {\n metadata.iptc = this.cleanValues(tags.iptc);\n }\n\n // Update the file with extracted metadata\n const updateResult = await this.updateFileUseCase.execute({\n id: input.fileId,\n metadata\n });\n\n if (updateResult.isFail()) {\n return controller.response.error({\n message: `Failed to update file with metadata: ${updateResult.error.message}`\n });\n }\n\n return controller.response.done();\n } catch (error) {\n return controller.response.error({\n message: `Failed to extract metadata: ${\n error instanceof Error ? error.message : String(error)\n }`\n });\n }\n }\n\n private cleanValues(tags: ExifTags) {\n const cleaned: Record<string, any> = {};\n\n for (const [key, tag] of Object.entries(tags)) {\n if (!tag || typeof tag !== \"object\") {\n continue;\n }\n\n // Use description if available, otherwise value\n if (tag.description !== undefined && tag.description !== null) {\n cleaned[key] = tag.description;\n } else if (Array.isArray(tag.value) && tag.value.length > 20) {\n // Skip large byte arrays\n } else if (tag.value !== undefined) {\n cleaned[key] = tag.value;\n }\n }\n\n return cleaned;\n }\n}\n\nconst ExtractMetadataTaskHandler = TaskHandler.createImplementation({\n implementation: ExtractMetadataTaskHandlerImpl,\n dependencies: [GlobalKeyValueStore, UpdateFileUseCase]\n});\n\nclass ExtractMetadataTask implements TaskDefinition.Interface {\n id = \"fileManagerExtractMetadata\";\n title = \"Extract image metadata (dimensions, EXIF, IPTC)\";\n description = \"A task to extract metadata from uploaded image files\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n handler = ExtractMetadataTaskHandler;\n}\n\nexport const ExtractMetadataTaskDefinition = TaskDefinition.createImplementation({\n implementation: ExtractMetadataTask,\n dependencies: []\n});\n"],"names":["ExtractMetadataTaskHandlerImpl","keyValueStore","updateFileUseCase","input","controller","metadataReader","MetadataReader","fileMetadata","s3","S3","bucket","String","process","s3Object","sharp","m","ExifReader","buffer","Buffer","sharpInstance","sharpMetadata","tags","metadata","updateResult","error","Error","cleaned","key","tag","Object","undefined","Array","ExtractMetadataTaskHandler","TaskHandler","GlobalKeyValueStore","UpdateFileUseCase","ExtractMetadataTask","ExtractMetadataTaskDefinition","TaskDefinition"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
1
+ import { TaskDefinition, TaskHandler } from "@webiny/api-core/features/task/TaskDefinition/index.js";
2
2
  export interface InvalidateCacheInput {
3
3
  /**
4
4
  * Caller of the task (e.g., `fm-before-update`, `fm-after-delete`).
@@ -9,16 +9,21 @@ export interface InvalidateCacheInput {
9
9
  */
10
10
  paths: string[];
11
11
  }
12
- declare class InvalidateCloudfrontCacheTask implements TaskDefinition.Interface<InvalidateCacheInput> {
12
+ declare class InvalidateCloudfrontCacheTaskHandlerImpl implements TaskHandler.Interface<InvalidateCacheInput> {
13
+ private continueIfCode;
14
+ run({ input, controller }: TaskHandler.RunParams<InvalidateCacheInput>): Promise<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultAborted | import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultError | TaskDefinition.ResultContinue<InvalidateCacheInput> | TaskDefinition.ResultDone<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
15
+ private invalidateCache;
16
+ }
17
+ declare class InvalidateCloudfrontCacheTask implements TaskDefinition.Interface {
13
18
  id: string;
14
19
  title: string;
15
20
  description: string;
16
21
  maxIterations: number;
17
22
  isPrivate: boolean;
18
23
  selfCleanup: ("onAbort" | "onSuccess")[];
19
- private continueIfCode;
20
- run({ input, controller }: TaskDefinition.RunParams<InvalidateCacheInput>): Promise<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultAborted | import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultError | TaskDefinition.ResultContinue<InvalidateCacheInput> | TaskDefinition.ResultDone<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
21
- private invalidateCache;
24
+ handler: typeof InvalidateCloudfrontCacheTaskHandlerImpl & {
25
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskHandler<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
26
+ };
22
27
  }
23
28
  export declare const InvalidateCloudfrontCacheTaskDefinition: typeof InvalidateCloudfrontCacheTask & {
24
29
  __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskDefinition<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
@@ -1,10 +1,10 @@
1
1
  import { ServiceDiscovery } from "@webiny/api-core/features/serviceDiscovery/index.js";
2
2
  import { CloudFront } from "@webiny/aws-sdk/client-cloudfront/index.js";
3
- import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
3
+ import { TaskDefinition, TaskHandler } from "@webiny/api-core/features/task/TaskDefinition/index.js";
4
4
  import { executeWithRetry } from "@webiny/utils";
5
5
  class ReturnContinue extends Error {
6
6
  }
7
- class InvalidateCloudfrontCacheTask {
7
+ class InvalidateCloudfrontCacheTaskHandlerImpl {
8
8
  async run({ input, controller }) {
9
9
  if (controller.runtime.isAborted()) return controller.response.aborted();
10
10
  const manifest = await ServiceDiscovery.load();
@@ -50,6 +50,18 @@ class InvalidateCloudfrontCacheTask {
50
50
  }
51
51
  });
52
52
  }
53
+ constructor(){
54
+ this.continueIfCode = [
55
+ "TooManyInvalidationsInProgress",
56
+ "Throttling"
57
+ ];
58
+ }
59
+ }
60
+ const InvalidateCloudfrontCacheTaskHandler = TaskHandler.createImplementation({
61
+ implementation: InvalidateCloudfrontCacheTaskHandlerImpl,
62
+ dependencies: []
63
+ });
64
+ class InvalidateCloudfrontCacheTask {
53
65
  constructor(){
54
66
  this.id = "invalidateAssetCache";
55
67
  this.title = "Invalidate CloudFront Cache";
@@ -60,10 +72,7 @@ class InvalidateCloudfrontCacheTask {
60
72
  "onSuccess",
61
73
  "onAbort"
62
74
  ];
63
- this.continueIfCode = [
64
- "TooManyInvalidationsInProgress",
65
- "Throttling"
66
- ];
75
+ this.handler = InvalidateCloudfrontCacheTaskHandler;
67
76
  }
68
77
  }
69
78
  const InvalidateCloudfrontCacheTaskDefinition = TaskDefinition.createImplementation({
@@ -1 +1 @@
1
- {"version":3,"file":"features/FlushCache/InvalidateCacheTask.js","sources":["../../../src/features/FlushCache/InvalidateCacheTask.ts"],"sourcesContent":["import { ServiceDiscovery } from \"@webiny/api-core/features/serviceDiscovery/index.js\";\nimport { CloudFront } from \"@webiny/aws-sdk/client-cloudfront/index.js\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { executeWithRetry } from \"@webiny/utils\";\n\nclass ReturnContinue extends Error {}\n\nexport interface InvalidateCacheInput {\n /**\n * Caller of the task (e.g., `fm-before-update`, `fm-after-delete`).\n */\n caller: string;\n /**\n * Cache paths to invalidate.\n */\n paths: string[];\n}\n\nclass InvalidateCloudfrontCacheTask implements TaskDefinition.Interface<InvalidateCacheInput> {\n id = \"invalidateAssetCache\";\n title = \"Invalidate CloudFront Cache\";\n description = \"A task to invalidate Cloudfront cache by given paths.\";\n maxIterations = 100;\n isPrivate = true;\n\n selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n private continueIfCode = [\"TooManyInvalidationsInProgress\", \"Throttling\"];\n\n public async run({ input, controller }: TaskDefinition.RunParams<InvalidateCacheInput>) {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const manifest = await ServiceDiscovery.load();\n\n if (!manifest) {\n return controller.response.error({\n message: `Unable to invalidate cache due to a missing service manifest.`,\n code: \"MISSING_SERVICE_MANIFEST\",\n data: {\n manifest: \"api\"\n }\n });\n }\n\n const { distributionId } = manifest.api.cloudfront;\n\n const invalidateCache = () => {\n return this.invalidateCache(input.caller, distributionId as string, input.paths);\n };\n\n try {\n await executeWithRetry(invalidateCache, {\n minTimeout: 2000,\n // instead of forever: true\n retries: 10000,\n onFailedAttempt: ({ error }) => {\n if (this.continueIfCode.includes(error.name)) {\n throw new ReturnContinue();\n }\n\n if (error.message.includes(\"not authorized to perform\")) {\n throw error;\n }\n\n if (controller.runtime.isCloseToTimeout()) {\n throw new ReturnContinue();\n }\n }\n });\n } catch (error) {\n if (error instanceof ReturnContinue) {\n return controller.response.continue(input);\n }\n\n return controller.response.error({\n message: error.message,\n code: \"EXECUTE_WITH_RETRY_FAILED\",\n data: input.paths\n });\n }\n\n return controller.response.done();\n }\n\n private async invalidateCache(\n caller: string,\n distributionId: string,\n paths: string[]\n ): Promise<void> {\n const cloudfront = new CloudFront();\n await cloudfront.createInvalidation({\n DistributionId: distributionId,\n InvalidationBatch: {\n CallerReference: `${new Date().getTime()}-${caller}`,\n Paths: {\n Quantity: paths.length,\n Items: paths\n }\n }\n });\n }\n}\n\nexport const InvalidateCloudfrontCacheTaskDefinition = TaskDefinition.createImplementation({\n implementation: InvalidateCloudfrontCacheTask,\n dependencies: []\n});\n"],"names":["ReturnContinue","Error","InvalidateCloudfrontCacheTask","input","controller","manifest","ServiceDiscovery","distributionId","invalidateCache","executeWithRetry","error","caller","paths","cloudfront","CloudFront","Date","InvalidateCloudfrontCacheTaskDefinition","TaskDefinition"],"mappings":";;;;AAKA,MAAMA,uBAAuBC;AAAO;AAapC,MAAMC;IAWF,MAAa,IAAI,EAAEC,KAAK,EAAEC,UAAU,EAAkD,EAAE;QACpF,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,MAAMC,WAAW,MAAMC,iBAAiB,IAAI;QAE5C,IAAI,CAACD,UACD,OAAOD,WAAW,QAAQ,CAAC,KAAK,CAAC;YAC7B,SAAS;YACT,MAAM;YACN,MAAM;gBACF,UAAU;YACd;QACJ;QAGJ,MAAM,EAAEG,cAAc,EAAE,GAAGF,SAAS,GAAG,CAAC,UAAU;QAElD,MAAMG,kBAAkB,IACb,IAAI,CAAC,eAAe,CAACL,MAAM,MAAM,EAAEI,gBAA0BJ,MAAM,KAAK;QAGnF,IAAI;YACA,MAAMM,iBAAiBD,iBAAiB;gBACpC,YAAY;gBAEZ,SAAS;gBACT,iBAAiB,CAAC,EAAEE,KAAK,EAAE;oBACvB,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACA,MAAM,IAAI,GACvC,MAAM,IAAIV;oBAGd,IAAIU,MAAM,OAAO,CAAC,QAAQ,CAAC,8BACvB,MAAMA;oBAGV,IAAIN,WAAW,OAAO,CAAC,gBAAgB,IACnC,MAAM,IAAIJ;gBAElB;YACJ;QACJ,EAAE,OAAOU,OAAO;YACZ,IAAIA,iBAAiBV,gBACjB,OAAOI,WAAW,QAAQ,CAAC,QAAQ,CAACD;YAGxC,OAAOC,WAAW,QAAQ,CAAC,KAAK,CAAC;gBAC7B,SAASM,MAAM,OAAO;gBACtB,MAAM;gBACN,MAAMP,MAAM,KAAK;YACrB;QACJ;QAEA,OAAOC,WAAW,QAAQ,CAAC,IAAI;IACnC;IAEA,MAAc,gBACVO,MAAc,EACdJ,cAAsB,EACtBK,KAAe,EACF;QACb,MAAMC,aAAa,IAAIC;QACvB,MAAMD,WAAW,kBAAkB,CAAC;YAChC,gBAAgBN;YAChB,mBAAmB;gBACf,iBAAiB,GAAG,IAAIQ,OAAO,OAAO,GAAG,CAAC,EAAEJ,QAAQ;gBACpD,OAAO;oBACH,UAAUC,MAAM,MAAM;oBACtB,OAAOA;gBACX;YACJ;QACJ;IACJ;;aAnFA,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aAEZ,WAAW,GAAG;YAAC;YAAsB;SAAmB;aAEhD,cAAc,GAAG;YAAC;YAAkC;SAAa;;AA4E7E;AAEO,MAAMI,0CAA0CC,eAAe,oBAAoB,CAAC;IACvF,gBAAgBf;IAChB,cAAc,EAAE;AACpB"}
1
+ {"version":3,"file":"features/FlushCache/InvalidateCacheTask.js","sources":["../../../src/features/FlushCache/InvalidateCacheTask.ts"],"sourcesContent":["import { ServiceDiscovery } from \"@webiny/api-core/features/serviceDiscovery/index.js\";\nimport { CloudFront } from \"@webiny/aws-sdk/client-cloudfront/index.js\";\nimport {\n TaskDefinition,\n TaskHandler\n} from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { executeWithRetry } from \"@webiny/utils\";\n\nclass ReturnContinue extends Error {}\n\nexport interface InvalidateCacheInput {\n /**\n * Caller of the task (e.g., `fm-before-update`, `fm-after-delete`).\n */\n caller: string;\n /**\n * Cache paths to invalidate.\n */\n paths: string[];\n}\n\nclass InvalidateCloudfrontCacheTaskHandlerImpl implements TaskHandler.Interface<InvalidateCacheInput> {\n private continueIfCode = [\"TooManyInvalidationsInProgress\", \"Throttling\"];\n\n public async run({ input, controller }: TaskHandler.RunParams<InvalidateCacheInput>) {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const manifest = await ServiceDiscovery.load();\n\n if (!manifest) {\n return controller.response.error({\n message: `Unable to invalidate cache due to a missing service manifest.`,\n code: \"MISSING_SERVICE_MANIFEST\",\n data: {\n manifest: \"api\"\n }\n });\n }\n\n const { distributionId } = manifest.api.cloudfront;\n\n const invalidateCache = () => {\n return this.invalidateCache(input.caller, distributionId as string, input.paths);\n };\n\n try {\n await executeWithRetry(invalidateCache, {\n minTimeout: 2000,\n // instead of forever: true\n retries: 10000,\n onFailedAttempt: ({ error }) => {\n if (this.continueIfCode.includes(error.name)) {\n throw new ReturnContinue();\n }\n\n if (error.message.includes(\"not authorized to perform\")) {\n throw error;\n }\n\n if (controller.runtime.isCloseToTimeout()) {\n throw new ReturnContinue();\n }\n }\n });\n } catch (error) {\n if (error instanceof ReturnContinue) {\n return controller.response.continue(input);\n }\n\n return controller.response.error({\n message: error.message,\n code: \"EXECUTE_WITH_RETRY_FAILED\",\n data: input.paths\n });\n }\n\n return controller.response.done();\n }\n\n private async invalidateCache(\n caller: string,\n distributionId: string,\n paths: string[]\n ): Promise<void> {\n const cloudfront = new CloudFront();\n await cloudfront.createInvalidation({\n DistributionId: distributionId,\n InvalidationBatch: {\n CallerReference: `${new Date().getTime()}-${caller}`,\n Paths: {\n Quantity: paths.length,\n Items: paths\n }\n }\n });\n }\n}\n\nconst InvalidateCloudfrontCacheTaskHandler = TaskHandler.createImplementation({\n implementation: InvalidateCloudfrontCacheTaskHandlerImpl,\n dependencies: []\n});\n\nclass InvalidateCloudfrontCacheTask implements TaskDefinition.Interface {\n id = \"invalidateAssetCache\";\n title = \"Invalidate CloudFront Cache\";\n description = \"A task to invalidate Cloudfront cache by given paths.\";\n maxIterations = 100;\n isPrivate = true;\n selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n handler = InvalidateCloudfrontCacheTaskHandler;\n}\n\nexport const InvalidateCloudfrontCacheTaskDefinition = TaskDefinition.createImplementation({\n implementation: InvalidateCloudfrontCacheTask,\n dependencies: []\n});\n"],"names":["ReturnContinue","Error","InvalidateCloudfrontCacheTaskHandlerImpl","input","controller","manifest","ServiceDiscovery","distributionId","invalidateCache","executeWithRetry","error","caller","paths","cloudfront","CloudFront","Date","InvalidateCloudfrontCacheTaskHandler","TaskHandler","InvalidateCloudfrontCacheTask","InvalidateCloudfrontCacheTaskDefinition","TaskDefinition"],"mappings":";;;;AAQA,MAAMA,uBAAuBC;AAAO;AAapC,MAAMC;IAGF,MAAa,IAAI,EAAEC,KAAK,EAAEC,UAAU,EAA+C,EAAE;QACjF,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,MAAMC,WAAW,MAAMC,iBAAiB,IAAI;QAE5C,IAAI,CAACD,UACD,OAAOD,WAAW,QAAQ,CAAC,KAAK,CAAC;YAC7B,SAAS;YACT,MAAM;YACN,MAAM;gBACF,UAAU;YACd;QACJ;QAGJ,MAAM,EAAEG,cAAc,EAAE,GAAGF,SAAS,GAAG,CAAC,UAAU;QAElD,MAAMG,kBAAkB,IACb,IAAI,CAAC,eAAe,CAACL,MAAM,MAAM,EAAEI,gBAA0BJ,MAAM,KAAK;QAGnF,IAAI;YACA,MAAMM,iBAAiBD,iBAAiB;gBACpC,YAAY;gBAEZ,SAAS;gBACT,iBAAiB,CAAC,EAAEE,KAAK,EAAE;oBACvB,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACA,MAAM,IAAI,GACvC,MAAM,IAAIV;oBAGd,IAAIU,MAAM,OAAO,CAAC,QAAQ,CAAC,8BACvB,MAAMA;oBAGV,IAAIN,WAAW,OAAO,CAAC,gBAAgB,IACnC,MAAM,IAAIJ;gBAElB;YACJ;QACJ,EAAE,OAAOU,OAAO;YACZ,IAAIA,iBAAiBV,gBACjB,OAAOI,WAAW,QAAQ,CAAC,QAAQ,CAACD;YAGxC,OAAOC,WAAW,QAAQ,CAAC,KAAK,CAAC;gBAC7B,SAASM,MAAM,OAAO;gBACtB,MAAM;gBACN,MAAMP,MAAM,KAAK;YACrB;QACJ;QAEA,OAAOC,WAAW,QAAQ,CAAC,IAAI;IACnC;IAEA,MAAc,gBACVO,MAAc,EACdJ,cAAsB,EACtBK,KAAe,EACF;QACb,MAAMC,aAAa,IAAIC;QACvB,MAAMD,WAAW,kBAAkB,CAAC;YAChC,gBAAgBN;YAChB,mBAAmB;gBACf,iBAAiB,GAAG,IAAIQ,OAAO,OAAO,GAAG,CAAC,EAAEJ,QAAQ;gBACpD,OAAO;oBACH,UAAUC,MAAM,MAAM;oBACtB,OAAOA;gBACX;YACJ;QACJ;IACJ;;aA3EQ,cAAc,GAAG;YAAC;YAAkC;SAAa;;AA4E7E;AAEA,MAAMI,uCAAuCC,YAAY,oBAAoB,CAAC;IAC1E,gBAAgBf;IAChB,cAAc,EAAE;AACpB;AAEA,MAAMgB;;aACF,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aACZ,WAAW,GAAG;YAAC;YAAsB;SAAmB;aAExD,OAAO,GAAGF;;AACd;AAEO,MAAMG,0CAA0CC,eAAe,oBAAoB,CAAC;IACvF,gBAAgBF;IAChB,cAAc,EAAE;AACpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/api-file-manager-s3",
3
- "version": "6.6.0-alpha.1",
3
+ "version": "6.6.0-alpha.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -14,21 +14,21 @@
14
14
  "author": "Webiny Ltd",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@webiny/api": "6.6.0-alpha.1",
18
- "@webiny/api-core": "6.6.0-alpha.1",
19
- "@webiny/api-file-manager": "6.6.0-alpha.1",
20
- "@webiny/api-graphql": "6.6.0-alpha.1",
21
- "@webiny/api-websockets": "6.6.0-alpha.1",
22
- "@webiny/aws-sdk": "6.6.0-alpha.1",
23
- "@webiny/background-tasks": "6.6.0-alpha.1",
17
+ "@webiny/api": "6.6.0-alpha.3",
18
+ "@webiny/api-core": "6.6.0-alpha.3",
19
+ "@webiny/api-file-manager": "6.6.0-alpha.3",
20
+ "@webiny/api-graphql": "6.6.0-alpha.3",
21
+ "@webiny/api-websockets": "6.6.0-alpha.3",
22
+ "@webiny/aws-sdk": "6.6.0-alpha.3",
23
+ "@webiny/background-tasks": "6.6.0-alpha.3",
24
24
  "@webiny/di": "1.1.0",
25
- "@webiny/event-handler-aws": "6.6.0-alpha.1",
26
- "@webiny/event-handler-core": "6.6.0-alpha.1",
27
- "@webiny/feature": "6.6.0-alpha.1",
28
- "@webiny/handler": "6.6.0-alpha.1",
29
- "@webiny/plugins": "6.6.0-alpha.1",
30
- "@webiny/utils": "6.6.0-alpha.1",
31
- "@webiny/validation": "6.6.0-alpha.1",
25
+ "@webiny/event-handler-aws": "6.6.0-alpha.3",
26
+ "@webiny/event-handler-core": "6.6.0-alpha.3",
27
+ "@webiny/feature": "6.6.0-alpha.3",
28
+ "@webiny/handler": "6.6.0-alpha.3",
29
+ "@webiny/plugins": "6.6.0-alpha.3",
30
+ "@webiny/utils": "6.6.0-alpha.3",
31
+ "@webiny/validation": "6.6.0-alpha.3",
32
32
  "exifreader": "4.43.0",
33
33
  "mime": "4.1.0",
34
34
  "object-hash": "3.0.0",
@@ -38,7 +38,7 @@
38
38
  "sharp": "0.35.3"
39
39
  },
40
40
  "devDependencies": {
41
- "@webiny/build-tools": "6.6.0-alpha.1",
41
+ "@webiny/build-tools": "6.6.0-alpha.3",
42
42
  "rimraf": "6.1.3",
43
43
  "typescript": "7.0.2",
44
44
  "vitest": "4.1.11"