@maas/payload-plugin-media-cloud 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/handleDelete.js +3 -3
- package/dist/adapter/handleDelete.js.map +1 -1
- package/dist/collections/mediaCollection.js +5 -0
- package/dist/collections/mediaCollection.js.map +1 -1
- package/dist/components/upload-handler/upload-handler.js +7 -7
- package/dist/components/upload-handler/upload-handler.js.map +1 -1
- package/dist/components/upload-manager/upload-manager.js +6 -6
- package/dist/components/upload-manager/upload-manager.js.map +1 -1
- package/dist/endpoints/muxAssetHandler.js +1 -0
- package/dist/endpoints/muxAssetHandler.js.map +1 -1
- package/dist/endpoints/muxCreateUploadHandler.js +3 -3
- package/dist/endpoints/muxCreateUploadHandler.js.map +1 -1
- package/dist/error-handler/dist/index.js +43 -25
- package/dist/error-handler/dist/index.js.map +1 -1
- package/dist/hooks/useEmitter.d.ts +2 -3
- package/dist/hooks/useEmitter.js.map +1 -1
- package/dist/hooks/useErrorHandler.d.ts +5 -5
- package/dist/hooks/useErrorHandler.js +7 -6
- package/dist/hooks/useErrorHandler.js.map +1 -1
- package/dist/plugin.js +7 -7
- package/dist/plugin.js.map +1 -1
- package/dist/tus/stores/s3/file-operations.js +2 -2
- package/dist/tus/stores/s3/file-operations.js.map +1 -1
- package/dist/tus/stores/s3/metadata-manager.js +5 -5
- package/dist/tus/stores/s3/metadata-manager.js.map +1 -1
- package/dist/tus/stores/s3/parts-manager.js +8 -8
- package/dist/tus/stores/s3/parts-manager.js.map +1 -1
- package/dist/tus/stores/s3/s3-store.js +11 -11
- package/dist/tus/stores/s3/s3-store.js.map +1 -1
- package/dist/types/errors.d.ts +5 -37
- package/dist/types/errors.js +99 -38
- package/dist/types/errors.js.map +1 -1
- package/dist/utils/file.js +3 -3
- package/dist/utils/file.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3-store.js","names":["options: S3StoreConfig","id: string","isIncompletePart: boolean","value: 'false' | 'true'","upload: Upload","request: AWS.CreateMultipartUploadCommandInput","file_id: string","upload_length: number","readable: stream.Readable","offset: number","metadata: TusUploadMetadata","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 { MediaCloudError } from '../../../types/errors'\n\nconst { logConsole } = 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 logConsole(MediaCloudError.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 logConsole(MediaCloudError.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 logConsole(MediaCloudError.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 logConsole(MediaCloudError.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 logConsole(MediaCloudError.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 logConsole(MediaCloudError.S3_STORE_READ_SUCCESS)\n return data.Body as Readable\n } catch (error) {\n logConsole(MediaCloudError.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 logConsole(MediaCloudError.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 logConsole(MediaCloudError.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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"}
|
|
1
|
+
{"version":3,"file":"s3-store.js","names":["options: S3StoreConfig","id: string","isIncompletePart: boolean","value: 'false' | 'true'","upload: Upload","request: AWS.CreateMultipartUploadCommandInput","file_id: string","upload_length: number","readable: stream.Readable","offset: number","metadata: TusUploadMetadata","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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"}
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -1,40 +1,8 @@
|
|
|
1
|
+
import { ErrorSet, LogSet } from "@maas/error-handler";
|
|
2
|
+
|
|
1
3
|
//#region src/types/errors.d.ts
|
|
2
|
-
declare
|
|
3
|
-
|
|
4
|
-
MUX_CONFIG_INCOMPLETE = "Mux configuration is missing. Mux features will not be available",
|
|
5
|
-
MUX_UPLOAD_ID_MISSING = "No upload-id found for upload",
|
|
6
|
-
MUX_ASSET_DELETE_ERROR = "Error deleting Mux asset",
|
|
7
|
-
MUX_UPLOAD_ERROR = "Mux video upload failed",
|
|
8
|
-
MUX_DIRECT_UPLOAD_ERROR = "Mux direct upload failed",
|
|
9
|
-
MUX_CREATE_UPLOAD_ERROR = "Error in Mux create upload handler",
|
|
10
|
-
MUX_REQUEST_NO_JSON = "Request does not support json() method",
|
|
11
|
-
S3_CONFIG_MISSING = "S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions",
|
|
12
|
-
S3_DELETE_ERROR = "Error deleting file from S3",
|
|
13
|
-
S3_UNIQUE_NAME_ERROR = "Could not find a unique file name after maximum tries",
|
|
14
|
-
TUS_UPLOAD_ERROR = "TUS file upload error occurred",
|
|
15
|
-
FILE_TYPE_UNKNOWN = "Unable to determine file type",
|
|
16
|
-
FILE_TYPE_ERROR = "Error determining file type",
|
|
17
|
-
FILENAME_SANITIZE_ERROR = "Error sanitizing filename",
|
|
18
|
-
UPLOAD_NO_URL = "No upload URL provided, cannot parse upload ID",
|
|
19
|
-
UPLOAD_HANDLER_ERROR = "Upload handler error occurred",
|
|
20
|
-
UPLOAD_POLLING_ERROR = "Polling error for upload",
|
|
21
|
-
PLUGIN_NOT_CONFIGURED = "Payload Media Cloud plugin is not configured",
|
|
22
|
-
NAMING_FUNCTION_ERROR = "Error in namingFunction",
|
|
23
|
-
S3_STORE_MULTIPART_INIT = "Initializing multipart upload",
|
|
24
|
-
S3_STORE_MULTIPART_CREATED = "Multipart upload created",
|
|
25
|
-
S3_STORE_UPLOAD_FAILED = "Failed to finish upload",
|
|
26
|
-
S3_STORE_READ_ATTEMPT = "Attempting to read file from S3",
|
|
27
|
-
S3_STORE_READ_SUCCESS = "Successfully read file from S3",
|
|
28
|
-
S3_STORE_READ_RETRY = "Failed to read file, retries left",
|
|
29
|
-
S3_STORE_READ_FAILED = "Failed to read file after all retries",
|
|
30
|
-
S3_STORE_FILE_NOT_FOUND = "No file found",
|
|
31
|
-
S3_STORE_RETRIEVE_PARTS_ERROR = "Error retrieving parts",
|
|
32
|
-
S3_STORE_METADATA_SAVING = "Saving metadata",
|
|
33
|
-
S3_STORE_METADATA_SAVED = "Metadata file saved",
|
|
34
|
-
S3_STORE_METADATA_CACHE_CLEARED = "Removing cached data",
|
|
35
|
-
S3_STORE_INCOMPLETE_PART_UPLOADED = "Finished uploading incomplete part",
|
|
36
|
-
S3_STORE_CHUNK_REMOVAL_FAILED = "Failed to remove chunk",
|
|
37
|
-
}
|
|
4
|
+
declare const MediaCloudErrors: ErrorSet;
|
|
5
|
+
declare const MediaCloudLogs: LogSet;
|
|
38
6
|
//#endregion
|
|
39
|
-
export {
|
|
7
|
+
export { MediaCloudErrors, MediaCloudLogs };
|
|
40
8
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/types/errors.js
CHANGED
|
@@ -1,42 +1,103 @@
|
|
|
1
1
|
//#region src/types/errors.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
2
|
+
const MediaCloudErrors = {
|
|
3
|
+
MUX_CONFIG_MISSING: {
|
|
4
|
+
message: "Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux",
|
|
5
|
+
errorCode: 400
|
|
6
|
+
},
|
|
7
|
+
MUX_CONFIG_INCOMPLETE: {
|
|
8
|
+
message: "Mux configuration is missing. Mux features will not be available",
|
|
9
|
+
errorCode: 400
|
|
10
|
+
},
|
|
11
|
+
MUX_UPLOAD_ID_MISSING: {
|
|
12
|
+
message: "No upload-id found for upload",
|
|
13
|
+
errorCode: 400
|
|
14
|
+
},
|
|
15
|
+
MUX_ASSET_DELETE_ERROR: {
|
|
16
|
+
message: "Error deleting Mux asset",
|
|
17
|
+
errorCode: 500
|
|
18
|
+
},
|
|
19
|
+
MUX_UPLOAD_ERROR: {
|
|
20
|
+
message: "Mux video upload failed",
|
|
21
|
+
errorCode: 500
|
|
22
|
+
},
|
|
23
|
+
MUX_DIRECT_UPLOAD_ERROR: {
|
|
24
|
+
message: "Mux direct upload failed",
|
|
25
|
+
errorCode: 500
|
|
26
|
+
},
|
|
27
|
+
MUX_CREATE_UPLOAD_ERROR: {
|
|
28
|
+
message: "Error in Mux create upload handler",
|
|
29
|
+
errorCode: 500
|
|
30
|
+
},
|
|
31
|
+
MUX_REQUEST_NO_JSON: {
|
|
32
|
+
message: "Request does not support json() method",
|
|
33
|
+
errorCode: 400
|
|
34
|
+
},
|
|
35
|
+
S3_CONFIG_MISSING: {
|
|
36
|
+
message: "S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions",
|
|
37
|
+
errorCode: 400
|
|
38
|
+
},
|
|
39
|
+
S3_DELETE_ERROR: {
|
|
40
|
+
message: "Error deleting file from S3",
|
|
41
|
+
errorCode: 500
|
|
42
|
+
},
|
|
43
|
+
S3_UNIQUE_NAME_ERROR: {
|
|
44
|
+
message: "Could not find a unique file name after maximum tries",
|
|
45
|
+
errorCode: 500
|
|
46
|
+
},
|
|
47
|
+
TUS_UPLOAD_ERROR: {
|
|
48
|
+
message: "TUS file upload error occurred",
|
|
49
|
+
errorCode: 500
|
|
50
|
+
},
|
|
51
|
+
FILE_TYPE_UNKNOWN: {
|
|
52
|
+
message: "Unable to determine file type",
|
|
53
|
+
errorCode: 400
|
|
54
|
+
},
|
|
55
|
+
FILE_TYPE_ERROR: {
|
|
56
|
+
message: "Error determining file type",
|
|
57
|
+
errorCode: 500
|
|
58
|
+
},
|
|
59
|
+
FILENAME_SANITIZE_ERROR: {
|
|
60
|
+
message: "Error sanitizing filename",
|
|
61
|
+
errorCode: 500
|
|
62
|
+
},
|
|
63
|
+
UPLOAD_NO_URL: {
|
|
64
|
+
message: "No upload URL provided, cannot parse upload ID",
|
|
65
|
+
errorCode: 400
|
|
66
|
+
},
|
|
67
|
+
UPLOAD_HANDLER_ERROR: {
|
|
68
|
+
message: "Upload handler error occurred",
|
|
69
|
+
errorCode: 500
|
|
70
|
+
},
|
|
71
|
+
UPLOAD_POLLING_ERROR: {
|
|
72
|
+
message: "Polling error for upload",
|
|
73
|
+
errorCode: 500
|
|
74
|
+
},
|
|
75
|
+
PLUGIN_NOT_CONFIGURED: {
|
|
76
|
+
message: "Payload Media Cloud plugin is not configured",
|
|
77
|
+
errorCode: 500
|
|
78
|
+
},
|
|
79
|
+
NAMING_FUNCTION_ERROR: {
|
|
80
|
+
message: "Error in namingFunction",
|
|
81
|
+
errorCode: 500
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const MediaCloudLogs = {
|
|
85
|
+
S3_STORE_MULTIPART_INIT: { message: "Initializing multipart upload" },
|
|
86
|
+
S3_STORE_MULTIPART_CREATED: { message: "Multipart upload created" },
|
|
87
|
+
S3_STORE_UPLOAD_FAILED: { message: "Failed to finish upload" },
|
|
88
|
+
S3_STORE_READ_ATTEMPT: { message: "Attempting to read file from S3" },
|
|
89
|
+
S3_STORE_READ_SUCCESS: { message: "Successfully read file from S3" },
|
|
90
|
+
S3_STORE_READ_RETRY: { message: "Failed to read file, retries left" },
|
|
91
|
+
S3_STORE_READ_FAILED: { message: "Failed to read file after all retries" },
|
|
92
|
+
S3_STORE_FILE_NOT_FOUND: { message: "No file found" },
|
|
93
|
+
S3_STORE_RETRIEVE_PARTS_ERROR: { message: "Error retrieving parts" },
|
|
94
|
+
S3_STORE_METADATA_SAVING: { message: "Saving metadata" },
|
|
95
|
+
S3_STORE_METADATA_SAVED: { message: "Metadata file saved" },
|
|
96
|
+
S3_STORE_METADATA_CACHE_CLEARED: { message: "Removing cached data" },
|
|
97
|
+
S3_STORE_INCOMPLETE_PART_UPLOADED: { message: "Finished uploading incomplete part" },
|
|
98
|
+
S3_STORE_CHUNK_REMOVAL_FAILED: { message: "Failed to remove chunk" }
|
|
99
|
+
};
|
|
39
100
|
|
|
40
101
|
//#endregion
|
|
41
|
-
export {
|
|
102
|
+
export { MediaCloudErrors, MediaCloudLogs };
|
|
42
103
|
//# sourceMappingURL=errors.js.map
|
package/dist/types/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"errors.js","names":["MediaCloudErrors: ErrorSet","MediaCloudLogs: LogSet"],"sources":["../../src/types/errors.ts"],"sourcesContent":["import type { ErrorSet, LogSet } from '@maas/error-handler'\n\nexport const MediaCloudErrors: ErrorSet = {\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\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: LogSet = {\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":";AAEA,MAAaA,mBAA6B;CAExC,oBAAoB;EAClB,SACE;EACF,WAAW;CACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;CACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;CACZ;CACD,wBAAwB;EACtB,SAAS;EACT,WAAW;CACZ;CACD,kBAAkB;EAChB,SAAS;EACT,WAAW;CACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;CACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;CACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;CACZ;CAGD,mBAAmB;EACjB,SACE;EACF,WAAW;CACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;CACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;CACZ;CAGD,kBAAkB;EAChB,SAAS;EACT,WAAW;CACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;CACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;CACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;CACZ;CAGD,eAAe;EACb,SAAS;EACT,WAAW;CACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;CACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;CACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;CACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;CACZ;AACF;AAED,MAAaC,iBAAyB;CAEpC,yBAAyB,EACvB,SAAS,gCACV;CACD,4BAA4B,EAC1B,SAAS,2BACV;CACD,wBAAwB,EACtB,SAAS,0BACV;CACD,uBAAuB,EACrB,SAAS,kCACV;CACD,uBAAuB,EACrB,SAAS,iCACV;CACD,qBAAqB,EACnB,SAAS,oCACV;CACD,sBAAsB,EACpB,SAAS,wCACV;CACD,yBAAyB,EACvB,SAAS,gBACV;CACD,+BAA+B,EAC7B,SAAS,yBACV;CACD,0BAA0B,EACxB,SAAS,kBACV;CACD,yBAAyB,EACvB,SAAS,sBACV;CACD,iCAAiC,EAC/B,SAAS,uBACV;CACD,mCAAmC,EACjC,SAAS,qCACV;CACD,+BAA+B,EAC7B,SAAS,yBACV;AACF"}
|
package/dist/utils/file.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MediaCloudErrors } from "../types/errors.js";
|
|
2
2
|
import { useErrorHandler } from "../hooks/useErrorHandler.js";
|
|
3
3
|
import { fileTypeFromBuffer } from "file-type";
|
|
4
4
|
import { parse } from "pathe";
|
|
@@ -47,7 +47,7 @@ async function getFileType(file) {
|
|
|
47
47
|
const fileType = await fileTypeFromBuffer(buffer);
|
|
48
48
|
return fileType?.mime;
|
|
49
49
|
} catch (_error) {
|
|
50
|
-
logError(
|
|
50
|
+
logError(MediaCloudErrors.FILE_TYPE_ERROR);
|
|
51
51
|
return void 0;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -74,7 +74,7 @@ function sanitizeFilename(filename) {
|
|
|
74
74
|
const sanitized = parsed.name.replace(/[^a-zA-Z0-9_.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "");
|
|
75
75
|
return `${sanitized}${parsed.ext}`;
|
|
76
76
|
} catch (_error) {
|
|
77
|
-
logError(
|
|
77
|
+
logError(MediaCloudErrors.FILENAME_SANITIZE_ERROR);
|
|
78
78
|
return filename;
|
|
79
79
|
}
|
|
80
80
|
}
|
package/dist/utils/file.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","names":["file: File","originalFilename: string","filename: string"],"sources":["../../src/utils/file.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport { parse } from 'pathe'\n\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport {
|
|
1
|
+
{"version":3,"file":"file.js","names":["file: File","originalFilename: string","filename: string"],"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,UAAU,GAAG,iBAAiB;AAEtC,MAAM,8BAA8B,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;;AAOD,eAAsB,QAAQA,MAA8B;AAC1D,KAAI;EACF,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,aAAa;EACtD,MAAM,WAAW,MAAM,mBAAmB,OAAO;AAEjD,SAAO,UAAU,OACb,4BAA4B,IAAI,SAAS,KAAK,GAC9C;CACL,QAAO;AACN,SAAO;CACR;AACF;;;;;;AAOD,eAAsB,YAAYA,MAAyC;AACzE,KAAI;EACF,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,aAAa;EACtD,MAAM,WAAW,MAAM,mBAAmB,OAAO;AACjD,SAAO,UAAU;CAClB,SAAQ,QAAQ;EACf,SAAS,iBAAiB,gBAAgB;AAC1C,SAAO;CACR;AACF;;;;;;AAOD,SAAgB,uBAAuBC,kBAAkC;CACvE,MAAM,YAAY,KAAK,KAAK,CAAC,SAAS,GAAG;CACzC,MAAM,EAAE,MAAM,KAAK,GAAG,MAAM,iBAAiB;AAC7C,QAAO,GAAG,KAAK,CAAC,EAAE,YAAY,KAAK;AACpC;;;;;;;;AASD,SAAgB,iBAAiBC,UAA0B;AACzD,KAAI;EACF,MAAM,SAAS,MAAM,SAAS;EAC9B,MAAM,YAAY,OAAO,KACtB,QAAQ,oBAAoB,IAAI,CAChC,QAAQ,UAAU,IAAI,CACtB,QAAQ,UAAU,GAAG;AACxB,SAAO,GAAG,YAAY,OAAO,KAAK;CACnC,SAAQ,QAAQ;EACf,SAAS,iBAAiB,wBAAwB;AAClD,SAAO;CACR;AACF"}
|