@maas/payload-plugin-media-cloud 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/adapter/{handleDelete.d.ts → handleDelete.d.mts} +2 -2
  2. package/dist/adapter/{handleDelete.js → handleDelete.mjs} +3 -3
  3. package/dist/adapter/handleDelete.mjs.map +1 -0
  4. package/dist/adapter/{handleUpload.d.ts → handleUpload.d.mts} +1 -1
  5. package/dist/adapter/{handleUpload.js → handleUpload.mjs} +1 -1
  6. package/dist/adapter/handleUpload.mjs.map +1 -0
  7. package/dist/adapter/{staticHandler.d.ts → staticHandler.d.mts} +2 -2
  8. package/dist/adapter/{staticHandler.js → staticHandler.mjs} +1 -1
  9. package/dist/adapter/staticHandler.mjs.map +1 -0
  10. package/dist/adapter/{storageAdapter.d.ts → storageAdapter.d.mts} +3 -3
  11. package/dist/adapter/{storageAdapter.js → storageAdapter.mjs} +4 -4
  12. package/dist/adapter/storageAdapter.mjs.map +1 -0
  13. package/dist/collections/{mediaCollection.d.ts → mediaCollection.d.mts} +2 -2
  14. package/dist/collections/{mediaCollection.js → mediaCollection.mjs} +24 -5
  15. package/dist/collections/mediaCollection.mjs.map +1 -0
  16. package/dist/components/index.d.mts +7 -0
  17. package/dist/components/index.mjs +5 -0
  18. package/dist/components/mux-preview/index.d.mts +2 -0
  19. package/dist/components/mux-preview/index.mjs +3 -0
  20. package/dist/components/mux-preview/{mux-preview.d.ts → mux-preview.d.mts} +1 -1
  21. package/dist/components/mux-preview/{mux-preview.js → mux-preview.mjs} +2 -11
  22. package/dist/components/mux-preview/mux-preview.mjs.map +1 -0
  23. package/dist/components/upload-handler/index.d.mts +2 -0
  24. package/dist/components/upload-handler/index.mjs +3 -0
  25. package/dist/components/upload-handler/{upload-handler.d.ts → upload-handler.d.mts} +1 -1
  26. package/dist/components/upload-handler/{upload-handler.js → upload-handler.mjs} +17 -19
  27. package/dist/components/upload-handler/upload-handler.mjs.map +1 -0
  28. package/dist/components/upload-manager/index.d.mts +2 -0
  29. package/dist/components/upload-manager/index.mjs +3 -0
  30. package/dist/components/upload-manager/{upload-manager.d.ts → upload-manager.d.mts} +1 -1
  31. package/dist/components/upload-manager/upload-manager.mjs +274 -0
  32. package/dist/components/upload-manager/upload-manager.mjs.map +1 -0
  33. package/dist/endpoints/{muxAssetHandler.d.ts → muxAssetHandler.d.mts} +1 -1
  34. package/dist/endpoints/{muxAssetHandler.js → muxAssetHandler.mjs} +3 -4
  35. package/dist/endpoints/muxAssetHandler.mjs.map +1 -0
  36. package/dist/endpoints/{muxCreateUploadHandler.d.ts → muxCreateUploadHandler.d.mts} +2 -2
  37. package/dist/endpoints/{muxCreateUploadHandler.js → muxCreateUploadHandler.mjs} +4 -5
  38. package/dist/endpoints/muxCreateUploadHandler.mjs.map +1 -0
  39. package/dist/endpoints/{muxWebhookHandler.d.ts → muxWebhookHandler.d.mts} +1 -1
  40. package/dist/endpoints/{muxWebhookHandler.js → muxWebhookHandler.mjs} +1 -1
  41. package/dist/endpoints/muxWebhookHandler.mjs.map +1 -0
  42. package/dist/endpoints/tusPostProcessorHandler.d.mts +11 -0
  43. package/dist/endpoints/tusPostProcessorHandler.mjs +48 -0
  44. package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -0
  45. package/dist/error-handler/dist/{index.js → index.mjs} +4 -8
  46. package/dist/error-handler/dist/index.mjs.map +1 -0
  47. package/dist/hooks/{useEmitter.d.ts → useEmitter.d.mts} +1 -1
  48. package/dist/hooks/{useEmitter.js → useEmitter.mjs} +1 -1
  49. package/dist/hooks/useEmitter.mjs.map +1 -0
  50. package/dist/hooks/useErrorHandler.d.mts +177 -0
  51. package/dist/hooks/{useErrorHandler.js → useErrorHandler.mjs} +3 -3
  52. package/dist/hooks/useErrorHandler.mjs.map +1 -0
  53. package/dist/index.d.mts +3 -0
  54. package/dist/index.mjs +3 -0
  55. package/dist/{plugin.d.ts → plugin.d.mts} +2 -2
  56. package/dist/{plugin.js → plugin.mjs} +23 -14
  57. package/dist/plugin.mjs.map +1 -0
  58. package/dist/tus/stores/s3/{expiration-manager.d.ts → expiration-manager.d.mts} +1 -1
  59. package/dist/tus/stores/s3/{expiration-manager.js → expiration-manager.mjs} +1 -1
  60. package/dist/tus/stores/s3/expiration-manager.mjs.map +1 -0
  61. package/dist/tus/stores/s3/{file-operations.d.ts → file-operations.d.mts} +1 -1
  62. package/dist/tus/stores/s3/{file-operations.js → file-operations.mjs} +4 -5
  63. package/dist/tus/stores/s3/file-operations.mjs.map +1 -0
  64. package/dist/tus/stores/s3/{metadata-manager.d.ts → metadata-manager.d.mts} +2 -2
  65. package/dist/tus/stores/s3/{metadata-manager.js → metadata-manager.mjs} +3 -3
  66. package/dist/tus/stores/s3/metadata-manager.mjs.map +1 -0
  67. package/dist/tus/stores/s3/{parts-manager.d.ts → parts-manager.d.mts} +5 -5
  68. package/dist/tus/stores/s3/{parts-manager.js → parts-manager.mjs} +11 -15
  69. package/dist/tus/stores/s3/parts-manager.mjs.map +1 -0
  70. package/dist/tus/stores/s3/{s3-store.d.ts → s3-store.d.mts} +7 -7
  71. package/dist/tus/stores/s3/{s3-store.js → s3-store.mjs} +18 -29
  72. package/dist/tus/stores/s3/s3-store.mjs.map +1 -0
  73. package/dist/tus/stores/s3/{semaphore.d.ts → semaphore.d.mts} +1 -1
  74. package/dist/tus/stores/s3/{semaphore.js → semaphore.mjs} +2 -3
  75. package/dist/tus/stores/s3/semaphore.mjs.map +1 -0
  76. package/dist/types/errors.d.mts +142 -0
  77. package/dist/types/{errors.js → errors.mjs} +13 -1
  78. package/dist/types/errors.mjs.map +1 -0
  79. package/dist/types/{index.d.ts → index.d.mts} +1 -1
  80. package/dist/types/index.mjs +1 -0
  81. package/dist/utils/{file.d.ts → file.d.mts} +1 -1
  82. package/dist/utils/{file.js → file.mjs} +7 -11
  83. package/dist/utils/file.mjs.map +1 -0
  84. package/package.json +20 -16
  85. package/dist/adapter/handleDelete.js.map +0 -1
  86. package/dist/adapter/handleUpload.js.map +0 -1
  87. package/dist/adapter/staticHandler.js.map +0 -1
  88. package/dist/adapter/storageAdapter.js.map +0 -1
  89. package/dist/collections/mediaCollection.js.map +0 -1
  90. package/dist/components/index.d.ts +0 -4
  91. package/dist/components/index.js +0 -5
  92. package/dist/components/mux-preview/index.d.ts +0 -2
  93. package/dist/components/mux-preview/index.js +0 -3
  94. package/dist/components/mux-preview/mux-preview.js.map +0 -1
  95. package/dist/components/upload-handler/index.d.ts +0 -2
  96. package/dist/components/upload-handler/index.js +0 -3
  97. package/dist/components/upload-handler/upload-handler.js.map +0 -1
  98. package/dist/components/upload-manager/index.d.ts +0 -2
  99. package/dist/components/upload-manager/index.js +0 -3
  100. package/dist/components/upload-manager/upload-manager.js +0 -315
  101. package/dist/components/upload-manager/upload-manager.js.map +0 -1
  102. package/dist/endpoints/muxAssetHandler.js.map +0 -1
  103. package/dist/endpoints/muxCreateUploadHandler.js.map +0 -1
  104. package/dist/endpoints/muxWebhookHandler.js.map +0 -1
  105. package/dist/error-handler/dist/index.js.map +0 -1
  106. package/dist/hooks/useEmitter.js.map +0 -1
  107. package/dist/hooks/useErrorHandler.d.ts +0 -12
  108. package/dist/hooks/useErrorHandler.js.map +0 -1
  109. package/dist/index.d.ts +0 -3
  110. package/dist/index.js +0 -3
  111. package/dist/plugin.js.map +0 -1
  112. package/dist/tus/stores/s3/expiration-manager.js.map +0 -1
  113. package/dist/tus/stores/s3/file-operations.js.map +0 -1
  114. package/dist/tus/stores/s3/metadata-manager.js.map +0 -1
  115. package/dist/tus/stores/s3/parts-manager.js.map +0 -1
  116. package/dist/tus/stores/s3/s3-store.js.map +0 -1
  117. package/dist/tus/stores/s3/semaphore.js.map +0 -1
  118. package/dist/types/errors.d.ts +0 -8
  119. package/dist/types/errors.js.map +0 -1
  120. package/dist/types/index.js +0 -0
  121. package/dist/utils/file.js.map +0 -1
@@ -1,10 +1,10 @@
1
- import { MediaCloudLogs } from "../../../types/errors.js";
2
- import { useErrorHandler } from "../../../hooks/useErrorHandler.js";
3
- import { Semaphore } from "./semaphore.js";
4
- import { S3ExpirationManager } from "./expiration-manager.js";
5
- import { S3FileOperations } from "./file-operations.js";
6
- import { S3MetadataManager } from "./metadata-manager.js";
7
- import { S3PartsManager } from "./parts-manager.js";
1
+ import { MediaCloudLogs } from "../../../types/errors.mjs";
2
+ import { useErrorHandler } from "../../../hooks/useErrorHandler.mjs";
3
+ import { Semaphore } from "./semaphore.mjs";
4
+ import { S3ExpirationManager } from "./expiration-manager.mjs";
5
+ import { S3FileOperations } from "./file-operations.mjs";
6
+ import { S3MetadataManager } from "./metadata-manager.mjs";
7
+ import { S3PartsManager } from "./parts-manager.mjs";
8
8
  import stream from "node:stream";
9
9
  import { NoSuchKey, NotFound, S3 } from "@aws-sdk/client-s3";
10
10
  import { DataStore, ERRORS, MemoryKvStore, TUS_RESUMABLE, Upload } from "@tus/utils";
@@ -12,26 +12,16 @@ import { DataStore, ERRORS, MemoryKvStore, TUS_RESUMABLE, Upload } from "@tus/ut
12
12
  //#region src/tus/stores/s3/s3-store.ts
13
13
  const { log } = useErrorHandler();
14
14
  var S3Store = class extends DataStore {
15
- client;
16
- bucket;
17
- partSize = 8 * 1024 * 1024;
18
- minPartSize = 5 * 1024 * 1024;
19
- maxMultipartParts = 1e4;
20
- maxUploadSize = 5497558138880;
21
- useTags = false;
22
- expirationPeriodInMilliseconds = 0;
23
- acl;
24
- cache;
25
- partUploadSemaphore;
26
- metadataManager;
27
- fileOperations;
28
- partsManager;
29
- expirationManager;
30
- customEndpoint;
31
15
  constructor(options) {
32
16
  super();
17
+ this.partSize = 8 * 1024 * 1024;
18
+ this.minPartSize = 5 * 1024 * 1024;
19
+ this.maxMultipartParts = 1e4;
20
+ this.maxUploadSize = 5497558138880;
21
+ this.useTags = false;
22
+ this.expirationPeriodInMilliseconds = 0;
33
23
  const { maxMultipartParts, minPartSize, partSize, s3ClientConfig, maxConcurrentPartUploads, useTags, expirationPeriodInMilliseconds, cache } = options;
34
- const { acl, bucket,...restS3ClientConfig } = s3ClientConfig;
24
+ const { acl, bucket, ...restS3ClientConfig } = s3ClientConfig;
35
25
  this.extensions = [
36
26
  "creation",
37
27
  "creation-with-upload",
@@ -86,7 +76,7 @@ var S3Store = class extends DataStore {
86
76
  * @returns The tag string or undefined if tags shouldn't be used
87
77
  */
88
78
  generateCompleteTag(value) {
89
- if (!this.shouldUseExpirationTags()) return void 0;
79
+ if (!this.shouldUseExpirationTags()) return;
90
80
  return `Tus-Completed=${value}`;
91
81
  }
92
82
  /**
@@ -161,13 +151,12 @@ var S3Store = class extends DataStore {
161
151
  })());
162
152
  }
163
153
  const partNumber = this.fileOperations.calculatePartNumber({ parts: await this.partsManager.retrieveParts({ id }) });
164
- const bytesUploaded = await this.partsManager.uploadParts({
154
+ const newOffset = requestedOffset + await this.partsManager.uploadParts({
165
155
  metadata,
166
156
  readStream: finalReadable,
167
157
  currentPartNumber: partNumber,
168
158
  offset
169
- });
170
- const newOffset = requestedOffset + bytesUploaded - (offsetDiff > 0 ? offsetDiff : 0);
159
+ }) - (offsetDiff > 0 ? offsetDiff : 0);
171
160
  if (metadata.file.size === newOffset) try {
172
161
  const parts = await this.partsManager.retrieveParts({ id });
173
162
  await this.partsManager.finishMultipartUpload({
@@ -341,4 +330,4 @@ var S3Store = class extends DataStore {
341
330
 
342
331
  //#endregion
343
332
  export { S3Store };
344
- //# sourceMappingURL=s3-store.js.map
333
+ //# sourceMappingURL=s3-store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-store.mjs","names":["request: AWS.CreateMultipartUploadCommandInput","metadata: TusUploadMetadata","offset: number","lastError: Error | null","region: string"],"sources":["../../../../src/tus/stores/s3/s3-store.ts"],"sourcesContent":["import { NoSuchKey, NotFound, S3 } from '@aws-sdk/client-s3'\nimport {\n DataStore,\n ERRORS,\n MemoryKvStore,\n TUS_RESUMABLE,\n Upload,\n type KvStore,\n} from '@tus/utils'\nimport stream, { type Readable } from 'node:stream'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { Semaphore } from './semaphore'\nimport { S3ExpirationManager } from './expiration-manager'\nimport { S3FileOperations } from './file-operations'\nimport { S3MetadataManager } from './metadata-manager'\nimport { S3PartsManager } from './parts-manager'\n\nimport type AWS from '@aws-sdk/client-s3'\nimport type { AWSError, S3StoreConfig, TusUploadMetadata } from '../../../types'\nimport { MediaCloudLogs } from '../../../types/errors'\n\nconst { log } = useErrorHandler()\n\nexport class S3Store extends DataStore {\n public client: S3\n public bucket: string\n public partSize = 8 * 1024 * 1024 // 8MB preferred part size\n public minPartSize = 5 * 1024 * 1024 // 5MB minimum part size\n public maxMultipartParts = 10_000\n public maxUploadSize = 5_497_558_138_880 as const // 5TiB\n public useTags = false\n public expirationPeriodInMilliseconds = 0\n protected acl?: string\n\n protected cache: KvStore<TusUploadMetadata>\n protected partUploadSemaphore: Semaphore\n protected metadataManager: S3MetadataManager\n protected fileOperations: S3FileOperations\n protected partsManager: S3PartsManager\n protected expirationManager: S3ExpirationManager\n protected customEndpoint: string\n\n constructor(options: S3StoreConfig) {\n super()\n const {\n maxMultipartParts,\n minPartSize,\n partSize,\n s3ClientConfig,\n maxConcurrentPartUploads,\n useTags,\n expirationPeriodInMilliseconds,\n cache,\n } = options\n const { acl, bucket, ...restS3ClientConfig } = s3ClientConfig\n\n this.extensions = [\n 'creation',\n 'creation-with-upload',\n 'creation-defer-length',\n 'termination',\n 'expiration',\n ]\n\n this.bucket = bucket\n this.acl = acl\n this.client = new S3(restS3ClientConfig)\n this.customEndpoint = String(restS3ClientConfig.endpoint)\n\n this.partSize = partSize ?? this.partSize\n this.minPartSize = minPartSize ?? this.minPartSize\n this.maxMultipartParts = maxMultipartParts ?? this.maxMultipartParts\n\n this.useTags = useTags ?? this.useTags\n this.expirationPeriodInMilliseconds =\n expirationPeriodInMilliseconds ?? this.expirationPeriodInMilliseconds\n this.cache = cache ?? new MemoryKvStore<TusUploadMetadata>()\n this.partUploadSemaphore = new Semaphore(maxConcurrentPartUploads ?? 60)\n\n // Initialize component managers\n this.metadataManager = new S3MetadataManager(\n this.client,\n this.bucket,\n this.cache,\n this.shouldUseExpirationTags.bind(this),\n this.generateCompleteTag.bind(this)\n )\n\n this.fileOperations = new S3FileOperations(\n this.maxMultipartParts,\n this.maxUploadSize,\n this.minPartSize,\n this.partSize\n )\n\n this.partsManager = new S3PartsManager(\n this.client,\n this.bucket,\n this.minPartSize,\n this.partUploadSemaphore,\n this.metadataManager,\n this.fileOperations,\n this.generateCompleteTag.bind(this)\n )\n\n this.expirationManager = new S3ExpirationManager(\n this.client,\n this.bucket,\n this.expirationPeriodInMilliseconds,\n this.generateInfoKey.bind(this),\n this.generatePartKey.bind(this)\n )\n\n // Cleanup expired uploads when the store is initialized\n this.deleteExpired()\n }\n\n /**\n * Generate the key name for the info file\n * @param id - The upload ID\n * @returns The info file key\n */\n protected generateInfoKey(id: string): string {\n return `${id}.info`\n }\n\n /**\n * Generate the key name for a part file\n * @param id - The upload ID\n * @param isIncompletePart - Whether this is an incomplete part (default: false)\n * @returns The part file key\n */\n protected generatePartKey(\n id: string,\n isIncompletePart: boolean = false\n ): string {\n return isIncompletePart ? `${id}.part` : id\n }\n\n /**\n * Helper method to check if expiration tags should be used\n * @returns True if expiration tags should be used\n */\n protected shouldUseExpirationTags(): boolean {\n return this.expirationPeriodInMilliseconds !== 0 && this.useTags\n }\n\n /**\n * Generates a tag for marking complete/incomplete uploads\n * @param value - Either 'false' or 'true' to mark completion status\n * @returns The tag string or undefined if tags shouldn't be used\n */\n protected generateCompleteTag(value: 'false' | 'true'): string | undefined {\n if (!this.shouldUseExpirationTags()) {\n return undefined\n }\n return `Tus-Completed=${value}`\n }\n\n /**\n * Creates a multipart upload on S3 attaching any metadata to it.\n * Also, a `${file_id}.info` file is created which holds some information\n * about the upload itself like: `upload-id`, `upload-length`, etc.\n * @param upload - The upload object to create\n * @returns Promise that resolves to the created upload\n */\n public async create(upload: Upload): Promise<Upload> {\n log(MediaCloudLogs.S3_STORE_MULTIPART_INIT)\n const request: AWS.CreateMultipartUploadCommandInput = {\n Bucket: this.bucket,\n Key: upload.id,\n Metadata: { 'tus-version': TUS_RESUMABLE },\n }\n\n if (upload.metadata?.contentType) {\n request.ContentType = upload.metadata.contentType as string\n }\n\n if (upload.metadata?.cacheControl) {\n request.CacheControl = upload.metadata.cacheControl as string\n }\n\n if (this.acl) {\n request.ACL = this.acl as AWS.ObjectCannedACL\n }\n\n upload.creation_date = new Date().toISOString()\n\n const response = await this.client.createMultipartUpload(request)\n upload.storage = {\n type: 's3',\n bucket: this.bucket,\n path: response.Key as string,\n }\n await this.metadataManager.saveMetadata({\n upload,\n uploadId: response.UploadId as string,\n })\n log(MediaCloudLogs.S3_STORE_MULTIPART_CREATED)\n\n return upload\n }\n\n /**\n * Declares the length of the upload\n * @param file_id - The file ID\n * @param upload_length - The length of the upload\n * @returns Promise that resolves when length is declared\n */\n public async declareUploadLength(\n file_id: string,\n upload_length: number\n ): Promise<void> {\n const { file, 'upload-id': uploadId } =\n await this.metadataManager.getMetadata({ id: file_id })\n if (!file) {\n throw ERRORS.FILE_NOT_FOUND\n }\n\n file.size = upload_length\n\n await this.metadataManager.saveMetadata({ upload: file, uploadId })\n }\n\n /**\n * Writes `buffer` to the file specified by the upload's `id` at `offset`\n * @param readable - The readable stream to write\n * @param id - The upload ID\n * @param offset - The byte offset to write at\n * @returns Promise that resolves to the number of bytes written\n */\n public async write(\n readable: stream.Readable,\n id: string,\n offset: number\n ): Promise<number> {\n const metadata = await this.metadataManager.getMetadata({ id })\n\n // TUS sends PATCH requests with an `upload-offset` header.\n // Offset the write by the offset in the PATCH request.\n const calculatedOffset = this.fileOperations.calculateOffsetFromParts({\n parts: await this.partsManager.retrieveParts({ id }),\n })\n const offsetDiff = offset - calculatedOffset\n const requestedOffset = offset\n\n let finalReadable = readable\n\n if (offsetDiff < 0) {\n throw ERRORS.FILE_WRITE_ERROR\n }\n\n // If the offset given in the PATCH request is higher than\n // the expected offset, we need to prepend an incomplete\n // part to the readable stream, if one exists.\n if (offsetDiff > 0) {\n const incompletePart = await this.partsManager.downloadIncompletePart({\n id,\n })\n\n if (!incompletePart) {\n throw ERRORS.FILE_WRITE_ERROR\n }\n\n if (incompletePart.size !== offsetDiff) {\n throw ERRORS.FILE_WRITE_ERROR\n }\n\n // Clear the incomplete part from S3 since it's going to be combined with the current request\n await this.partsManager.deleteIncompletePart({ id })\n\n // Adjust offset to account for the incomplete part\n offset = requestedOffset - incompletePart.size\n\n finalReadable = stream.Readable.from(\n (async function* () {\n yield* incompletePart.createReader({ cleanUpOnEnd: true })\n yield* readable\n })()\n )\n }\n\n const partNumber = this.fileOperations.calculatePartNumber({\n parts: await this.partsManager.retrieveParts({ id }),\n })\n\n const bytesUploaded = await this.partsManager.uploadParts({\n metadata,\n readStream: finalReadable,\n currentPartNumber: partNumber,\n offset,\n })\n\n // The size of the incomplete part should not be counted, because the\n // process of the incomplete part should be fully transparent to the user.\n const newOffset =\n requestedOffset + bytesUploaded - (offsetDiff > 0 ? offsetDiff : 0)\n\n // Check if the upload is complete\n if (metadata.file.size === newOffset) {\n try {\n const parts = await this.partsManager.retrieveParts({ id })\n await this.partsManager.finishMultipartUpload({ metadata, parts })\n\n // Update the metadata with completed state\n const completedUpload = new Upload({\n ...metadata.file,\n offset: newOffset,\n size: metadata.file.size,\n storage: metadata.file.storage,\n })\n\n await this.metadataManager.completeMetadata({ upload: completedUpload })\n // Don't clear cache immediately - Payload might still need the metadata\n // await this.metadataManager.clearCache(id)\n } catch (error) {\n log(MediaCloudLogs.S3_STORE_UPLOAD_FAILED)\n throw error\n }\n }\n\n return newOffset\n }\n\n /**\n * Returns the current state of the upload, i.e how much data has been\n * uploaded and if the upload is complete.\n */\n public async getUpload(id: string): Promise<Upload> {\n let metadata: TusUploadMetadata\n\n try {\n metadata = await this.metadataManager.getMetadata({ id })\n } catch (error) {\n if (\n error instanceof NoSuchKey ||\n error instanceof NotFound ||\n (error as AWSError)?.Code === 'NotFound' ||\n (error as AWSError)?.Code === 'NoSuchKey'\n ) {\n throw ERRORS.FILE_NOT_FOUND\n }\n throw error\n }\n\n let offset: number\n\n try {\n const parts = await this.partsManager.retrieveParts({ id })\n offset = this.fileOperations.calculateOffsetFromParts({ parts })\n } catch (error) {\n // Check if the error is caused by the upload not being found. This happens\n // when the multipart upload has already been completed or aborted.\n if (\n (error as AWSError)?.Code === 'NoSuchUpload' ||\n (error as AWSError)?.Code === 'NoSuchKey'\n ) {\n return new Upload({\n ...metadata.file,\n metadata: metadata.file.metadata,\n offset: metadata.file.size as number,\n size: metadata.file.size,\n storage: metadata.file.storage,\n })\n }\n\n log(MediaCloudLogs.S3_STORE_RETRIEVE_PARTS_ERROR)\n throw error\n }\n\n const incompletePartSize = await this.partsManager.getIncompletePartSize({\n id,\n })\n\n return new Upload({\n ...metadata.file,\n offset: offset + (incompletePartSize ?? 0),\n size: metadata.file.size,\n storage: metadata.file.storage,\n })\n }\n\n /**\n * Reads the file specified by the upload's `id` and returns a readable stream\n */\n async read(id: string): Promise<Readable> {\n log(MediaCloudLogs.S3_STORE_READ_ATTEMPT)\n let retries = 3\n let lastError: Error | null = null\n\n while (retries > 0) {\n try {\n const data = await this.client.getObject({\n Bucket: this.bucket,\n Key: id,\n })\n log(MediaCloudLogs.S3_STORE_READ_SUCCESS)\n return data.Body as Readable\n } catch (error) {\n log(MediaCloudLogs.S3_STORE_READ_RETRY)\n lastError = error as Error\n retries--\n\n if (retries > 0) {\n // Wait a bit before retrying, in case S3 needs time for consistency\n await new Promise((resolve) => setTimeout(resolve, 100))\n }\n }\n }\n\n log(MediaCloudLogs.S3_STORE_READ_FAILED)\n throw lastError || new Error(`Failed to read file ${id} after retries`)\n }\n\n /**\n * Removes the file specified by the upload's `id`\n */\n public async remove(id: string): Promise<void> {\n try {\n const { 'upload-id': uploadId } = await this.metadataManager.getMetadata({\n id,\n })\n if (uploadId) {\n await this.client.abortMultipartUpload({\n Bucket: this.bucket,\n Key: id,\n UploadId: uploadId,\n })\n }\n } catch (error) {\n if (\n (error as AWSError)?.code &&\n ['NoSuchKey', 'NoSuchUpload', 'NotFound'].includes(\n (error as AWSError).Code || ''\n )\n ) {\n log(MediaCloudLogs.S3_STORE_FILE_NOT_FOUND)\n throw ERRORS.FILE_NOT_FOUND\n }\n throw error\n }\n\n await this.client.deleteObjects({\n Bucket: this.bucket,\n Delete: {\n Objects: [\n { Key: id },\n { Key: this.metadataManager.generateInfoKey({ id }) },\n {\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n }),\n },\n ],\n },\n })\n\n await this.metadataManager.clearCache({ id })\n }\n\n /**\n * Combine all multipart uploads into a single object\n */\n public async completeMultipartUpload(id: string): Promise<Upload> {\n const metadata = await this.metadataManager.getMetadata({ id })\n const parts = await this.partsManager.retrieveParts({ id })\n\n const incompletePartInfo = await this.partsManager.downloadIncompletePart({\n id,\n })\n\n if (incompletePartInfo) {\n // Upload the incomplete part as a regular part\n await this.partsManager.uploadPart({\n metadata,\n readStream: incompletePartInfo.createReader({ cleanUpOnEnd: true }),\n partNumber: parts.length + 1,\n })\n\n // Remove the incomplete part\n await this.partsManager.deleteIncompletePart({ id })\n\n // Re-fetch parts to include the newly uploaded part\n const updatedParts = await this.partsManager.retrieveParts({ id })\n await this.partsManager.finishMultipartUpload({\n metadata,\n parts: updatedParts,\n })\n } else {\n await this.partsManager.finishMultipartUpload({ metadata, parts })\n }\n\n const completedUpload = new Upload({\n ...metadata.file,\n offset: metadata.file.size ?? 0,\n size: metadata.file.size ?? 0,\n storage: metadata.file.storage,\n })\n\n await this.metadataManager.completeMetadata({ upload: completedUpload })\n\n return completedUpload\n }\n\n /**\n * Get the full S3 URL for an uploaded file\n */\n public getUrl(id: string): string {\n // Use the custom endpoint if available\n if (this.customEndpoint) {\n return `${this.customEndpoint}/${this.bucket}/${id}`\n }\n\n // Fallback to standard AWS S3 URL format\n const regionConfig = this.client.config.region\n let region: string\n\n // If region is a function, we can't resolve it synchronously, so use a fallback\n if (typeof regionConfig === 'function') {\n region = 'us-east-1' // fallback for sync calls\n } else {\n region = regionConfig || 'us-east-1'\n }\n\n // Standard AWS S3 URL format\n if (region === 'us-east-1') {\n return `https://${this.bucket}.s3.amazonaws.com/${id}`\n } else {\n return `https://${this.bucket}.s3.${region}.amazonaws.com/${id}`\n }\n }\n\n /**\n * Deletes expired incomplete uploads.\n * Returns the number of deleted uploads.\n */\n async deleteExpired(): Promise<number> {\n return this.expirationManager.deleteExpired()\n }\n\n /**\n * Returns the expiration period in milliseconds\n */\n getExpiration(): number {\n return this.expirationManager.getExpiration()\n }\n}\n"],"mappings":"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"}
@@ -13,4 +13,4 @@ declare class Semaphore {
13
13
  type SemaphorePermit = () => void;
14
14
  //#endregion
15
15
  export { Semaphore, SemaphorePermit };
16
- //# sourceMappingURL=semaphore.d.ts.map
16
+ //# sourceMappingURL=semaphore.d.mts.map
@@ -4,9 +4,8 @@
4
4
  * Used to limit the number of simultaneous part uploads to S3.
5
5
  */
6
6
  var Semaphore = class {
7
- permits;
8
- queue = [];
9
7
  constructor(permits) {
8
+ this.queue = [];
10
9
  this.permits = permits;
11
10
  }
12
11
  release() {
@@ -29,4 +28,4 @@ var Semaphore = class {
29
28
 
30
29
  //#endregion
31
30
  export { Semaphore };
32
- //# sourceMappingURL=semaphore.js.map
31
+ //# sourceMappingURL=semaphore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semaphore.mjs","names":[],"sources":["../../../../src/tus/stores/s3/semaphore.ts"],"sourcesContent":["/**\n * A semaphore implementation for controlling concurrent operations.\n * Used to limit the number of simultaneous part uploads to S3.\n */\nexport class Semaphore {\n private permits: number\n private queue: (() => void)[] = []\n\n constructor(permits: number) {\n this.permits = permits\n }\n\n private release(): void {\n this.permits++\n const next = this.queue.shift()\n if (next) {\n next()\n }\n }\n\n async acquire(): Promise<() => void> {\n return new Promise((resolve) => {\n if (this.permits > 0) {\n this.permits--\n resolve(() => this.release())\n } else {\n this.queue.push(() => {\n this.permits--\n resolve(() => this.release())\n })\n }\n })\n }\n}\n\nexport type SemaphorePermit = () => void\n"],"mappings":";;;;;AAIA,IAAa,YAAb,MAAuB;CAIrB,YAAY,SAAiB;eAFG,EAAE;AAGhC,OAAK,UAAU;;CAGjB,AAAQ,UAAgB;AACtB,OAAK;EACL,MAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,MAAI,KACF,OAAM;;CAIV,MAAM,UAA+B;AACnC,SAAO,IAAI,SAAS,YAAY;AAC9B,OAAI,KAAK,UAAU,GAAG;AACpB,SAAK;AACL,kBAAc,KAAK,SAAS,CAAC;SAE7B,MAAK,MAAM,WAAW;AACpB,SAAK;AACL,kBAAc,KAAK,SAAS,CAAC;KAC7B;IAEJ"}
@@ -0,0 +1,142 @@
1
+ //#region src/types/errors.d.ts
2
+ declare const MediaCloudErrors: {
3
+ readonly MUX_CONFIG_MISSING: {
4
+ readonly message: "Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux";
5
+ readonly errorCode: 400;
6
+ };
7
+ readonly MUX_CONFIG_INCOMPLETE: {
8
+ readonly message: "Mux configuration is missing. Mux features will not be available";
9
+ readonly errorCode: 400;
10
+ };
11
+ readonly MUX_UPLOAD_ID_MISSING: {
12
+ readonly message: "No upload-id found for upload";
13
+ readonly errorCode: 400;
14
+ };
15
+ readonly MUX_ASSET_DELETE_ERROR: {
16
+ readonly message: "Error deleting Mux asset";
17
+ readonly errorCode: 500;
18
+ };
19
+ readonly MUX_UPLOAD_ERROR: {
20
+ readonly message: "Mux video upload failed";
21
+ readonly errorCode: 500;
22
+ };
23
+ readonly MUX_DIRECT_UPLOAD_ERROR: {
24
+ readonly message: "Mux direct upload failed";
25
+ readonly errorCode: 500;
26
+ };
27
+ readonly MUX_CREATE_UPLOAD_ERROR: {
28
+ readonly message: "Error in Mux create upload handler";
29
+ readonly errorCode: 500;
30
+ };
31
+ readonly MUX_REQUEST_NO_JSON: {
32
+ readonly message: "Request does not support json() method";
33
+ readonly errorCode: 400;
34
+ };
35
+ readonly S3_CONFIG_MISSING: {
36
+ readonly message: "S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions";
37
+ readonly errorCode: 400;
38
+ };
39
+ readonly S3_DELETE_ERROR: {
40
+ readonly message: "Error deleting file from S3";
41
+ readonly errorCode: 500;
42
+ };
43
+ readonly S3_UNIQUE_NAME_ERROR: {
44
+ readonly message: "Could not find a unique file name after maximum tries";
45
+ readonly errorCode: 500;
46
+ };
47
+ readonly TUS_UPLOAD_ERROR: {
48
+ readonly message: "TUS file upload error occurred";
49
+ readonly errorCode: 500;
50
+ };
51
+ readonly FILE_TYPE_UNKNOWN: {
52
+ readonly message: "Unable to determine file type";
53
+ readonly errorCode: 400;
54
+ };
55
+ readonly FILE_TYPE_ERROR: {
56
+ readonly message: "Error determining file type";
57
+ readonly errorCode: 500;
58
+ };
59
+ readonly FILENAME_SANITIZE_ERROR: {
60
+ readonly message: "Error sanitizing filename";
61
+ readonly errorCode: 500;
62
+ };
63
+ readonly FILE_MISSING_NAME: {
64
+ readonly message: "Filename is missing, cannot parse file";
65
+ readonly errorCode: 500;
66
+ };
67
+ readonly FILE_NOT_FOUND: {
68
+ readonly message: "File not found";
69
+ readonly errorCode: 404;
70
+ };
71
+ readonly FILE_DIMENSIONS_ERROR: {
72
+ readonly message: "Error setting media dimensions";
73
+ readonly errorCode: 500;
74
+ };
75
+ readonly UPLOAD_NO_URL: {
76
+ readonly message: "No upload URL provided, cannot parse upload ID";
77
+ readonly errorCode: 400;
78
+ };
79
+ readonly UPLOAD_HANDLER_ERROR: {
80
+ readonly message: "Upload handler error occurred";
81
+ readonly errorCode: 500;
82
+ };
83
+ readonly UPLOAD_POLLING_ERROR: {
84
+ readonly message: "Polling error for upload";
85
+ readonly errorCode: 500;
86
+ };
87
+ readonly PLUGIN_NOT_CONFIGURED: {
88
+ readonly message: "Payload Media Cloud plugin is not configured";
89
+ readonly errorCode: 500;
90
+ };
91
+ readonly NAMING_FUNCTION_ERROR: {
92
+ readonly message: "Error in namingFunction";
93
+ readonly errorCode: 500;
94
+ };
95
+ };
96
+ declare const MediaCloudLogs: {
97
+ readonly S3_STORE_MULTIPART_INIT: {
98
+ readonly message: "Initializing multipart upload";
99
+ };
100
+ readonly S3_STORE_MULTIPART_CREATED: {
101
+ readonly message: "Multipart upload created";
102
+ };
103
+ readonly S3_STORE_UPLOAD_FAILED: {
104
+ readonly message: "Failed to finish upload";
105
+ };
106
+ readonly S3_STORE_READ_ATTEMPT: {
107
+ readonly message: "Attempting to read file from S3";
108
+ };
109
+ readonly S3_STORE_READ_SUCCESS: {
110
+ readonly message: "Successfully read file from S3";
111
+ };
112
+ readonly S3_STORE_READ_RETRY: {
113
+ readonly message: "Failed to read file, retries left";
114
+ };
115
+ readonly S3_STORE_READ_FAILED: {
116
+ readonly message: "Failed to read file after all retries";
117
+ };
118
+ readonly S3_STORE_FILE_NOT_FOUND: {
119
+ readonly message: "No file found";
120
+ };
121
+ readonly S3_STORE_RETRIEVE_PARTS_ERROR: {
122
+ readonly message: "Error retrieving parts";
123
+ };
124
+ readonly S3_STORE_METADATA_SAVING: {
125
+ readonly message: "Saving metadata";
126
+ };
127
+ readonly S3_STORE_METADATA_SAVED: {
128
+ readonly message: "Metadata file saved";
129
+ };
130
+ readonly S3_STORE_METADATA_CACHE_CLEARED: {
131
+ readonly message: "Removing cached data";
132
+ };
133
+ readonly S3_STORE_INCOMPLETE_PART_UPLOADED: {
134
+ readonly message: "Finished uploading incomplete part";
135
+ };
136
+ readonly S3_STORE_CHUNK_REMOVAL_FAILED: {
137
+ readonly message: "Failed to remove chunk";
138
+ };
139
+ };
140
+ //#endregion
141
+ export { MediaCloudErrors, MediaCloudLogs };
142
+ //# sourceMappingURL=errors.d.mts.map
@@ -60,6 +60,18 @@ const MediaCloudErrors = {
60
60
  message: "Error sanitizing filename",
61
61
  errorCode: 500
62
62
  },
63
+ FILE_MISSING_NAME: {
64
+ message: "Filename is missing, cannot parse file",
65
+ errorCode: 500
66
+ },
67
+ FILE_NOT_FOUND: {
68
+ message: "File not found",
69
+ errorCode: 404
70
+ },
71
+ FILE_DIMENSIONS_ERROR: {
72
+ message: "Error setting media dimensions",
73
+ errorCode: 500
74
+ },
63
75
  UPLOAD_NO_URL: {
64
76
  message: "No upload URL provided, cannot parse upload ID",
65
77
  errorCode: 400
@@ -100,4 +112,4 @@ const MediaCloudLogs = {
100
112
 
101
113
  //#endregion
102
114
  export { MediaCloudErrors, MediaCloudLogs };
103
- //# sourceMappingURL=errors.js.map
115
+ //# sourceMappingURL=errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["export const MediaCloudErrors = {\n // Mux Errors\n MUX_CONFIG_MISSING: {\n message:\n 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n errorCode: 400,\n },\n MUX_CONFIG_INCOMPLETE: {\n message: 'Mux configuration is missing. Mux features will not be available',\n errorCode: 400,\n },\n MUX_UPLOAD_ID_MISSING: {\n message: 'No upload-id found for upload',\n errorCode: 400,\n },\n MUX_ASSET_DELETE_ERROR: {\n message: 'Error deleting Mux asset',\n errorCode: 500,\n },\n MUX_UPLOAD_ERROR: {\n message: 'Mux video upload failed',\n errorCode: 500,\n },\n MUX_DIRECT_UPLOAD_ERROR: {\n message: 'Mux direct upload failed',\n errorCode: 500,\n },\n MUX_CREATE_UPLOAD_ERROR: {\n message: 'Error in Mux create upload handler',\n errorCode: 500,\n },\n MUX_REQUEST_NO_JSON: {\n message: 'Request does not support json() method',\n errorCode: 400,\n },\n\n // S3 Errors\n S3_CONFIG_MISSING: {\n message:\n 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n errorCode: 400,\n },\n S3_DELETE_ERROR: {\n message: 'Error deleting file from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n\n // File Errors\n FILE_TYPE_UNKNOWN: {\n message: 'Unable to determine file type',\n errorCode: 400,\n },\n FILE_TYPE_ERROR: {\n message: 'Error determining file type',\n errorCode: 500,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n} as const\n\nexport const MediaCloudLogs = {\n // S3 Store Logging\n S3_STORE_MULTIPART_INIT: {\n message: 'Initializing multipart upload',\n },\n S3_STORE_MULTIPART_CREATED: {\n message: 'Multipart upload created',\n },\n S3_STORE_UPLOAD_FAILED: {\n message: 'Failed to finish upload',\n },\n S3_STORE_READ_ATTEMPT: {\n message: 'Attempting to read file from S3',\n },\n S3_STORE_READ_SUCCESS: {\n message: 'Successfully read file from S3',\n },\n S3_STORE_READ_RETRY: {\n message: 'Failed to read file, retries left',\n },\n S3_STORE_READ_FAILED: {\n message: 'Failed to read file after all retries',\n },\n S3_STORE_FILE_NOT_FOUND: {\n message: 'No file found',\n },\n S3_STORE_RETRIEVE_PARTS_ERROR: {\n message: 'Error retrieving parts',\n },\n S3_STORE_METADATA_SAVING: {\n message: 'Saving metadata',\n },\n S3_STORE_METADATA_SAVED: {\n message: 'Metadata file saved',\n },\n S3_STORE_METADATA_CACHE_CLEARED: {\n message: 'Removing cached data',\n },\n S3_STORE_INCOMPLETE_PART_UPLOADED: {\n message: 'Finished uploading incomplete part',\n },\n S3_STORE_CHUNK_REMOVAL_FAILED: {\n message: 'Failed to remove chunk',\n },\n} as const\n"],"mappings":";AAAA,MAAa,mBAAmB;CAE9B,oBAAoB;EAClB,SACE;EACF,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,wBAAwB;EACtB,SAAS;EACT,WAAW;EACZ;CACD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SACE;EACF,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,gBAAgB;EACd,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CAGD,eAAe;EACb,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACF;AAED,MAAa,iBAAiB;CAE5B,yBAAyB,EACvB,SAAS,iCACV;CACD,4BAA4B,EAC1B,SAAS,4BACV;CACD,wBAAwB,EACtB,SAAS,2BACV;CACD,uBAAuB,EACrB,SAAS,mCACV;CACD,uBAAuB,EACrB,SAAS,kCACV;CACD,qBAAqB,EACnB,SAAS,qCACV;CACD,sBAAsB,EACpB,SAAS,yCACV;CACD,yBAAyB,EACvB,SAAS,iBACV;CACD,+BAA+B,EAC7B,SAAS,0BACV;CACD,0BAA0B,EACxB,SAAS,mBACV;CACD,yBAAyB,EACvB,SAAS,uBACV;CACD,iCAAiC,EAC/B,SAAS,wBACV;CACD,mCAAmC,EACjC,SAAS,sCACV;CACD,+BAA+B,EAC7B,SAAS,0BACV;CACF"}
@@ -71,4 +71,4 @@ interface NodeFSError extends Error {
71
71
  type StaticRenditions = Record<string, unknown> | null | undefined;
72
72
  //#endregion
73
73
  export { AWSError, IncompletePartInfo, MediaCloudPluginOptions, NodeFSError, S3StoreConfig, StaticRenditions, TusUploadMetadata };
74
- //# sourceMappingURL=index.d.ts.map
74
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ export { };
@@ -27,4 +27,4 @@ declare function generateUniqueFilename(originalFilename: string): string;
27
27
  declare function sanitizeFilename(filename: string): string;
28
28
  //#endregion
29
29
  export { generateUniqueFilename, getFileType, isVideo, sanitizeFilename };
30
- //# sourceMappingURL=file.d.ts.map
30
+ //# sourceMappingURL=file.d.mts.map
@@ -1,5 +1,5 @@
1
- import { MediaCloudErrors } from "../types/errors.js";
2
- import { useErrorHandler } from "../hooks/useErrorHandler.js";
1
+ import { MediaCloudErrors } from "../types/errors.mjs";
2
+ import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
3
3
  import { fileTypeFromBuffer } from "file-type";
4
4
  import { parse } from "pathe";
5
5
 
@@ -29,8 +29,7 @@ const MUX_SUPPORTED_VIDEO_FORMATS = new Set([
29
29
  */
30
30
  async function isVideo(file) {
31
31
  try {
32
- const buffer = new Uint8Array(await file.arrayBuffer());
33
- const fileType = await fileTypeFromBuffer(buffer);
32
+ const fileType = await fileTypeFromBuffer(new Uint8Array(await file.arrayBuffer()));
34
33
  return fileType?.mime ? MUX_SUPPORTED_VIDEO_FORMATS.has(fileType.mime) : false;
35
34
  } catch {
36
35
  return false;
@@ -43,12 +42,10 @@ async function isVideo(file) {
43
42
  */
44
43
  async function getFileType(file) {
45
44
  try {
46
- const buffer = new Uint8Array(await file.arrayBuffer());
47
- const fileType = await fileTypeFromBuffer(buffer);
48
- return fileType?.mime;
45
+ return (await fileTypeFromBuffer(new Uint8Array(await file.arrayBuffer())))?.mime;
49
46
  } catch (_error) {
50
47
  logError(MediaCloudErrors.FILE_TYPE_ERROR);
51
- return void 0;
48
+ return;
52
49
  }
53
50
  }
54
51
  /**
@@ -71,8 +68,7 @@ function generateUniqueFilename(originalFilename) {
71
68
  function sanitizeFilename(filename) {
72
69
  try {
73
70
  const parsed = parse(filename);
74
- const sanitized = parsed.name.replace(/[^a-zA-Z0-9_.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "");
75
- return `${sanitized}${parsed.ext}`;
71
+ return `${parsed.name.replace(/[^a-zA-Z0-9_.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "")}${parsed.ext}`;
76
72
  } catch (_error) {
77
73
  logError(MediaCloudErrors.FILENAME_SANITIZE_ERROR);
78
74
  return filename;
@@ -81,4 +77,4 @@ function sanitizeFilename(filename) {
81
77
 
82
78
  //#endregion
83
79
  export { generateUniqueFilename, getFileType, isVideo, sanitizeFilename };
84
- //# sourceMappingURL=file.js.map
80
+ //# sourceMappingURL=file.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.mjs","names":[],"sources":["../../src/utils/file.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport { parse } from 'pathe'\n\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nconst { logError } = useErrorHandler()\n\nconst MUX_SUPPORTED_VIDEO_FORMATS = new Set([\n 'application/mxf',\n 'video/3gpp',\n 'video/3gpp2',\n 'video/mp2t',\n 'video/mp4',\n 'video/mpeg',\n 'video/quicktime',\n 'video/webm',\n 'video/x-f4v',\n 'video/x-flv',\n 'video/x-matroska',\n 'video/x-ms-asf',\n 'video/x-ms-wmv',\n 'video/x-msvideo',\n 'video/x-mxf',\n])\n\n/**\n * Checks if a file is a video file supported by Mux\n * @param file - The file to check\n * @returns Promise that resolves to true if the file is a supported video format, false otherwise\n */\nexport async function isVideo(file: File): Promise<boolean> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n\n return fileType?.mime\n ? MUX_SUPPORTED_VIDEO_FORMATS.has(fileType.mime)\n : false\n } catch {\n return false\n }\n}\n\n/**\n * Gets the MIME type of a file using file-type library\n * @param file - The file to check\n * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined\n */\nexport async function getFileType(file: File): Promise<string | undefined> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n return fileType?.mime\n } catch (_error) {\n logError(MediaCloudErrors.FILE_TYPE_ERROR)\n return undefined\n }\n}\n\n/**\n * Generates a unique filename by appending a random suffix if needed\n * @param originalFilename - The original filename\n * @returns A unique filename with a random suffix appended before the extension\n */\nexport function generateUniqueFilename(originalFilename: string): string {\n const timestamp = Date.now().toString(36)\n const { name, ext } = parse(originalFilename)\n return `${name}-${timestamp}${ext}`\n}\n\n/**\n * Sanitizes a filename by removing/replacing invalid characters and handling edge cases\n * Converts all non-alphanumeric characters (except dots for extensions) to underscores\n * @param filename - The filename to sanitize\n * @returns A sanitized filename safe for all operating systems\n * @throws {TypeError} When filename is not a string\n */\nexport function sanitizeFilename(filename: string): string {\n try {\n const parsed = parse(filename)\n const sanitized = parsed.name\n .replace(/[^a-zA-Z0-9_.-]/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/^_|_$/g, '')\n return `${sanitized}${parsed.ext}`\n } catch (_error) {\n logError(MediaCloudErrors.FILENAME_SANITIZE_ERROR)\n return filename\n }\n}\n"],"mappings":";;;;;;AAMA,MAAM,EAAE,aAAa,iBAAiB;AAEtC,MAAM,8BAA8B,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,eAAsB,QAAQ,MAA8B;AAC1D,KAAI;EAEF,MAAM,WAAW,MAAM,mBADR,IAAI,WAAW,MAAM,KAAK,aAAa,CAAC,CACN;AAEjD,SAAO,UAAU,OACb,4BAA4B,IAAI,SAAS,KAAK,GAC9C;SACE;AACN,SAAO;;;;;;;;AASX,eAAsB,YAAY,MAAyC;AACzE,KAAI;AAGF,UADiB,MAAM,mBADR,IAAI,WAAW,MAAM,KAAK,aAAa,CAAC,CACN,GAChC;UACV,QAAQ;AACf,WAAS,iBAAiB,gBAAgB;AAC1C;;;;;;;;AASJ,SAAgB,uBAAuB,kBAAkC;CACvE,MAAM,YAAY,KAAK,KAAK,CAAC,SAAS,GAAG;CACzC,MAAM,EAAE,MAAM,QAAQ,MAAM,iBAAiB;AAC7C,QAAO,GAAG,KAAK,GAAG,YAAY;;;;;;;;;AAUhC,SAAgB,iBAAiB,UAA0B;AACzD,KAAI;EACF,MAAM,SAAS,MAAM,SAAS;AAK9B,SAAO,GAJW,OAAO,KACtB,QAAQ,oBAAoB,IAAI,CAChC,QAAQ,UAAU,IAAI,CACtB,QAAQ,UAAU,GAAG,GACF,OAAO;UACtB,QAAQ;AACf,WAAS,iBAAiB,wBAAwB;AAClD,SAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maas/payload-plugin-media-cloud",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "contributors": [
6
6
  {
@@ -16,26 +16,27 @@
16
16
  "files": [
17
17
  "dist"
18
18
  ],
19
- "main": "./dist/index.js",
20
- "types": "./dist/index.d.ts",
19
+ "main": "./dist/index.mjs",
20
+ "types": "./dist/index.d.mts",
21
21
  "exports": {
22
22
  ".": {
23
- "types": "./dist/index.d.ts",
24
- "import": "./dist/index.js"
23
+ "types": "./dist/index.d.mts",
24
+ "import": "./dist/index.mjs"
25
25
  },
26
26
  "./components": {
27
- "types": "./dist/components/index.d.ts",
28
- "import": "./dist/components/index.js"
27
+ "types": "./dist/components/index.d.mts",
28
+ "import": "./dist/components/index.mjs"
29
29
  }
30
30
  },
31
31
  "sideEffects": false,
32
32
  "dependencies": {
33
- "@aws-sdk/client-s3": "^3.859.0",
34
- "@mux/mux-node": "^12.4.0",
33
+ "@aws-sdk/client-s3": "^3.940.0",
34
+ "@mux/mux-node": "^12.8.0",
35
35
  "@mux/upchunk": "^3.5.0",
36
36
  "@tus/server": "^2.3.0",
37
37
  "@tus/utils": "^0.6.0",
38
- "file-type": "^21.0.0",
38
+ "file-type": "^21.1.1",
39
+ "image-size": "^2.0.2",
39
40
  "mitt": "^3.0.1",
40
41
  "multistream": "^4.1.0",
41
42
  "pathe": "^2.0.3",
@@ -58,15 +59,18 @@
58
59
  }
59
60
  },
60
61
  "devDependencies": {
62
+ "@mux/mux-player-react": "^3.9.1",
61
63
  "@payloadcms/plugin-cloud-storage": "3.49.1",
62
64
  "@payloadcms/ui": "3.49.1",
63
- "payload": "3.49.1",
64
65
  "@types/multistream": "^4.1.3",
65
- "@types/node": "^22.17.0",
66
- "@types/react": "^19.1.9",
67
- "@types/react-dom": "^19.1.7",
68
- "glob": "^11.0.3",
69
- "tsdown": "^0.13.3",
66
+ "@types/node": "^22.19.1",
67
+ "@types/react": "^19.2.7",
68
+ "@types/react-dom": "^19.2.3",
69
+ "glob": "^13.0.0",
70
+ "payload": "3.49.1",
71
+ "react": "19.1.0",
72
+ "react-dom": "19.1.0",
73
+ "tsdown": "^0.16.8",
70
74
  "typescript": "5.8.3",
71
75
  "@maas/error-handler": "1.0.0"
72
76
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"handleDelete.js","names":["args: HandleDeleteArgs","args: DeleteMuxAssetArgs","args: DeleteS3FileArgs"],"sources":["../../src/adapter/handleDelete.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { Mux } from '@mux/mux-node'\nimport type { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface HandleDeleteArgs {\n s3Store: S3Store\n getMuxClient: () => Mux\n}\n\ninterface DeleteMuxAssetArgs {\n getMuxClient: () => Mux\n uploadId: string\n}\n\ninterface DeleteS3FileArgs {\n s3Store: S3Store\n filename: string\n}\n\nconst { logError } = useErrorHandler()\n\n/**\n * Creates a handle delete function for processing file deletions from both Mux and S3 storage\n * @param args - The arguments for creating the delete handler\n * @param args.s3Store - The S3Store instance for S3 file operations\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns A HandleDelete function that processes deletion requests based on storage type\n */\nexport function getHandleDelete(args: HandleDeleteArgs): HandleDelete {\n const { s3Store, getMuxClient } = args\n return async ({ doc, req }) => {\n if (req?.method !== 'DELETE' || !doc) {\n return\n }\n\n const media = doc as Document\n\n if (media?.storage === 'mux' && media.mux?.uploadId) {\n await deleteMuxAsset({ getMuxClient, uploadId: media.mux.uploadId })\n } else if (media.filename) {\n await deleteS3File({ s3Store, filename: media.filename })\n }\n }\n}\n\n/**\n * Deletes a Mux asset by upload ID\n * @param args - The arguments for deleting the Mux asset\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @param args.uploadId - The upload ID of the Mux asset to delete\n * @returns Promise that resolves when the asset is deleted or rejects on error\n */\nasync function deleteMuxAsset(args: DeleteMuxAssetArgs): Promise<void> {\n const { getMuxClient, uploadId } = args\n\n try {\n const mux = getMuxClient()\n const assets = await mux.video.assets.list({ upload_id: uploadId })\n if (assets.data.length > 0) {\n const asset = assets.data[0]\n await mux.video.assets.delete(asset.id)\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR)\n }\n}\n\n/**\n * Deletes a file from S3 storage including its metadata info file\n * @param args - The arguments for deleting the S3 file\n * @param args.s3Store - The S3Store instance for S3 operations\n * @param args.filename - The filename of the file to delete from S3\n * @returns Promise that resolves when the file is deleted or rejects on error\n */\nasync function deleteS3File(args: DeleteS3FileArgs): Promise<void> {\n const { s3Store, filename } = args\n\n try {\n const { client, bucket } = s3Store\n await client.deleteObjects({\n Bucket: bucket,\n Delete: {\n Objects: [{ Key: filename }, { Key: `${filename}.info` }],\n },\n })\n } catch (_error) {\n logError(MediaCloudErrors.S3_DELETE_ERROR)\n }\n}\n"],"mappings":";;;;AAuBA,MAAM,EAAE,UAAU,GAAG,iBAAiB;;;;;;;;AAStC,SAAgB,gBAAgBA,MAAsC;CACpE,MAAM,EAAE,SAAS,cAAc,GAAG;AAClC,QAAO,OAAO,EAAE,KAAK,KAAK,KAAK;AAC7B,MAAI,KAAK,WAAW,YAAY,CAAC,IAC/B;EAGF,MAAM,QAAQ;AAEd,MAAI,OAAO,YAAY,SAAS,MAAM,KAAK,UACzC,MAAM,eAAe;GAAE;GAAc,UAAU,MAAM,IAAI;EAAU,EAAC;WAC3D,MAAM,UACf,MAAM,aAAa;GAAE;GAAS,UAAU,MAAM;EAAU,EAAC;CAE5D;AACF;;;;;;;;AASD,eAAe,eAAeC,MAAyC;CACrE,MAAM,EAAE,cAAc,UAAU,GAAG;AAEnC,KAAI;EACF,MAAM,MAAM,cAAc;EAC1B,MAAM,SAAS,MAAM,IAAI,MAAM,OAAO,KAAK,EAAE,WAAW,SAAU,EAAC;AACnE,MAAI,OAAO,KAAK,SAAS,GAAG;GAC1B,MAAM,QAAQ,OAAO,KAAK;GAC1B,MAAM,IAAI,MAAM,OAAO,OAAO,MAAM,GAAG;EACxC;CACF,SAAQ,QAAQ;EACf,SAAS,iBAAiB,uBAAuB;CAClD;AACF;;;;;;;;AASD,eAAe,aAAaC,MAAuC;CACjE,MAAM,EAAE,SAAS,UAAU,GAAG;AAE9B,KAAI;EACF,MAAM,EAAE,QAAQ,QAAQ,GAAG;EAC3B,MAAM,OAAO,cAAc;GACzB,QAAQ;GACR,QAAQ,EACN,SAAS,CAAC,EAAE,KAAK,SAAU,GAAE,EAAE,KAAK,GAAG,SAAS,KAAK,CAAC,CAAE,CAAC,EAC1D;EACF,EAAC;CACH,SAAQ,QAAQ;EACf,SAAS,iBAAiB,gBAAgB;CAC3C;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"handleUpload.js","names":[],"sources":["../../src/adapter/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\n\ninterface ClientUploadContext {\n storage: 'mux' | 's3'\n uploadId?: string\n mimeType?: string\n}\n\n/**\n * Creates a handle upload function for processing file uploads to both Mux and S3 storage\n * @returns A HandleUpload function that processes client upload context and updates document data\n */\nexport function getHandleUpload(): HandleUpload {\n return async function ({ clientUploadContext, data }) {\n if (!clientUploadContext) {\n return data\n }\n\n const ctx = clientUploadContext as ClientUploadContext\n data.mimeType = ctx.mimeType\n\n switch (ctx.storage) {\n case 'mux':\n data.storage = 'mux'\n data.mux = {\n uploadId: ctx.uploadId || '',\n status: 'preparing',\n }\n break\n default:\n data.storage = 's3'\n break\n }\n\n return data\n }\n}\n"],"mappings":";;;;;AAYA,SAAgB,kBAAgC;AAC9C,QAAO,eAAgB,EAAE,qBAAqB,MAAM,EAAE;AACpD,MAAI,CAAC,oBACH,QAAO;EAGT,MAAM,MAAM;EACZ,KAAK,WAAW,IAAI;AAEpB,UAAQ,IAAI,SAAZ;GACE,KAAK;IACH,KAAK,UAAU;IACf,KAAK,MAAM;KACT,UAAU,IAAI,YAAY;KAC1B,QAAQ;IACT;AACD;GACF;IACE,KAAK,UAAU;AACf;EACH;AAED,SAAO;CACR;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"staticHandler.js","names":["args: StaticHandlerArgs","args?: CreateEmptyResponseArgs","stream"],"sources":["../../src/adapter/staticHandler.ts"],"sourcesContent":["import { Readable } from 'node:stream'\nimport type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface StaticHandlerArgs {\n s3Store: S3Store\n}\n\ninterface ServeS3FileArgs {\n s3Store: S3Store\n doc: Document\n}\n\ninterface CreateEmptyResponseArgs {\n mimeType?: string\n}\n\n/**\n * Creates a static handler that serves files from S3 or returns empty responses for Mux assets\n * @param args - The arguments for creating the static handler\n * @param args.s3Store - The S3Store instance used for file operations\n * @returns A StaticHandler function that serves files or empty responses based on storage type\n */\nexport function getStaticHandler(args: StaticHandlerArgs): StaticHandler {\n const { s3Store } = args\n return async (req, { params }) => {\n const { payload } = req\n const filename = params.filename\n\n const { docs } = await payload.find({\n collection: 'media',\n where: { filename: { equals: filename } },\n })\n\n const doc = docs[0] as Document\n\n if (!doc) {\n return createEmptyResponse()\n }\n\n if (doc.storage === 'mux') {\n return createEmptyResponse({ mimeType: doc.mimeType })\n }\n\n return await serveS3File({ s3Store, doc })\n }\n}\n\n/**\n * Creates an empty response with appropriate headers\n * @param args - Optional arguments for the empty response\n * @param args.mimeType - The MIME type to set in the Content-Type header\n * @returns A Response object with empty content and appropriate headers\n */\nfunction createEmptyResponse(args?: CreateEmptyResponseArgs): Response {\n const { mimeType = 'application/octet-stream' } = args ?? {}\n return new Response(new Uint8Array(0), {\n status: 200,\n headers: {\n 'Content-Type': mimeType,\n 'Content-Length': '0',\n },\n })\n}\n\n/**\n * Serves a file from S3 storage\n * @param args - The arguments for serving the S3 file\n * @param args.s3Store - The S3Store instance to read the file from\n * @param args.doc - The document containing file metadata\n * @returns A Promise that resolves to a Response object containing the file stream or empty response on error\n */\nasync function serveS3File({\n s3Store,\n doc,\n}: ServeS3FileArgs): Promise<Response> {\n try {\n const stream = await s3Store.read(doc.filename)\n\n if (stream) {\n const webStream = Readable.toWeb(stream) as ReadableStream<Uint8Array>\n return new Response(webStream)\n }\n } catch (_error) {\n // File not found or other error, fall back to empty response\n }\n\n return createEmptyResponse({})\n}\n"],"mappings":";;;;;;;;;AAwBA,SAAgB,iBAAiBA,MAAwC;CACvE,MAAM,EAAE,SAAS,GAAG;AACpB,QAAO,OAAO,KAAK,EAAE,QAAQ,KAAK;EAChC,MAAM,EAAE,SAAS,GAAG;EACpB,MAAM,WAAW,OAAO;EAExB,MAAM,EAAE,MAAM,GAAG,MAAM,QAAQ,KAAK;GAClC,YAAY;GACZ,OAAO,EAAE,UAAU,EAAE,QAAQ,SAAU,EAAE;EAC1C,EAAC;EAEF,MAAM,MAAM,KAAK;AAEjB,MAAI,CAAC,IACH,QAAO,qBAAqB;AAG9B,MAAI,IAAI,YAAY,MAClB,QAAO,oBAAoB,EAAE,UAAU,IAAI,SAAU,EAAC;AAGxD,SAAO,MAAM,YAAY;GAAE;GAAS;EAAK,EAAC;CAC3C;AACF;;;;;;;AAQD,SAAS,oBAAoBC,MAA0C;CACrE,MAAM,EAAE,WAAW,4BAA4B,GAAG,QAAQ,CAAE;AAC5D,QAAO,IAAI,SAAS,IAAI,WAAW,IAAI;EACrC,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,kBAAkB;EACnB;CACF;AACF;;;;;;;;AASD,eAAe,YAAY,EACzB,SACA,KACgB,EAAqB;AACrC,KAAI;EACF,MAAMC,WAAS,MAAM,QAAQ,KAAK,IAAI,SAAS;AAE/C,MAAIA,UAAQ;GACV,MAAM,YAAY,SAAS,MAAMA,SAAO;AACxC,UAAO,IAAI,SAAS;EACrB;CACF,SAAQ,QAAQ,CAEhB;AAED,QAAO,oBAAoB,CAAE,EAAC;AAC/B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"storageAdapter.js","names":["args: StorageAdapterArgs"],"sources":["../../src/adapter/storageAdapter.ts"],"sourcesContent":["import { getHandleUpload } from './handleUpload'\nimport { getHandleDelete } from './handleDelete'\nimport { getStaticHandler } from './staticHandler'\n\nimport type {\n Adapter,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Mux } from '@mux/mux-node'\nimport type { MediaCloudPluginOptions } from '../types'\nimport type { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface StorageAdapterArgs {\n pluginOptions: MediaCloudPluginOptions\n s3Store: S3Store\n getMuxClient: () => Mux\n}\n\n/**\n * Creates the storage adapter for media cloud plugin\n * @param args - The arguments for creating the storage adapter\n * @param args.pluginOptions - The media cloud plugin options\n * @param args.s3Store - The S3Store instance for file operations\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns An Adapter function that returns a GeneratedAdapter configuration\n */\nexport function getStorageAdapter(args: StorageAdapterArgs): Adapter {\n const { s3Store, getMuxClient } = args\n\n return (): GeneratedAdapter => ({\n name: 'media-cloud',\n clientUploads: true,\n handleUpload: getHandleUpload(),\n handleDelete: getHandleDelete({ s3Store, getMuxClient }),\n staticHandler: getStaticHandler({ s3Store }),\n })\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,SAAgB,kBAAkBA,MAAmC;CACnE,MAAM,EAAE,SAAS,cAAc,GAAG;AAElC,QAAO,OAAyB;EAC9B,MAAM;EACN,eAAe;EACf,cAAc,iBAAiB;EAC/B,cAAc,gBAAgB;GAAE;GAAS;EAAc,EAAC;EACxD,eAAe,iBAAiB,EAAE,QAAS,EAAC;CAC7C;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mediaCollection.js","names":["args: GetMediaCollectionArgs"],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { S3Store } from './../tus/stores/s3/s3-store'\n\ninterface GetMediaCollectionArgs {\n s3Store: S3Store\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { s3Store } = args\n\n return {\n slug: 'media',\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n if (doc?.storage === 'mux' && doc?.mux?.playbackId) {\n return `https://image.mux.com/${doc.mux.playbackId}/thumbnail.jpg?width=200&height=200&fit_mode=pad`\n } else if (doc?.storage === 's3') {\n // @TODO: Make configurable CDN to resize images on the fly like imgix or wsrv let use wesrv for now since its free for all\n const url = s3Store.getUrl(doc.filename)\n return `https://wsrv.nl/?url=${url}?width=200&height=200&default=1`\n }\n return null\n },\n },\n fields: [\n {\n name: 'alt',\n label: 'Alternative Text',\n type: 'text',\n },\n {\n name: 'caption',\n label: 'Caption',\n type: 'text',\n },\n {\n name: 'copyright',\n label: 'Copyright',\n type: 'text',\n },\n {\n name: 'storage',\n label: 'Storage',\n type: 'select',\n options: [\n {\n label: 'Mux',\n value: 'mux',\n },\n {\n label: 'S3',\n value: 's3',\n },\n ],\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'mux',\n label: 'Mux',\n type: 'group',\n admin: {\n disableListColumn: true,\n disableBulkEdit: true,\n disableListFilter: true,\n readOnly: true,\n condition: (data) => {\n return data.storage === 'mux'\n },\n },\n fields: [\n {\n name: 'preview',\n type: 'ui',\n admin: {\n condition: (data) => {\n return data.storage === 'mux'\n },\n disableListColumn: true,\n components: {\n Field: '@maas/payload-plugin-media-cloud/components#MuxPreview',\n },\n },\n },\n {\n name: 'status',\n label: 'Status',\n type: 'text',\n },\n {\n name: 'uploadId',\n label: 'Upload ID',\n type: 'text',\n },\n {\n name: 'assetId',\n label: 'Asset ID',\n type: 'text',\n },\n {\n name: 'playbackId',\n label: 'Playback ID',\n type: 'text',\n },\n {\n name: 'aspectRatio',\n label: 'Aspect Ratio',\n type: 'text',\n },\n {\n name: 'duration',\n label: 'Duration',\n type: 'number',\n },\n {\n name: 'tracks',\n label: 'Tracks',\n type: 'json',\n },\n {\n name: 'maxResolutionTier',\n label: 'Max Resolution Tier',\n type: 'text',\n },\n {\n name: 'videoQuality',\n label: 'Video Quality',\n type: 'text',\n },\n {\n name: 'staticRenditions',\n label: 'Static Renditions',\n type: 'json',\n },\n ],\n },\n ],\n }\n}\n"],"mappings":";;;;;;AAaA,SAAgB,mBACdA,MACkB;CAClB,MAAM,EAAE,SAAS,GAAG;AAEpB,QAAO;EACL,MAAM;EACN,QAAQ;GACN,MAAM,MAAM;GACZ,QAAQ,MAAM;EACf;EACD,OAAO;GACL,OAAO;GACP,YAAY,EACV,cAAc,GACf;EACF;EACD,QAAQ;GACN,MAAM;GACN,gBAAgB;GAChB,gBAAgB;GAChB,eAAe,EAAE,KAAwB,EAAE;AACzC,QAAI,KAAK,YAAY,SAAS,KAAK,KAAK,WACtC,QAAO,CAAC,sBAAsB,EAAE,IAAI,IAAI,WAAW,gDAAgD,CAAC;aAC3F,KAAK,YAAY,MAAM;KAEhC,MAAM,MAAM,QAAQ,OAAO,IAAI,SAAS;AACxC,YAAO,CAAC,qBAAqB,EAAE,IAAI,+BAA+B,CAAC;IACpE;AACD,WAAO;GACR;EACF;EACD,QAAQ;GACN;IACE,MAAM;IACN,OAAO;IACP,MAAM;GACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;GACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;GACP;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACN,SAAS,CACP;KACE,OAAO;KACP,OAAO;IACR,GACD;KACE,OAAO;KACP,OAAO;IACR,CACF;IACD,OAAO,EACL,UAAU,KACX;GACF;GACD;IACE,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;KACL,mBAAmB;KACnB,iBAAiB;KACjB,mBAAmB;KACnB,UAAU;KACV,WAAW,CAAC,SAAS;AACnB,aAAO,KAAK,YAAY;KACzB;IACF;IACD,QAAQ;KACN;MACE,MAAM;MACN,MAAM;MACN,OAAO;OACL,WAAW,CAAC,SAAS;AACnB,eAAO,KAAK,YAAY;OACzB;OACD,mBAAmB;OACnB,YAAY,EACV,OAAO,yDACR;MACF;KACF;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;KACP;IACF;GACF;EACF;CACF;AACF"}
@@ -1,4 +0,0 @@
1
- import { MuxPreview } from "./mux-preview/mux-preview.js";
2
- import { UploadHandler } from "./upload-handler/upload-handler.js";
3
- import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager/upload-manager.js";
4
- export { MuxPreview, UploadHandler, UploadManagerProvider, useUploadManagerContext };
@@ -1,5 +0,0 @@
1
- import { MuxPreview } from "./mux-preview/mux-preview.js";
2
- import { UploadHandler } from "./upload-handler/upload-handler.js";
3
- import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager/upload-manager.js";
4
-
5
- export { MuxPreview, UploadHandler, UploadManagerProvider, useUploadManagerContext };
@@ -1,2 +0,0 @@
1
- import { MuxPreview } from "./mux-preview.js";
2
- export { MuxPreview };
@@ -1,3 +0,0 @@
1
- import { MuxPreview } from "./mux-preview.js";
2
-
3
- export { MuxPreview };
@@ -1 +0,0 @@
1
- {"version":3,"file":"mux-preview.js","names":["props: UIFieldClientProps"],"sources":["../../../src/components/mux-preview/mux-preview.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\nimport MuxPlayer from '@mux/mux-player-react'\n\nimport type React from 'react'\nimport type { UIFieldClientProps } from 'payload'\n\n/**\n * React component for previewing Mux video assets\n * @param props - The UI field client props containing field data\n * @returns JSX element for Mux video preview\n */\nexport function MuxPreview(props: UIFieldClientProps) {\n const [playbackId, setPlaybackId] = useState<string | null>(null)\n const [isClient, setIsClient] = useState(false)\n\n // Ensure we’re on the client side\n useEffect(() => {\n setIsClient(true)\n }, [])\n\n useEffect(() => {\n if (!isClient) {\n return\n }\n\n // Get collection from schemaPath and ID from URL\n const collection = props.schemaPath?.split('.')[0] // \"media.muxPlayer\" -> \"media\"\n const docId = window.location.pathname.split('/').pop() // Get last part of URL\n\n if (collection && docId && docId !== 'create' && docId !== 'admin') {\n fetch(`/api/${collection}/${docId}`)\n .then((res) => res.json())\n .then((data) => setPlaybackId(data.mux?.playbackId || null))\n .catch(() => setPlaybackId(null))\n }\n }, [props.schemaPath, isClient])\n\n // Only render on client with playbackId\n return isClient && playbackId ? (\n <MuxPlayer\n playbackId={playbackId}\n streamType=\"on-demand\"\n disableTracking={true}\n style={{ height: '60vh' }}\n nohotkeys={true}\n preload=\"metadata\"\n />\n ) : null\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,WAAWA,OAA2B;CACpD,MAAM,CAAC,YAAY,cAAc,GAAG,SAAwB,KAAK;CACjE,MAAM,CAAC,UAAU,YAAY,GAAG,SAAS,MAAM;CAG/C,UAAU,MAAM;EACd,YAAY,KAAK;CAClB,GAAE,CAAE,EAAC;CAEN,UAAU,MAAM;AACd,MAAI,CAAC,SACH;EAIF,MAAM,aAAa,MAAM,YAAY,MAAM,IAAI,CAAC;EAChD,MAAM,QAAQ,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,KAAK;AAEvD,MAAI,cAAc,SAAS,UAAU,YAAY,UAAU,SACzD,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CACjC,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC,CACzB,KAAK,CAAC,SAAS,cAAc,KAAK,KAAK,cAAc,KAAK,CAAC,CAC3D,MAAM,MAAM,cAAc,KAAK,CAAC;CAEtC,GAAE,CAAC,MAAM,YAAY,QAAS,EAAC;AAGhC,QAAO,YAAY,iCAChB;EACa;EACZ,YAAW;EACX,iBAAiB;EACjB,OAAO,EAAE,QAAQ,OAAQ;EACzB,WAAW;EACX,SAAQ;GACR,GACA;AACL"}
@@ -1,2 +0,0 @@
1
- import { UploadHandler } from "./upload-handler.js";
2
- export { UploadHandler };
@@ -1,3 +0,0 @@
1
- import { UploadHandler } from "./upload-handler.js";
2
-
3
- export { UploadHandler };
@@ -1 +0,0 @@
1
- {"version":3,"file":"upload-handler.js","names":["uploadUrl?: string | null","args: UploadArgs","uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors } from '../../types/errors'\nimport { emitter } from '../../hooks/useEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, sanitizeFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n updateFilename: (filename: string) => void\n}\n\ninterface UploadResult {\n uploadId: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n}\n\nconst { logError, throwError } = useErrorHandler()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Utility function to parse upload ID from URL\n * @param uploadUrl - The upload URL to parse\n * @returns The extracted upload ID or empty string if parsing fails\n */\nfunction parseUploadId(uploadUrl?: string | null): string {\n if (!uploadUrl) {\n logError(MediaCloudErrors.UPLOAD_NO_URL)\n return ''\n }\n const url = new URL(uploadUrl)\n return url.pathname.split('/').pop() || ''\n}\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType, updateFilename } = args\n\n const filename = sanitizeFilename(file.name)\n const getUploadUrlEndpoint = `${serverURL}${apiRoute}/mux/upload`\n const getAssetEndpoint = `${serverURL}${apiRoute}/mux/asset`\n\n updateFilename(filename)\n\n try {\n // Request upload URL from Mux\n const response = await fetch(getUploadUrlEndpoint, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('add-upload', {\n id: uploadId,\n filename,\n polling: false,\n pollingUrl: getAssetEndpoint,\n })\n\n // Set up event handlers\n uploader.on('error', () => {\n logError(MediaCloudErrors.MUX_UPLOAD_ERROR)\n toast.error('Video upload failed')\n emitter.emit('remove-upload', { id: uploadId })\n })\n\n uploader.on('progress', (progress) => {\n emitter.emit('update-upload', {\n id: uploadId,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', () => {\n emitter.emit('upload-completed', { id: uploadId })\n })\n\n return {\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType, updateFilename } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n metadata: {\n filename,\n filetype,\n filesize,\n contentType: filetype,\n contentDisposition: 'inline',\n contentLength: filesize,\n },\n onError: () => {\n logError(MediaCloudErrors.TUS_UPLOAD_ERROR)\n toast.error('File upload failed')\n resolve(null)\n },\n onProgress: (bytesUploaded, bytesTotal) => {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n const uploadId = parseUploadId(upload?.url)\n emitter.emit('update-upload', {\n id: uploadId,\n progress: percentage,\n })\n },\n onSuccess: () => {\n const uploadId = parseUploadId(upload?.url)\n emitter.emit('upload-completed', { id: uploadId })\n },\n onUploadUrlAvailable: () => {\n const uploadId = parseUploadId(upload?.url)\n updateFilename(uploadId)\n emitter.emit('add-upload', { id: uploadId, filename })\n resolve({\n uploadId,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async (args) => {\n const { serverURL, apiRoute, file, updateFilename } = args\n\n try {\n const mimeType = await getFileType(file)\n\n if (!mimeType) {\n throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN)\n return null\n }\n\n const isVideoFile = await isVideo(file)\n const uploadArgs: UploadArgs = {\n file,\n serverURL,\n apiRoute,\n mimeType,\n updateFilename,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (_error) {\n logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR)\n toast.error('Upload failed')\n return null\n }\n },\n})\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager.js";
2
- export { UploadManagerProvider, useUploadManagerContext };
@@ -1,3 +0,0 @@
1
- import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager.js";
2
-
3
- export { UploadManagerProvider, useUploadManagerContext };