@visulima/storage 1.0.0-alpha.1 → 1.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +33 -14
  3. package/dist/adapter/nuxt/module.d.ts +39 -0
  4. package/dist/adapter/nuxt/module.js +93 -0
  5. package/dist/adapter/nuxt/package.json +20 -0
  6. package/dist/handler/base/base-handler-core.d.ts +91 -0
  7. package/dist/handler/base/base-handler-fetch.d.ts +76 -0
  8. package/dist/handler/base/base-handler-node.d.ts +137 -0
  9. package/dist/handler/http/fetch/index.d.ts +4 -0
  10. package/dist/handler/http/fetch/index.js +4 -0
  11. package/dist/handler/http/hono/index.d.ts +67 -0
  12. package/dist/handler/http/hono/index.js +65 -0
  13. package/dist/handler/http/nextjs/index.d.ts +61 -0
  14. package/dist/handler/http/nextjs/index.js +37 -0
  15. package/dist/handler/http/node/index.d.ts +4 -0
  16. package/dist/handler/http/node/index.js +4 -0
  17. package/dist/handler/http/solid-start/index.d.ts +60 -0
  18. package/dist/handler/http/solid-start/index.js +44 -0
  19. package/dist/handler/multipart/multipart-base.d.ts +84 -0
  20. package/dist/handler/multipart/multipart-fetch.d.ts +64 -0
  21. package/dist/handler/multipart/multipart.d.ts +49 -0
  22. package/dist/handler/rest/rest-base.d.ts +106 -0
  23. package/dist/handler/rest/rest-fetch.d.ts +88 -0
  24. package/dist/handler/rest/rest.d.ts +93 -0
  25. package/dist/handler/tus/tus-base.d.ts +152 -0
  26. package/dist/handler/tus/tus-fetch.d.ts +72 -0
  27. package/dist/handler/tus/tus.d.ts +78 -0
  28. package/dist/handler/types.d.ts +53 -0
  29. package/dist/handler/utils/request-parser.d.ts +72 -0
  30. package/dist/handler/utils/response-builder.d.ts +83 -0
  31. package/dist/handler/utils/storage-utils.d.ts +10 -0
  32. package/dist/handler/utils/stream-utils.d.ts +29 -0
  33. package/dist/handler/utils/upload-handlers.d.ts +76 -0
  34. package/dist/index.d.ts +17 -0
  35. package/dist/index.js +13 -0
  36. package/dist/metrics/index.d.ts +2 -0
  37. package/dist/metrics/no-op-metrics.d.ts +15 -0
  38. package/dist/metrics/opentelemetry-metrics.d.ts +55 -0
  39. package/dist/openapi/index.d.ts +5 -0
  40. package/dist/openapi/index.js +5 -0
  41. package/dist/openapi/rest.d.ts +7 -0
  42. package/dist/openapi/shared.d.ts +13 -0
  43. package/dist/openapi/transform.d.ts +3 -0
  44. package/dist/openapi/tus.d.ts +7 -0
  45. package/dist/openapi/xhr.d.ts +7 -0
  46. package/dist/packem_shared/AwsLightFile-tTneXZgG.js +11 -0
  47. package/dist/packem_shared/AwsLightMetaStorage-BWSOtVaN.js +4 -0
  48. package/dist/packem_shared/AwsLightStorage-Blv-GP4v.js +133 -0
  49. package/dist/packem_shared/AzureFile-CesgFzps.js +8 -0
  50. package/dist/packem_shared/AzureSMetaStorage-CFs-OYJT.js +88 -0
  51. package/dist/packem_shared/AzureStorage-B0v048hG.js +357 -0
  52. package/dist/packem_shared/BaseTransformer-C2gLib6v.js +82 -0
  53. package/dist/packem_shared/DiskStorage-BlltBst6.js +12 -0
  54. package/dist/packem_shared/DiskStorageWithChecksum-BJTmMklc.js +238 -0
  55. package/dist/packem_shared/ERRORS-D0apMqnc.js +97 -0
  56. package/dist/packem_shared/File-Bb3P23dr.js +69 -0
  57. package/dist/packem_shared/GCSConfig-vPP22kN6.js +11 -0
  58. package/dist/packem_shared/GCSFile-BIEunhAN.js +8 -0
  59. package/dist/packem_shared/GCSMetaStorage-BMFEdzJ7.js +7 -0
  60. package/dist/packem_shared/GCStorage-DnhiwaS9.js +406 -0
  61. package/dist/packem_shared/LocalMetaStorage-CZHhKkMd.js +7 -0
  62. package/dist/packem_shared/MediaTransformer-D6658DL1.js +1197 -0
  63. package/dist/packem_shared/MetaStorage-pECeFOad.js +46 -0
  64. package/dist/packem_shared/Metadata-DRLXeJ0F.js +89 -0
  65. package/dist/packem_shared/Multipart-CNFK_Okz.js +163 -0
  66. package/dist/packem_shared/Multipart-CtUL7BQw.js +128 -0
  67. package/dist/packem_shared/NetlifyBlobFile-CXzyjqrD.js +14 -0
  68. package/dist/packem_shared/NetlifyBlobMetaStorage-DxZ5aDvD.js +9 -0
  69. package/dist/packem_shared/NetlifyBlobStorage-CLPtjHqd.js +376 -0
  70. package/dist/packem_shared/NoOpMetrics-DhAk5rXc.js +10 -0
  71. package/dist/packem_shared/OpenTelemetryMetrics-BnxhqIaH.js +67 -0
  72. package/dist/packem_shared/Rest-CAOAEkOj.js +267 -0
  73. package/dist/packem_shared/Rest-DNuLwBrK.js +228 -0
  74. package/dist/packem_shared/S3File-DZiyk9Qt.js +11 -0
  75. package/dist/packem_shared/S3MetaStorage-Dz9aDabA.js +76 -0
  76. package/dist/packem_shared/S3Storage-DHmTk-hU.js +316 -0
  77. package/dist/packem_shared/TUS_RESUMABLE-GJzZ9R-f.js +434 -0
  78. package/dist/packem_shared/Tus-B8PmlMgR.js +200 -0
  79. package/dist/packem_shared/Tus-C4F1aYOl.js +195 -0
  80. package/dist/packem_shared/ValidationError-BfF1aE4h.js +26 -0
  81. package/dist/packem_shared/VercelBlobFile-BCg4aTEq.js +18 -0
  82. package/dist/packem_shared/VercelBlobMetaStorage-Bd9F-VFm.js +9 -0
  83. package/dist/packem_shared/VercelBlobStorage-DD9LMOS6.js +261 -0
  84. package/dist/packem_shared/_commonjsHelpers-B85MJLTf.js +5 -0
  85. package/dist/packem_shared/aws-light-meta-storage-DKJBgbR6.js +446 -0
  86. package/dist/packem_shared/backblaze-BlMnIcBC.js +20 -0
  87. package/dist/packem_shared/base-handler-core-BKuf4YLT.js +303 -0
  88. package/dist/packem_shared/base-handler-fetch-Cr0hLiqg.js +291 -0
  89. package/dist/packem_shared/base-handler-node-gk5aN9Cx.js +730 -0
  90. package/dist/packem_shared/cache-B88MXQ_2.js +18 -0
  91. package/dist/packem_shared/cloudflare-Bi1q8wXE.js +21 -0
  92. package/dist/packem_shared/defaultCloudStorageFileNameValidation-DUuacjWv.js +11 -0
  93. package/dist/packem_shared/digitalOcean-CWQRJM3L.js +21 -0
  94. package/dist/packem_shared/disk-storage-Bh2wy3YW.js +810 -0
  95. package/dist/packem_shared/gcs-meta-storage-0tgMNnPO.js +158 -0
  96. package/dist/packem_shared/has-content-CY66ehMK.js +3 -0
  97. package/dist/packem_shared/headers-DoS5nwM-.js +3220 -0
  98. package/dist/packem_shared/is-expired-CTThU1q5.js +8 -0
  99. package/dist/packem_shared/isRetryableError-Dycp7127.js +82 -0
  100. package/dist/packem_shared/isValidMediaType-BeDgiObq.js +35 -0
  101. package/dist/packem_shared/local-meta-storage-By-3SNBI.js +649 -0
  102. package/dist/packem_shared/minio-C2YBZQOw.js +22 -0
  103. package/dist/packem_shared/multipart-base-B3i67SSX.js +102 -0
  104. package/dist/packem_shared/part-match-BMNqHDYD.js +96 -0
  105. package/dist/packem_shared/path-CR6YkPXX-7R1-9CMk.js +161 -0
  106. package/dist/packem_shared/response-builder-BtnRiBUI.js +45 -0
  107. package/dist/packem_shared/rest-base-GRCcnan7.js +457 -0
  108. package/dist/packem_shared/restOpenApiSpec-CwcHHY2C.js +800 -0
  109. package/dist/packem_shared/s3-base-storage-C1DIHTYY.js +510 -0
  110. package/dist/packem_shared/sharedGet-Cpo7QUyu.js +1019 -0
  111. package/dist/packem_shared/storage-C6NSXb8h.js +1175 -0
  112. package/dist/packem_shared/tigris-Hac8TKnX.js +21 -0
  113. package/dist/packem_shared/transformOpenApiSpec-DWM5WtnN.js +975 -0
  114. package/dist/packem_shared/tusOpenApiSpec-6ohiToBy.js +740 -0
  115. package/dist/packem_shared/update-size-CCGm6i1J.js +8 -0
  116. package/dist/packem_shared/validator-BeX_lJet.js +78 -0
  117. package/dist/packem_shared/waitForStorage-Cscw85sx.js +18 -0
  118. package/dist/packem_shared/wasabi-DIyflHSd.js +21 -0
  119. package/dist/packem_shared/xhrOpenApiSpec-DS17brnx.js +264 -0
  120. package/dist/storage/aws/clients/backblaze.d.ts +12 -0
  121. package/dist/storage/aws/clients/cloudflare.d.ts +13 -0
  122. package/dist/storage/aws/clients/digital-ocean.d.ts +12 -0
  123. package/dist/storage/aws/clients/index.d.ts +6 -0
  124. package/dist/storage/aws/clients/index.js +6 -0
  125. package/dist/storage/aws/clients/minio.d.ts +13 -0
  126. package/dist/storage/aws/clients/tigris.d.ts +12 -0
  127. package/dist/storage/aws/clients/types.d.ts +95 -0
  128. package/dist/storage/aws/clients/wasabi.d.ts +12 -0
  129. package/dist/storage/aws/index.d.ts +4 -0
  130. package/dist/storage/aws/index.js +3 -0
  131. package/dist/storage/aws/s3-base-storage.d.ts +248 -0
  132. package/dist/storage/aws/s3-client-adapter.d.ts +110 -0
  133. package/dist/storage/aws/s3-file.d.ts +10 -0
  134. package/dist/storage/aws/s3-meta-storage.d.ts +15 -0
  135. package/dist/storage/aws/s3-storage.d.ts +70 -0
  136. package/dist/storage/aws/types.d.ts +119 -0
  137. package/dist/storage/aws-light/aws-light-api-adapter.d.ts +130 -0
  138. package/dist/storage/aws-light/aws-light-file.d.ts +10 -0
  139. package/dist/storage/aws-light/aws-light-meta-storage.d.ts +19 -0
  140. package/dist/storage/aws-light/aws-light-storage.d.ts +65 -0
  141. package/dist/storage/aws-light/index.d.ts +4 -0
  142. package/dist/storage/aws-light/index.js +3 -0
  143. package/dist/storage/aws-light/types.d.ts +37 -0
  144. package/dist/storage/azure/azure-file.d.ts +6 -0
  145. package/dist/storage/azure/azure-meta-storage.d.ts +15 -0
  146. package/dist/storage/azure/azure-storage.d.ts +69 -0
  147. package/dist/storage/azure/index.d.ts +4 -0
  148. package/dist/storage/azure/index.js +3 -0
  149. package/dist/storage/azure/types.d.ts +62 -0
  150. package/dist/storage/gcs/fetch-error.d.ts +11 -0
  151. package/dist/storage/gcs/gcs-config.d.ts +2 -0
  152. package/dist/storage/gcs/gcs-file.d.ts +6 -0
  153. package/dist/storage/gcs/gcs-meta-storage.d.ts +27 -0
  154. package/dist/storage/gcs/gcs-storage.d.ts +91 -0
  155. package/dist/storage/gcs/index.d.ts +5 -0
  156. package/dist/storage/gcs/index.js +4 -0
  157. package/dist/storage/gcs/types.d.ts +55 -0
  158. package/dist/storage/gcs/utils.d.ts +7 -0
  159. package/dist/storage/local/disk-storage-with-checksum.d.ts +15 -0
  160. package/dist/storage/local/disk-storage.d.ts +135 -0
  161. package/dist/storage/local/index.d.ts +4 -0
  162. package/dist/storage/local/index.js +3 -0
  163. package/dist/storage/local/local-meta-storage.d.ts +32 -0
  164. package/dist/storage/meta-storage.d.ts +30 -0
  165. package/dist/storage/netlify-blob/index.d.ts +4 -0
  166. package/dist/storage/netlify-blob/index.js +3 -0
  167. package/dist/storage/netlify-blob/netlify-blob-file.d.ts +12 -0
  168. package/dist/storage/netlify-blob/netlify-blob-meta-storage.d.ts +7 -0
  169. package/dist/storage/netlify-blob/netlify-blob-storage.d.ts +93 -0
  170. package/dist/storage/netlify-blob/types.d.ts +30 -0
  171. package/dist/storage/storage.d.ts +353 -0
  172. package/dist/storage/types.d.ts +221 -0
  173. package/dist/storage/utils/file/file.d.ts +25 -0
  174. package/dist/storage/utils/file/get-file-status.d.ts +9 -0
  175. package/dist/storage/utils/file/has-content.d.ts +9 -0
  176. package/dist/storage/utils/file/index.d.ts +10 -0
  177. package/dist/storage/utils/file/is-expired.d.ts +8 -0
  178. package/dist/storage/utils/file/metadata.d.ts +18 -0
  179. package/dist/storage/utils/file/part-match.d.ts +11 -0
  180. package/dist/storage/utils/file/types.d.ts +37 -0
  181. package/dist/storage/utils/file/update-metadata.d.ts +10 -0
  182. package/dist/storage/utils/file/update-size.d.ts +10 -0
  183. package/dist/storage/vercel-blob/index.d.ts +4 -0
  184. package/dist/storage/vercel-blob/index.js +3 -0
  185. package/dist/storage/vercel-blob/types.d.ts +32 -0
  186. package/dist/storage/vercel-blob/vercel-blob-file.d.ts +16 -0
  187. package/dist/storage/vercel-blob/vercel-blob-meta-storage.d.ts +7 -0
  188. package/dist/storage/vercel-blob/vercel-blob-storage.d.ts +76 -0
  189. package/dist/transformer/audio-transformer.d.ts +128 -0
  190. package/dist/transformer/audio-transformer.js +278 -0
  191. package/dist/transformer/base-transformer.d.ts +63 -0
  192. package/dist/transformer/image-transformer.d.ts +571 -0
  193. package/dist/transformer/image-transformer.js +1100 -0
  194. package/dist/transformer/index.d.ts +5 -0
  195. package/dist/transformer/index.js +4 -0
  196. package/dist/transformer/media-transformer.d.ts +332 -0
  197. package/dist/transformer/types.d.ts +652 -0
  198. package/dist/transformer/utils.d.ts +33 -0
  199. package/dist/transformer/validation-error.d.ts +23 -0
  200. package/dist/transformer/video-transformer.d.ts +145 -0
  201. package/dist/transformer/video-transformer.js +310 -0
  202. package/dist/utils/cache.d.ts +32 -0
  203. package/dist/utils/chunked-upload.d.ts +65 -0
  204. package/dist/utils/detect-file-type.d.ts +28 -0
  205. package/dist/utils/errors.d.ts +74 -0
  206. package/dist/utils/file-path-url-matcher.d.ts +18 -0
  207. package/dist/utils/headers.d.ts +111 -0
  208. package/dist/utils/http.d.ts +92 -0
  209. package/dist/utils/locker.d.ts +26 -0
  210. package/dist/utils/pipes/stream-checksum.d.ts +47 -0
  211. package/dist/utils/pipes/stream-length.d.ts +22 -0
  212. package/dist/utils/primitives/get-last-one.d.ts +3 -0
  213. package/dist/utils/primitives/is-record.d.ts +2 -0
  214. package/dist/utils/primitives/map-values.d.ts +9 -0
  215. package/dist/utils/primitives/pick.d.ts +2 -0
  216. package/dist/utils/primitives/to-milliseconds.d.ts +12 -0
  217. package/dist/utils/primitives/to-seconds.d.ts +12 -0
  218. package/dist/utils/range-checksum.d.ts +33 -0
  219. package/dist/utils/range-hasher.d.ts +46 -0
  220. package/dist/utils/retry.d.ts +64 -0
  221. package/dist/utils/types.d.ts +108 -0
  222. package/dist/utils/validation-error.d.ts +22 -0
  223. package/dist/utils/validator.d.ts +36 -0
  224. package/package.json +5 -5
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Custom error class for validation failures
3
+ */
4
+ declare class ValidationError extends Error {
5
+ readonly code: string;
6
+ readonly details: {
7
+ invalidParams: string[];
8
+ mediaType: string;
9
+ suggestions?: string[];
10
+ validParams: string[];
11
+ };
12
+ /**
13
+ * Creates a new ValidationError instance with detailed error information.
14
+ * @param message Human-readable error message describing the validation failure
15
+ * @param code Machine-readable error code for programmatic error handling
16
+ * @param mediaType The media type where validation failed ('image', 'video', or 'audio')
17
+ * @param invalidParameters Array of parameter names that were invalid
18
+ * @param validParameters Array of valid parameter names or values
19
+ * @param suggestions Optional array of suggested corrections or valid values
20
+ */
21
+ constructor(message: string, code: string, mediaType: string, invalidParameters: string[], validParameters: string[], suggestions?: string[]);
22
+ }
23
+ export default ValidationError;
@@ -0,0 +1,145 @@
1
+ import type { BaseStorage } from "../storage/storage.d.ts";
2
+ import type { File, FileReturn } from "../storage/utils/index.d.ts";
3
+ import BaseTransformer from "./base-transformer.d.ts";
4
+ import type { VideoCropOptions, VideoResizeOptions, VideoRotateOptions, VideoTransformationStep, VideoTransformerConfig, VideoTransformOptions, VideoTransformResult } from "./types.d.ts";
5
+ /**
6
+ * Video transformer that uses storage backends and Mediabunny library to retrieve and transform videos.
7
+ * @template TFile The file type used by this transformer.
8
+ * @template TFileReturn The return type for file retrieval operations.
9
+ *
10
+ * Supports video transformations with query parameters for on-demand video processing.
11
+ * @example
12
+ * ```ts
13
+ * const transformer = new VideoTransformer(storage, {
14
+ * maxVideoSize: 100 * 1024 * 1024, // 100MB
15
+ * cache: new Map()
16
+ * });
17
+ *
18
+ * // Programmatic usage - resize a video
19
+ * const result = await transformer.resize('video-id', {
20
+ * width: 1920,
21
+ * height: 1080,
22
+ * fit: 'contain'
23
+ * });
24
+ *
25
+ * // URL-based transformations
26
+ * // GET /files/video-id?width=1280&height=720&fit=cover&bitrate=2000000
27
+ * // GET /files/video-id?codec=vp9&bitrate=1000000
28
+ * ```
29
+ *
30
+ * ## Supported Query Parameters
31
+ *
32
+ * - `width`: Width in pixels (Number)
33
+ * - `height`: Height in pixels (Number)
34
+ * - `fit`: Resize fit mode - cover/contain/fill/inside/outside
35
+ * - `codec`: Video codec - avc/hevc/vp8/vp9/av1
36
+ * - `bitrate`: Video bitrate in bits per second (Number)
37
+ * - `frameRate`: Frame rate in Hz (Number)
38
+ * - `format`: Output format - mp4/webm/mkv/ogg
39
+ */
40
+ declare class VideoTransformer<TFile extends File = File, TFileReturn extends FileReturn = FileReturn> extends BaseTransformer<VideoTransformerConfig, VideoTransformResult<TFileReturn>, TFile, TFileReturn> {
41
+ /**
42
+ * Creates a new VideoTransformer instance.
43
+ * @param storage The storage backend for retrieving and storing video files.
44
+ * @param config Configuration options for video transformation including cache settings, codec defaults, and size limits.
45
+ */
46
+ constructor(storage: BaseStorage<TFile, TFileReturn>, config?: VideoTransformerConfig);
47
+ /**
48
+ * Resizes a video to specified dimensions with optional fit mode.
49
+ * @param fileId Unique identifier of the video file to resize.
50
+ * @param options Resize options including width, height, and fit mode.
51
+ * @returns Promise resolving to transformed video result.
52
+ */
53
+ resize(fileId: string, options: VideoResizeOptions): Promise<VideoTransformResult<TFileReturn>>;
54
+ /**
55
+ * Crops a video to the specified region.
56
+ * @param fileId Unique identifier of the video file to crop.
57
+ * @param options Crop options including region coordinates.
58
+ * @returns Promise resolving to transformed video result.
59
+ */
60
+ crop(fileId: string, options: VideoCropOptions): Promise<VideoTransformResult<TFileReturn>>;
61
+ /**
62
+ * Rotates a video by the specified angle.
63
+ * @param fileId Unique identifier of the video file to rotate.
64
+ * @param options Rotation configuration with angle in degrees and optional background color.
65
+ * @returns Promise resolving to transformed video result.
66
+ */
67
+ rotate(fileId: string, options: VideoRotateOptions): Promise<VideoTransformResult<TFileReturn>>;
68
+ /**
69
+ * Converts video format or codec.
70
+ * @param fileId Unique identifier of the video file to convert.
71
+ * @param format Target video format (e.g., "mp4", "webm", "mkv").
72
+ * @param options Video transformation options including quality, bitrate, and codec settings.
73
+ * @returns Promise resolving to transformed video result with updated format metadata.
74
+ */
75
+ convertFormat(fileId: string, format: string, options?: VideoTransformOptions): Promise<VideoTransformResult<TFileReturn>>;
76
+ /**
77
+ * Transcodes video to different codec.
78
+ * @param fileId Unique identifier of the video file to transcode.
79
+ * @param codec Target video codec (e.g., "avc", "hevc", "vp9").
80
+ * @param options Additional transform options.
81
+ * @returns Promise resolving to transformed video result.
82
+ */
83
+ transcode(fileId: string, codec: VideoTransformOptions["codec"], options?: Omit<VideoTransformOptions, "codec">): Promise<VideoTransformResult<TFileReturn>>;
84
+ /**
85
+ * Applies a custom transformation pipeline to a video.
86
+ * @param fileId Unique identifier of the video file to transform.
87
+ * @param steps Array of transformation steps to apply in sequence.
88
+ * @returns Promise resolving to transformed video result.
89
+ */
90
+ transform(fileId: string, steps: VideoTransformationStep[]): Promise<VideoTransformResult<TFileReturn>>;
91
+ /**
92
+ * Applies multiple transformations in sequence using Mediabunny.
93
+ * @param buffer The original video buffer.
94
+ * @param steps Array of video transformation steps to apply.
95
+ * @returns Promise resolving to transformed video buffer.
96
+ * @private
97
+ */
98
+ private applyTransformations;
99
+ /**
100
+ * Converts transformation steps to Mediabunny video options.
101
+ * @param steps Array of video transformation steps to convert.
102
+ * @returns Mediabunny video options object for conversion.
103
+ * @private
104
+ */
105
+ private stepsToVideoOptions;
106
+ /**
107
+ * Determines output format based on transformation steps.
108
+ * @param steps Array of video transformation steps.
109
+ * @returns Mediabunny output format instance (defaults to MP4).
110
+ * @private
111
+ */
112
+ private determineOutputFormat;
113
+ /**
114
+ * Converts format string to Mediabunny output format.
115
+ * @param format Video format string (mp4, webm, mkv, ogg).
116
+ * @returns Mediabunny output format instance.
117
+ * @private
118
+ */
119
+ private formatStringToOutputFormat;
120
+ /**
121
+ * Validates that the file is a supported video.
122
+ * @param file The file to validate.
123
+ * @returns Promise that resolves if validation passes.
124
+ * @throws Error if file size exceeds limits, wrong content type, unsupported format, or invalid video.
125
+ * @private
126
+ */
127
+ private validateVideo;
128
+ /**
129
+ * Creates transformation result with metadata.
130
+ * @param buffer The transformed video buffer.
131
+ * @param originalFile The original file information.
132
+ * @returns Video transformation result with metadata.
133
+ * @private
134
+ */
135
+ private createTransformResult;
136
+ /**
137
+ * Generates cache key for transformation.
138
+ * @param fileId The file identifier.
139
+ * @param steps Array of transformation steps.
140
+ * @returns Unique cache key string.
141
+ * @private
142
+ */
143
+ private generateCacheKey;
144
+ }
145
+ export default VideoTransformer;
@@ -0,0 +1,310 @@
1
+ import { Input, BufferSource, ALL_FORMATS, Output, BufferTarget, Conversion, Mp4OutputFormat, WebMOutputFormat, OggOutputFormat, MkvOutputFormat } from 'mediabunny';
2
+ import BaseTransformer from '../packem_shared/BaseTransformer-C2gLib6v.js';
3
+ import { isValidMediaType, getFormatFromContentType } from '../packem_shared/isValidMediaType-BeDgiObq.js';
4
+
5
+ class VideoTransformer extends BaseTransformer {
6
+ /**
7
+ * Creates a new VideoTransformer instance.
8
+ * @param storage The storage backend for retrieving and storing video files.
9
+ * @param config Configuration options for video transformation including cache settings, codec defaults, and size limits.
10
+ */
11
+ constructor(storage, config = {}) {
12
+ const logger = config.logger || storage.logger;
13
+ const transformerConfig = {
14
+ cacheTtl: 3600,
15
+ // 1 hour
16
+ defaultBitrate: 2e6,
17
+ // 2 Mbps
18
+ defaultCodec: "avc",
19
+ // No cache provided - no caching
20
+ maxCacheSize: 50,
21
+ // Max 50 transformed videos in cache
22
+ maxVideoSize: 500 * 1024 * 1024,
23
+ // 500MB
24
+ supportedFormats: ["mp4", "webm", "mkv", "avi", "mov", "flv", "wmv"],
25
+ ...config
26
+ };
27
+ super(storage, transformerConfig, logger);
28
+ }
29
+ /**
30
+ * Resizes a video to specified dimensions with optional fit mode.
31
+ * @param fileId Unique identifier of the video file to resize.
32
+ * @param options Resize options including width, height, and fit mode.
33
+ * @returns Promise resolving to transformed video result.
34
+ */
35
+ async resize(fileId, options) {
36
+ return this.transform(fileId, [{ options, type: "resize" }]);
37
+ }
38
+ /**
39
+ * Crops a video to the specified region.
40
+ * @param fileId Unique identifier of the video file to crop.
41
+ * @param options Crop options including region coordinates.
42
+ * @returns Promise resolving to transformed video result.
43
+ */
44
+ async crop(fileId, options) {
45
+ return this.transform(fileId, [{ options, type: "crop" }]);
46
+ }
47
+ /**
48
+ * Rotates a video by the specified angle.
49
+ * @param fileId Unique identifier of the video file to rotate.
50
+ * @param options Rotation configuration with angle in degrees and optional background color.
51
+ * @returns Promise resolving to transformed video result.
52
+ */
53
+ async rotate(fileId, options) {
54
+ return this.transform(fileId, [{ options, type: "rotate" }]);
55
+ }
56
+ /**
57
+ * Converts video format or codec.
58
+ * @param fileId Unique identifier of the video file to convert.
59
+ * @param format Target video format (e.g., "mp4", "webm", "mkv").
60
+ * @param options Video transformation options including quality, bitrate, and codec settings.
61
+ * @returns Promise resolving to transformed video result with updated format metadata.
62
+ */
63
+ async convertFormat(fileId, format, options = {}) {
64
+ return this.transform(fileId, [{ options: { ...options, format }, type: "format" }]);
65
+ }
66
+ /**
67
+ * Transcodes video to different codec.
68
+ * @param fileId Unique identifier of the video file to transcode.
69
+ * @param codec Target video codec (e.g., "avc", "hevc", "vp9").
70
+ * @param options Additional transform options.
71
+ * @returns Promise resolving to transformed video result.
72
+ */
73
+ async transcode(fileId, codec, options = {}) {
74
+ return this.transform(fileId, [{ options: { ...options, codec }, type: "codec" }]);
75
+ }
76
+ /**
77
+ * Applies a custom transformation pipeline to a video.
78
+ * @param fileId Unique identifier of the video file to transform.
79
+ * @param steps Array of transformation steps to apply in sequence.
80
+ * @returns Promise resolving to transformed video result.
81
+ */
82
+ async transform(fileId, steps) {
83
+ const fileQuery = { id: fileId };
84
+ const cacheKey = this.generateCacheKey(fileId, steps);
85
+ if (this.cache) {
86
+ const cached = await Promise.resolve(this.cache.get(cacheKey));
87
+ if (cached) {
88
+ this.logger?.debug("Returning cached transformed video for %s", fileId);
89
+ return cached;
90
+ }
91
+ }
92
+ const originalFile = await this.storage.get(fileQuery);
93
+ await this.validateVideo(originalFile);
94
+ const transformedBuffer = await this.applyTransformations(originalFile.content, steps);
95
+ const result = await this.createTransformResult(transformedBuffer, originalFile);
96
+ if (this.cache) {
97
+ this.cache.set(cacheKey, result);
98
+ }
99
+ return result;
100
+ }
101
+ /**
102
+ * Applies multiple transformations in sequence using Mediabunny.
103
+ * @param buffer The original video buffer.
104
+ * @param steps Array of video transformation steps to apply.
105
+ * @returns Promise resolving to transformed video buffer.
106
+ * @private
107
+ */
108
+ async applyTransformations(buffer, steps) {
109
+ const input = new Input({
110
+ formats: ALL_FORMATS,
111
+ source: new BufferSource(buffer)
112
+ });
113
+ const outputFormat = this.determineOutputFormat(steps);
114
+ const output = new Output({
115
+ format: outputFormat,
116
+ target: new BufferTarget()
117
+ });
118
+ const videoOptions = this.stepsToVideoOptions(steps);
119
+ const conversion = await Conversion.init({
120
+ input,
121
+ output,
122
+ video: videoOptions
123
+ });
124
+ if (!conversion.isValid) {
125
+ const reasons = conversion.discardedTracks.map((track) => track.reason).join(", ");
126
+ throw new Error(`Video transformation failed: ${reasons}`);
127
+ }
128
+ await conversion.execute();
129
+ return Buffer.from(output.target.buffer);
130
+ }
131
+ /**
132
+ * Converts transformation steps to Mediabunny video options.
133
+ * @param steps Array of video transformation steps to convert.
134
+ * @returns Mediabunny video options object for conversion.
135
+ * @private
136
+ */
137
+ // eslint-disable-next-line class-methods-use-this
138
+ stepsToVideoOptions(steps) {
139
+ const options = {};
140
+ for (const step of steps) {
141
+ switch (step.type) {
142
+ case "bitrate": {
143
+ const bitrateOptions = step.options;
144
+ if (bitrateOptions.bitrate) {
145
+ options.bitrate = bitrateOptions.bitrate;
146
+ }
147
+ break;
148
+ }
149
+ case "codec": {
150
+ const codecOptions = step.options;
151
+ if (codecOptions.codec) {
152
+ options.codec = codecOptions.codec;
153
+ }
154
+ if (codecOptions.bitrate) {
155
+ options.bitrate = codecOptions.bitrate;
156
+ }
157
+ break;
158
+ }
159
+ case "crop": {
160
+ const cropOptions = step.options;
161
+ options.crop = {
162
+ height: cropOptions.height,
163
+ left: cropOptions.left,
164
+ top: cropOptions.top,
165
+ width: cropOptions.width
166
+ };
167
+ break;
168
+ }
169
+ case "frameRate": {
170
+ const frameRateOptions = step.options;
171
+ if (frameRateOptions.frameRate) {
172
+ options.frameRate = frameRateOptions.frameRate;
173
+ }
174
+ break;
175
+ }
176
+ case "resize": {
177
+ const resizeOptions = step.options;
178
+ options.width = resizeOptions.width;
179
+ options.height = resizeOptions.height;
180
+ options.fit = resizeOptions.fit;
181
+ if (resizeOptions.position !== void 0) {
182
+ options.position = resizeOptions.position;
183
+ }
184
+ break;
185
+ }
186
+ case "rotate": {
187
+ const rotateOptions = step.options;
188
+ options.rotate = rotateOptions.angle;
189
+ if (rotateOptions.background) {
190
+ options.background = rotateOptions.background;
191
+ }
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ return options;
197
+ }
198
+ /**
199
+ * Determines output format based on transformation steps.
200
+ * @param steps Array of video transformation steps.
201
+ * @returns Mediabunny output format instance (defaults to MP4).
202
+ * @private
203
+ */
204
+ determineOutputFormat(steps) {
205
+ for (const step of steps) {
206
+ if (step.type === "format" && step.options.format) {
207
+ return this.formatStringToOutputFormat(step.options.format);
208
+ }
209
+ }
210
+ return new Mp4OutputFormat();
211
+ }
212
+ /**
213
+ * Converts format string to Mediabunny output format.
214
+ * @param format Video format string (mp4, webm, mkv, ogg).
215
+ * @returns Mediabunny output format instance.
216
+ * @private
217
+ */
218
+ // eslint-disable-next-line class-methods-use-this
219
+ formatStringToOutputFormat(format) {
220
+ switch (format.toLowerCase()) {
221
+ case "mkv": {
222
+ return new MkvOutputFormat();
223
+ }
224
+ case "mp4": {
225
+ return new Mp4OutputFormat();
226
+ }
227
+ case "ogg": {
228
+ return new OggOutputFormat();
229
+ }
230
+ case "webm": {
231
+ return new WebMOutputFormat();
232
+ }
233
+ default: {
234
+ return new Mp4OutputFormat();
235
+ }
236
+ }
237
+ }
238
+ /**
239
+ * Validates that the file is a supported video.
240
+ * @param file The file to validate.
241
+ * @returns Promise that resolves if validation passes.
242
+ * @throws Error if file size exceeds limits, wrong content type, unsupported format, or invalid video.
243
+ * @private
244
+ */
245
+ async validateVideo(file) {
246
+ const fileSize = typeof file.size === "string" ? Number.parseInt(file.size, 10) : file.size;
247
+ if (this.config?.maxVideoSize && fileSize > this.config.maxVideoSize) {
248
+ throw new Error(`Video size ${fileSize} exceeds maximum allowed size ${this.config.maxVideoSize}`);
249
+ }
250
+ if (!isValidMediaType(file.contentType, "video")) {
251
+ throw new Error(`File is not a video: ${file.contentType}`);
252
+ }
253
+ const format = getFormatFromContentType(file.contentType);
254
+ if (this.config?.supportedFormats && format && !this.config.supportedFormats.includes(format)) {
255
+ throw new Error(`Unsupported video format: ${format}`);
256
+ }
257
+ try {
258
+ const input = new Input({
259
+ formats: ALL_FORMATS,
260
+ source: new BufferSource(file.content)
261
+ });
262
+ const primaryTrack = await input.getPrimaryVideoTrack();
263
+ if (!primaryTrack) {
264
+ throw new Error("No video track found");
265
+ }
266
+ } catch (error) {
267
+ throw new Error(`Invalid video file: ${error}`);
268
+ }
269
+ }
270
+ /**
271
+ * Creates transformation result with metadata.
272
+ * @param buffer The transformed video buffer.
273
+ * @param originalFile The original file information.
274
+ * @returns Video transformation result with metadata.
275
+ * @private
276
+ */
277
+ async createTransformResult(buffer, originalFile) {
278
+ const input = new Input({
279
+ formats: ALL_FORMATS,
280
+ source: new BufferSource(buffer)
281
+ });
282
+ const videoTrack = await input.getPrimaryVideoTrack();
283
+ const duration = await input.computeDuration();
284
+ return {
285
+ bitrate: this.config.defaultBitrate,
286
+ buffer,
287
+ duration,
288
+ format: "mp4",
289
+ // Default, would need to detect actual format
290
+ height: videoTrack?.displayHeight || 0,
291
+ originalFile,
292
+ size: buffer.length,
293
+ width: videoTrack?.displayWidth || 0
294
+ };
295
+ }
296
+ /**
297
+ * Generates cache key for transformation.
298
+ * @param fileId The file identifier.
299
+ * @param steps Array of transformation steps.
300
+ * @returns Unique cache key string.
301
+ * @private
302
+ */
303
+ // eslint-disable-next-line class-methods-use-this
304
+ generateCacheKey(fileId, steps) {
305
+ const stepsKey = steps.map((step) => `${step.type}:${JSON.stringify(step.options)}`).join("|");
306
+ return `${fileId}:${stepsKey}`;
307
+ }
308
+ }
309
+
310
+ export { VideoTransformer as default };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Simple cache interface that any cache implementation can follow
3
+ */
4
+ export interface Cache<K = string, V = unknown> {
5
+ /** Clear all cache entries */
6
+ clear: () => void | Promise<void>;
7
+ /** Delete a value from cache */
8
+ delete: (key: K) => boolean | Promise<boolean>;
9
+ /** Get a value from cache */
10
+ get: (key: K) => V | undefined | Promise<V | undefined>;
11
+ /** Check if a key exists in cache */
12
+ has: (key: K) => boolean | Promise<boolean>;
13
+ /** Set a value in cache */
14
+ set: (key: K, value: V, options?: CacheOptions) => boolean | Promise<boolean>;
15
+ }
16
+ /**
17
+ * Options for cache operations
18
+ */
19
+ export interface CacheOptions {
20
+ /** TTL in milliseconds */
21
+ ttl?: number;
22
+ }
23
+ /**
24
+ * No-op cache implementation that does nothing (used when caching is disabled)
25
+ */
26
+ export declare class NoOpCache<K = string, V = unknown> implements Cache<K, V> {
27
+ get(): undefined;
28
+ set(): boolean;
29
+ delete(): boolean;
30
+ clear(): void;
31
+ has(): boolean;
32
+ }
@@ -0,0 +1,65 @@
1
+ import type { UploadFile } from "../storage/utils/index.d.ts";
2
+ /**
3
+ * Chunk information structure for tracking uploaded chunks.
4
+ */
5
+ export interface ChunkInfo {
6
+ /** Optional checksum for validation */
7
+ checksum?: string;
8
+ /** Length of chunk in bytes */
9
+ length: number;
10
+ /** Byte offset where chunk starts */
11
+ offset: number;
12
+ }
13
+ /**
14
+ * Checks if upload is complete based on chunks coverage.
15
+ * Verifies that all chunks form a continuous sequence covering the total file size.
16
+ * @param chunks Array of chunk information objects
17
+ * @param totalSize Total expected file size in bytes
18
+ * @returns True if upload is complete, false otherwise
19
+ */
20
+ export declare const isUploadComplete: (chunks: ChunkInfo[], totalSize: number) => boolean;
21
+ /**
22
+ * Validates a chunk against file constraints.
23
+ * @param chunkOffset Byte offset where chunk starts
24
+ * @param chunkLength Length of chunk in bytes
25
+ * @param totalSize Total file size in bytes
26
+ * @param maxChunkSize Maximum allowed chunk size (optional)
27
+ * @throws {Error} If validation fails
28
+ */
29
+ export declare const validateChunk: (chunkOffset: number, chunkLength: number, totalSize: number, maxChunkSize?: number) => void;
30
+ /**
31
+ * Tracks a chunk in the metadata chunks array (idempotent).
32
+ * @param chunks Existing chunks array
33
+ * @param chunkInfo New chunk information to track
34
+ * @returns Updated chunks array
35
+ */
36
+ export declare const trackChunk: (chunks: ChunkInfo[], chunkInfo: ChunkInfo) => ChunkInfo[];
37
+ /**
38
+ * Calculates upload progress based on chunks.
39
+ * @param chunks Array of chunk information objects
40
+ * @param totalSize Total expected file size in bytes
41
+ * @returns Object with bytesWritten and completion percentage
42
+ */
43
+ export declare const calculateUploadProgress: (chunks: ChunkInfo[], totalSize: number) => {
44
+ bytesWritten: number;
45
+ percentage: number;
46
+ };
47
+ /**
48
+ * Gets the total bytes written based on file metadata.
49
+ * For chunked uploads, uses bytesWritten from file or calculates from chunks.
50
+ * @param file The file object
51
+ * @returns Total bytes written
52
+ */
53
+ export declare const getBytesWritten: <TFile extends UploadFile>(file: TFile) => number;
54
+ /**
55
+ * Checks if a file is a chunked upload based on metadata.
56
+ * @param file The file object
57
+ * @returns True if file is a chunked upload
58
+ */
59
+ export declare const isChunkedUpload: <TFile extends UploadFile>(file: TFile) => boolean;
60
+ /**
61
+ * Gets the total size for a chunked upload.
62
+ * @param file The file object
63
+ * @returns Total size or undefined if not a chunked upload
64
+ */
65
+ export declare const getTotalSize: <TFile extends UploadFile>(file: TFile) => number | undefined;
@@ -0,0 +1,28 @@
1
+ import type { Readable } from "node:stream";
2
+ /**
3
+ * Detects the file type from a buffer using magic numbers (binary signatures).
4
+ * This provides more accurate file type detection than relying on file extensions or Content-Type headers.
5
+ * @param buffer The buffer to detect file type from
6
+ * @returns The detected file type with extension and MIME type, or undefined if detection fails
7
+ */
8
+ export declare const detectFileTypeFromBuffer: (buffer: Buffer | Uint8Array) => Promise<{
9
+ ext: string;
10
+ mime: string;
11
+ } | undefined>;
12
+ /**
13
+ * Detects file type from a Node.js stream by peeking at the first chunk and returns both the detected type and a new stream.
14
+ * This peeks at the stream data without consuming it, allowing the original stream to be used normally.
15
+ * @param stream The readable stream to detect file type from
16
+ * @param options Optional configuration for file type detection
17
+ * @param options.sampleSize The sample size in bytes to peek (default: 4100)
18
+ * @returns An object with the detected file type and a new readable stream
19
+ */
20
+ export declare const detectFileTypeFromStream: (stream: Readable, options?: {
21
+ sampleSize?: number;
22
+ }) => Promise<{
23
+ fileType?: {
24
+ ext: string;
25
+ mime: string;
26
+ };
27
+ stream: Readable;
28
+ }>;
@@ -0,0 +1,74 @@
1
+ import type { HttpError } from "./types.d.ts";
2
+ /**
3
+ * Canonical error codes used across handlers and storage adapters.
4
+ * These codes map to standardized HTTP status codes and error messages.
5
+ */
6
+ export declare enum ERRORS {
7
+ BAD_REQUEST = "BadRequest",
8
+ CHECKSUM_MISMATCH = "ChecksumMismatch",
9
+ FILE_CONFLICT = "FileConflict",
10
+ FILE_ERROR = "FileError",
11
+ FILE_LOCKED = "FileLocked",
12
+ FILE_NOT_ALLOWED = "FileNotAllowed",
13
+ FILE_NOT_FOUND = "FileNotFound",
14
+ FORBIDDEN = "Forbidden",
15
+ GONE = "Gone",
16
+ INVALID_FILE_NAME = "InvalidFileName",
17
+ INVALID_FILE_SIZE = "InvalidFileSize",
18
+ INVALID_RANGE = "InvalidRange",
19
+ INVALID_TYPE = "Invalidtype",
20
+ METHOD_NOT_ALLOWED = "MethodNotAllowed",
21
+ REQUEST_ABORTED = "RequestAborted",
22
+ REQUEST_ENTITY_TOO_LARGE = "RequestEntityTooLarge",
23
+ STORAGE_BUSY = "StorageBusy",
24
+ STORAGE_ERROR = "StorageError",
25
+ TOO_MANY_REQUESTS = "TooManyRequests",
26
+ UNKNOWN_ERROR = "UnknownError",
27
+ UNPROCESSABLE_ENTITY = "UnprocessableEntity",
28
+ UNSUPPORTED_CHECKSUM_ALGORITHM = "UnsupportedChecksumAlgorithm",
29
+ UNSUPPORTED_MEDIA_TYPE = "UnsupportedMediaType"
30
+ }
31
+ /**
32
+ * Type mapping of error codes to standardized HTTP error responses.
33
+ * @template T - The error code type (defaults to string)
34
+ */
35
+ export type ErrorResponses<T extends string = string> = {
36
+ [K in T]: HttpError;
37
+ };
38
+ /**
39
+ * Mapping of error codes to HttpError response objects.
40
+ * @returns Map of error codes to standardized HTTP error responses
41
+ */
42
+ export declare const ErrorMap: ErrorResponses<ERRORS>;
43
+ /**
44
+ * Error subclass carrying a stable error code and optional detail.
45
+ * Provides structured error information for upload operations.
46
+ */
47
+ export declare class UploadError extends Error {
48
+ name: string;
49
+ /** The standardized error code from the ERRORS enum */
50
+ UploadErrorCode: ERRORS;
51
+ /** Optional additional error details */
52
+ detail?: unknown;
53
+ /**
54
+ * Creates a new UploadError instance.
55
+ * @param code Standardized error code (defaults to UNKNOWN_ERROR)
56
+ * @param message Human-readable error message (defaults to the code)
57
+ * @param detail Optional additional error details
58
+ */
59
+ constructor(code?: ERRORS, message?: string, detail?: unknown);
60
+ }
61
+ /**
62
+ * Type guard to check if an error is an UploadError instance.
63
+ * @param error Error to check
64
+ * @returns True if the error is an UploadError with a valid error code
65
+ */
66
+ export declare const isUploadError: (error: unknown) => error is UploadError;
67
+ /**
68
+ * Convenience function to throw an UploadError from a string error code.
69
+ * Looks up the appropriate error message from ErrorMap.
70
+ * @param UploadErrorCode String error code to convert to UploadError
71
+ * @param detail Optional additional error details
72
+ * @throws UploadError with the specified code and message
73
+ */
74
+ export declare const throwErrorCode: (UploadErrorCode: ERRORS | string, detail?: string) => never;