@maas/payload-plugin-media-cloud 0.0.0

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 (87) hide show
  1. package/LICENSE +8 -0
  2. package/dist/adapter/handleDelete.d.ts +20 -0
  3. package/dist/adapter/handleDelete.js +70 -0
  4. package/dist/adapter/handleDelete.js.map +1 -0
  5. package/dist/adapter/handleUpload.d.ts +12 -0
  6. package/dist/adapter/handleUpload.js +29 -0
  7. package/dist/adapter/handleUpload.js.map +1 -0
  8. package/dist/adapter/staticHandler.d.ts +17 -0
  9. package/dist/adapter/staticHandler.js +64 -0
  10. package/dist/adapter/staticHandler.js.map +1 -0
  11. package/dist/adapter/storageAdapter.d.ts +23 -0
  12. package/dist/adapter/storageAdapter.js +30 -0
  13. package/dist/adapter/storageAdapter.js.map +1 -0
  14. package/dist/collections/mediaCollection.d.ts +16 -0
  15. package/dist/collections/mediaCollection.js +139 -0
  16. package/dist/collections/mediaCollection.js.map +1 -0
  17. package/dist/components/index.d.ts +4 -0
  18. package/dist/components/index.js +5 -0
  19. package/dist/components/mux-preview/index.d.ts +2 -0
  20. package/dist/components/mux-preview/index.js +3 -0
  21. package/dist/components/mux-preview/mux-preview.d.ts +14 -0
  22. package/dist/components/mux-preview/mux-preview.js +38 -0
  23. package/dist/components/mux-preview/mux-preview.js.map +1 -0
  24. package/dist/components/upload-handler/index.d.ts +2 -0
  25. package/dist/components/upload-handler/index.js +3 -0
  26. package/dist/components/upload-handler/upload-handler.d.ts +22 -0
  27. package/dist/components/upload-handler/upload-handler.js +178 -0
  28. package/dist/components/upload-handler/upload-handler.js.map +1 -0
  29. package/dist/components/upload-manager/index.d.ts +2 -0
  30. package/dist/components/upload-manager/index.js +3 -0
  31. package/dist/components/upload-manager/upload-manager-DN4RrmYB.css +204 -0
  32. package/dist/components/upload-manager/upload-manager-DN4RrmYB.css.map +1 -0
  33. package/dist/components/upload-manager/upload-manager.css +201 -0
  34. package/dist/components/upload-manager/upload-manager.d.ts +42 -0
  35. package/dist/components/upload-manager/upload-manager.js +315 -0
  36. package/dist/components/upload-manager/upload-manager.js.map +1 -0
  37. package/dist/components/upload-manager/upload-manager2.js +0 -0
  38. package/dist/endpoints/muxAssetHandler.d.ts +11 -0
  39. package/dist/endpoints/muxAssetHandler.js +59 -0
  40. package/dist/endpoints/muxAssetHandler.js.map +1 -0
  41. package/dist/endpoints/muxCreateUploadHandler.d.ts +13 -0
  42. package/dist/endpoints/muxCreateUploadHandler.js +40 -0
  43. package/dist/endpoints/muxCreateUploadHandler.js.map +1 -0
  44. package/dist/endpoints/muxWebhookHandler.d.ts +11 -0
  45. package/dist/endpoints/muxWebhookHandler.js +49 -0
  46. package/dist/endpoints/muxWebhookHandler.js.map +1 -0
  47. package/dist/hooks/useEmitter.d.ts +48 -0
  48. package/dist/hooks/useEmitter.js +19 -0
  49. package/dist/hooks/useEmitter.js.map +1 -0
  50. package/dist/hooks/useErrorHandler.d.ts +11 -0
  51. package/dist/hooks/useErrorHandler.js +19 -0
  52. package/dist/hooks/useErrorHandler.js.map +1 -0
  53. package/dist/index.d.ts +3 -0
  54. package/dist/index.js +3 -0
  55. package/dist/plugin.d.ts +15 -0
  56. package/dist/plugin.js +242 -0
  57. package/dist/plugin.js.map +1 -0
  58. package/dist/tus/stores/s3/expiration-manager.d.ts +36 -0
  59. package/dist/tus/stores/s3/expiration-manager.js +76 -0
  60. package/dist/tus/stores/s3/expiration-manager.js.map +1 -0
  61. package/dist/tus/stores/s3/file-operations.d.ts +66 -0
  62. package/dist/tus/stores/s3/file-operations.js +90 -0
  63. package/dist/tus/stores/s3/file-operations.js.map +1 -0
  64. package/dist/tus/stores/s3/log.d.ts +5 -0
  65. package/dist/tus/stores/s3/log.js +8 -0
  66. package/dist/tus/stores/s3/log.js.map +1 -0
  67. package/dist/tus/stores/s3/metadata-manager.d.ts +85 -0
  68. package/dist/tus/stores/s3/metadata-manager.js +135 -0
  69. package/dist/tus/stores/s3/metadata-manager.js.map +1 -0
  70. package/dist/tus/stores/s3/parts-manager.d.ts +130 -0
  71. package/dist/tus/stores/s3/parts-manager.js +328 -0
  72. package/dist/tus/stores/s3/parts-manager.js.map +1 -0
  73. package/dist/tus/stores/s3/s3-store.d.ts +110 -0
  74. package/dist/tus/stores/s3/s3-store.js +342 -0
  75. package/dist/tus/stores/s3/s3-store.js.map +1 -0
  76. package/dist/tus/stores/s3/semaphore.d.ts +16 -0
  77. package/dist/tus/stores/s3/semaphore.js +32 -0
  78. package/dist/tus/stores/s3/semaphore.js.map +1 -0
  79. package/dist/types/errors.d.ts +26 -0
  80. package/dist/types/errors.js +28 -0
  81. package/dist/types/errors.js.map +1 -0
  82. package/dist/types/index.d.ts +73 -0
  83. package/dist/types/index.js +0 -0
  84. package/dist/utils/file.d.ts +30 -0
  85. package/dist/utils/file.js +84 -0
  86. package/dist/utils/file.js.map +1 -0
  87. package/package.json +92 -0
@@ -0,0 +1,11 @@
1
+ import { MediaCloudError } from "../types/errors.js";
2
+ import { ErrorLevel } from "@maas/error-handler";
3
+
4
+ //#region src/hooks/useErrorHandler.d.ts
5
+ declare function useErrorHandler(): {
6
+ logError: (errorKey: MediaCloudError, level?: ErrorLevel) => void;
7
+ throwError: (errorKey: MediaCloudError) => never;
8
+ };
9
+ //#endregion
10
+ export { useErrorHandler };
11
+ //# sourceMappingURL=useErrorHandler.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { MediaCloudError } from "../types/errors.js";
2
+ import { ErrorLevel, createErrorHandler } from "@maas/error-handler";
3
+
4
+ //#region src/hooks/useErrorHandler.ts
5
+ function useErrorHandler() {
6
+ const { logError, throwError } = createErrorHandler({
7
+ prefix: "PLUGIN-MEDIA-CLOUD",
8
+ level: ErrorLevel.ERROR,
9
+ errors: MediaCloudError
10
+ });
11
+ return {
12
+ logError,
13
+ throwError
14
+ };
15
+ }
16
+
17
+ //#endregion
18
+ export { useErrorHandler };
19
+ //# sourceMappingURL=useErrorHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useErrorHandler.js","names":[],"sources":["../../src/hooks/useErrorHandler.ts"],"sourcesContent":["import { createErrorHandler, ErrorLevel } from '@maas/error-handler'\nimport { MediaCloudError } from '../types/errors'\n\nexport function useErrorHandler() {\n const { logError, throwError } = createErrorHandler({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n level: ErrorLevel.ERROR,\n errors: MediaCloudError,\n })\n\n return {\n logError,\n throwError,\n }\n}\n"],"mappings":";;;;AAGA,SAAgB,kBAAkB;CAChC,MAAM,EAAE,UAAU,YAAY,GAAG,mBAAmB;EAClD,QAAQ;EACR,OAAO,WAAW;EAClB,QAAQ;CACT,EAAC;AAEF,QAAO;EACL;EACA;CACD;AACF"}
@@ -0,0 +1,3 @@
1
+ import { MediaCloudPluginOptions } from "./types/index.js";
2
+ import { mediaCloudPlugin } from "./plugin.js";
3
+ export { type MediaCloudPluginOptions, mediaCloudPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { mediaCloudPlugin } from "./plugin.js";
2
+
3
+ export { mediaCloudPlugin };
@@ -0,0 +1,15 @@
1
+ import { MediaCloudPluginOptions } from "./types/index.js";
2
+ import { Config } from "payload";
3
+
4
+ //#region src/plugin.d.ts
5
+
6
+ /**
7
+ * Media Cloud Plugin for Payload CMS
8
+ * Provides file upload capabilities using S3 storage and Mux video processing
9
+ * @param pluginOptions - Configuration options for the plugin
10
+ * @returns A function that configures the Payload config
11
+ */
12
+ declare function mediaCloudPlugin(pluginOptions: MediaCloudPluginOptions): (config: Config) => Config;
13
+ //#endregion
14
+ export { mediaCloudPlugin };
15
+ //# sourceMappingURL=plugin.d.ts.map
package/dist/plugin.js ADDED
@@ -0,0 +1,242 @@
1
+ import { MediaCloudError } from "./types/errors.js";
2
+ import { useErrorHandler } from "./hooks/useErrorHandler.js";
3
+ import { getStorageAdapter } from "./adapter/storageAdapter.js";
4
+ import { getMediaCollection } from "./collections/mediaCollection.js";
5
+ import { getMuxAssetHandler } from "./endpoints/muxAssetHandler.js";
6
+ import { getMuxCreateUploadHandler } from "./endpoints/muxCreateUploadHandler.js";
7
+ import { getMuxWebhookHandler } from "./endpoints/muxWebhookHandler.js";
8
+ import { S3Store } from "./tus/stores/s3/s3-store.js";
9
+ import { generateUniqueFilename } from "./utils/file.js";
10
+ import Mux from "@mux/mux-node";
11
+ import { cloudStoragePlugin } from "@payloadcms/plugin-cloud-storage";
12
+ import { initClientUploads } from "@payloadcms/plugin-cloud-storage/utilities";
13
+ import { getPayload, sanitizeConfig } from "payload";
14
+ import { Server } from "@tus/server";
15
+
16
+ //#region src/plugin.ts
17
+ const { logError, throwError } = useErrorHandler();
18
+ let muxClient = null;
19
+ /**
20
+ * Validates Mux configuration options
21
+ * @param muxConfig - The Mux configuration to validate
22
+ * @throws {Error} When required Mux configuration is missing
23
+ */
24
+ function validateMuxConfig(muxConfig) {
25
+ if (!muxConfig?.tokenId || !muxConfig?.tokenSecret) throwError(MediaCloudError.MUX_CONFIG_MISSING);
26
+ }
27
+ /**
28
+ * Creates a new Mux client instance
29
+ * @param muxConfig - The Mux configuration options
30
+ * @returns A configured Mux client
31
+ */
32
+ function createMuxClient(muxConfig) {
33
+ return new Mux({
34
+ tokenId: muxConfig.tokenId,
35
+ tokenSecret: muxConfig.tokenSecret,
36
+ webhookSecret: muxConfig.webhookSecret
37
+ });
38
+ }
39
+ /**
40
+ * Creates and configures an S3Store instance
41
+ * @param s3Config - The S3 configuration options
42
+ * @returns A configured S3Store instance
43
+ * @throws {Error} When required S3 configuration is missing
44
+ */
45
+ function createS3Store(s3Config) {
46
+ if (!s3Config?.bucket || !s3Config?.region || !s3Config?.accessKeyId || !s3Config?.secretAccessKey) throwError(MediaCloudError.S3_CONFIG_MISSING);
47
+ return new S3Store({ s3ClientConfig: {
48
+ acl: "public-read",
49
+ bucket: s3Config.bucket,
50
+ credentials: {
51
+ accessKeyId: s3Config.accessKeyId,
52
+ secretAccessKey: s3Config.secretAccessKey
53
+ },
54
+ endpoint: s3Config.endpoint,
55
+ forcePathStyle: true,
56
+ region: s3Config.region
57
+ } });
58
+ }
59
+ /**
60
+ * Creates a TUS server instance with S3 storage
61
+ * @param args - The arguments for creating the TUS server
62
+ * @returns A configured TusServer instance
63
+ */
64
+ function createTusServer(args) {
65
+ const { config, s3Store } = args;
66
+ return new Server({
67
+ datastore: s3Store,
68
+ namingFunction: async (req, metadata) => {
69
+ try {
70
+ const payload = await getPayload({ config: sanitizeConfig(config) });
71
+ const { docs } = await payload.find({
72
+ collection: "media",
73
+ where: { filename: { equals: metadata?.filename || "" } }
74
+ });
75
+ if (docs.length > 0) return generateUniqueFilename(metadata?.filename ?? "");
76
+ else return metadata?.filename || generateUniqueFilename("");
77
+ } catch (_error) {
78
+ logError(MediaCloudError.NAMING_FUNCTION_ERROR);
79
+ return metadata?.filename || generateUniqueFilename("");
80
+ }
81
+ },
82
+ path: "/api/uploads"
83
+ });
84
+ }
85
+ /**
86
+ * Creates TUS upload endpoints for file handling
87
+ * @param tusServer - The TUS server instance
88
+ * @returns An array of endpoint configurations
89
+ */
90
+ function createTusEndpoints(tusServer) {
91
+ /**
92
+ * Handles TUS requests through the server
93
+ * @param req - The payload request object
94
+ * @returns The server response
95
+ */
96
+ function tusHandler(req) {
97
+ return tusServer.handleWeb(req);
98
+ }
99
+ return [
100
+ {
101
+ handler: tusHandler,
102
+ method: "options",
103
+ path: "/uploads"
104
+ },
105
+ {
106
+ handler: tusHandler,
107
+ method: "post",
108
+ path: "/uploads"
109
+ },
110
+ {
111
+ handler: tusHandler,
112
+ method: "get",
113
+ path: "/uploads/:id"
114
+ },
115
+ {
116
+ handler: tusHandler,
117
+ method: "put",
118
+ path: "/uploads/:id"
119
+ },
120
+ {
121
+ handler: tusHandler,
122
+ method: "patch",
123
+ path: "/uploads/:id"
124
+ },
125
+ {
126
+ handler: tusHandler,
127
+ method: "delete",
128
+ path: "/uploads/:id"
129
+ }
130
+ ];
131
+ }
132
+ /**
133
+ * Creates Mux-related endpoints for asset handling
134
+ * @param args - The arguments for creating Mux endpoints
135
+ * @returns An array of Mux endpoint configurations
136
+ */
137
+ function createMuxEndpoints(args) {
138
+ const { getMuxClient, pluginOptions } = args;
139
+ return [
140
+ {
141
+ handler: getMuxWebhookHandler({ getMuxClient }),
142
+ method: "get",
143
+ path: "/mux/webhook"
144
+ },
145
+ {
146
+ handler: getMuxCreateUploadHandler({
147
+ getMuxClient,
148
+ pluginOptions
149
+ }),
150
+ method: "post",
151
+ path: "/mux/upload"
152
+ },
153
+ {
154
+ handler: getMuxAssetHandler({ getMuxClient }),
155
+ method: "get",
156
+ path: "/mux/asset"
157
+ }
158
+ ];
159
+ }
160
+ /**
161
+ * Media Cloud Plugin for Payload CMS
162
+ * Provides file upload capabilities using S3 storage and Mux video processing
163
+ * @param pluginOptions - Configuration options for the plugin
164
+ * @returns A function that configures the Payload config
165
+ */
166
+ function mediaCloudPlugin(pluginOptions) {
167
+ return function(config) {
168
+ if (!pluginOptions) {
169
+ logError(MediaCloudError.PLUGIN_NOT_CONFIGURED);
170
+ return config;
171
+ }
172
+ const isPluginDisabled = pluginOptions.enabled === false;
173
+ /**
174
+ * Gets or creates a Mux client instance
175
+ * @returns The Mux client instance
176
+ */
177
+ function getMuxClient() {
178
+ if (muxClient) return muxClient;
179
+ return createMuxClient(pluginOptions.mux);
180
+ }
181
+ if (pluginOptions.mux) {
182
+ validateMuxConfig(pluginOptions.mux);
183
+ muxClient = getMuxClient();
184
+ } else {
185
+ logError(MediaCloudError.MUX_CONFIG_INCOMPLETE);
186
+ muxClient = null;
187
+ }
188
+ const s3Store = createS3Store(pluginOptions.s3);
189
+ const tusServer = createTusServer({
190
+ config,
191
+ s3Store
192
+ });
193
+ const mediaCollection = getMediaCollection({ s3Store });
194
+ if (isPluginDisabled) return config;
195
+ initClientUploads({
196
+ clientHandler: "@maas/payload-plugin-media-cloud/components#UploadHandler",
197
+ collections: { media: {
198
+ clientUploads: true,
199
+ disableLocalStorage: true
200
+ } },
201
+ config,
202
+ enabled: true,
203
+ serverHandler: () => {
204
+ return Response.json({ message: "Server handler is not implemented" }, { status: 501 });
205
+ },
206
+ serverHandlerPath: "/media-cloud/upload"
207
+ });
208
+ const cloudStorageConfig = { collections: { media: {
209
+ adapter: getStorageAdapter({
210
+ getMuxClient,
211
+ pluginOptions,
212
+ s3Store
213
+ }),
214
+ clientUploads: true,
215
+ disableLocalStorage: true
216
+ } } };
217
+ const mappedConfig = {
218
+ ...config,
219
+ admin: {
220
+ ...config.admin ?? {},
221
+ components: {
222
+ ...config.admin?.components ?? {},
223
+ providers: ["@maas/payload-plugin-media-cloud/components#UploadManagerProvider", ...config.admin?.components?.providers ?? []]
224
+ }
225
+ },
226
+ collections: [...config.collections ?? [], mediaCollection],
227
+ endpoints: [
228
+ ...config.endpoints ?? [],
229
+ ...createTusEndpoints(tusServer),
230
+ ...createMuxEndpoints({
231
+ getMuxClient,
232
+ pluginOptions
233
+ })
234
+ ]
235
+ };
236
+ return cloudStoragePlugin(cloudStorageConfig)(mappedConfig);
237
+ };
238
+ }
239
+
240
+ //#endregion
241
+ export { mediaCloudPlugin };
242
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","names":["muxClient: Mux | null","muxConfig: MediaCloudPluginOptions['mux']","s3Config: MediaCloudPluginOptions['s3']","args: CreateTusServerArgs","TusServer","tusServer: TusServer","req: PayloadRequest","args: CreateMuxEndpointsArgs","pluginOptions: MediaCloudPluginOptions","config: Config","mappedConfig: Config"],"sources":["../src/plugin.ts"],"sourcesContent":["import Mux from '@mux/mux-node'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getPayload, sanitizeConfig } from 'payload'\nimport { Server as TusServer, type DataStore } from '@tus/server'\n\nimport { MediaCloudError } from './types/errors'\nimport { useErrorHandler } from './hooks/useErrorHandler'\nimport { getStorageAdapter } from './adapter/storageAdapter'\nimport { getMediaCollection } from './collections/mediaCollection'\nimport { getMuxAssetHandler } from './endpoints/muxAssetHandler'\nimport { getMuxCreateUploadHandler } from './endpoints/muxCreateUploadHandler'\nimport { getMuxWebhookHandler } from './endpoints/muxWebhookHandler'\nimport { S3Store } from './tus/stores/s3/s3-store'\nimport { generateUniqueFilename } from './utils/file'\n\nimport type { Config, PayloadRequest } from 'payload'\nimport type { MediaCloudPluginOptions } from './types'\n\ninterface CreateTusServerArgs {\n config: Config\n s3Store: DataStore\n}\n\ninterface CreateMuxEndpointsArgs {\n getMuxClient: () => Mux\n pluginOptions: MediaCloudPluginOptions\n}\n\nconst { logError, throwError } = useErrorHandler()\n\nlet muxClient: Mux | null = null\n\n/**\n * Validates Mux configuration options\n * @param muxConfig - The Mux configuration to validate\n * @throws {Error} When required Mux configuration is missing\n */\nfunction validateMuxConfig(muxConfig: MediaCloudPluginOptions['mux']): void {\n if (!muxConfig?.tokenId || !muxConfig?.tokenSecret) {\n throwError(MediaCloudError.MUX_CONFIG_MISSING)\n }\n}\n\n/**\n * Creates a new Mux client instance\n * @param muxConfig - The Mux configuration options\n * @returns A configured Mux client\n */\nfunction createMuxClient(muxConfig: MediaCloudPluginOptions['mux']): Mux {\n // Create and return Mux client instance\n return new Mux({\n tokenId: muxConfig.tokenId,\n tokenSecret: muxConfig.tokenSecret,\n webhookSecret: muxConfig.webhookSecret,\n })\n}\n\n/**\n * Creates and configures an S3Store instance\n * @param s3Config - The S3 configuration options\n * @returns A configured S3Store instance\n * @throws {Error} When required S3 configuration is missing\n */\nfunction createS3Store(s3Config: MediaCloudPluginOptions['s3']): S3Store {\n // Validate S3 configuration\n if (\n !s3Config?.bucket ||\n !s3Config?.region ||\n !s3Config?.accessKeyId ||\n !s3Config?.secretAccessKey\n ) {\n throwError(MediaCloudError.S3_CONFIG_MISSING)\n }\n\n // Create and return S3Store instance\n return new S3Store({\n s3ClientConfig: {\n acl: 'public-read',\n bucket: s3Config.bucket,\n credentials: {\n accessKeyId: s3Config.accessKeyId,\n secretAccessKey: s3Config.secretAccessKey,\n },\n endpoint: s3Config.endpoint,\n forcePathStyle: true,\n region: s3Config.region,\n },\n })\n}\n\n/**\n * Creates a TUS server instance with S3 storage\n * @param args - The arguments for creating the TUS server\n * @returns A configured TusServer instance\n */\nfunction createTusServer(args: CreateTusServerArgs): TusServer {\n const { config, s3Store } = args\n\n return new TusServer({\n datastore: s3Store,\n namingFunction: async (req, metadata) => {\n try {\n // Get Payload instance\n const payload = await getPayload({\n config: sanitizeConfig(config),\n })\n\n // Check if a file with the same name already exists in the media collection\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n filename: {\n equals: metadata?.filename || '',\n },\n },\n })\n\n // If a file with the same name exists, generate a unique filename\n if (docs.length > 0) {\n return generateUniqueFilename(metadata?.filename ?? '')\n } else {\n // If no file with the same name exists, return the original filename\n return metadata?.filename || generateUniqueFilename('')\n }\n } catch (_error) {\n // If an error occurs, log it and return the original filename\n logError(MediaCloudError.NAMING_FUNCTION_ERROR)\n return metadata?.filename || generateUniqueFilename('')\n }\n },\n path: '/api/uploads',\n })\n}\n\n/**\n * Creates TUS upload endpoints for file handling\n * @param tusServer - The TUS server instance\n * @returns An array of endpoint configurations\n */\nfunction createTusEndpoints(tusServer: TusServer) {\n /**\n * Handles TUS requests through the server\n * @param req - The payload request object\n * @returns The server response\n */\n function tusHandler(req: PayloadRequest) {\n return tusServer.handleWeb(req as Request)\n }\n\n return [\n { handler: tusHandler, method: 'options' as const, path: '/uploads' },\n { handler: tusHandler, method: 'post' as const, path: '/uploads' },\n { handler: tusHandler, method: 'get' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'put' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'patch' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'delete' as const, path: '/uploads/:id' },\n ]\n}\n\n/**\n * Creates Mux-related endpoints for asset handling\n * @param args - The arguments for creating Mux endpoints\n * @returns An array of Mux endpoint configurations\n */\nfunction createMuxEndpoints(args: CreateMuxEndpointsArgs) {\n const { getMuxClient, pluginOptions } = args\n return [\n {\n handler: getMuxWebhookHandler({ getMuxClient }),\n method: 'get' as const,\n path: '/mux/webhook',\n },\n {\n handler: getMuxCreateUploadHandler({ getMuxClient, pluginOptions }),\n method: 'post' as const,\n path: '/mux/upload',\n },\n {\n handler: getMuxAssetHandler({ getMuxClient }),\n method: 'get' as const,\n path: '/mux/asset',\n },\n ]\n}\n\n/**\n * Media Cloud Plugin for Payload CMS\n * Provides file upload capabilities using S3 storage and Mux video processing\n * @param pluginOptions - Configuration options for the plugin\n * @returns A function that configures the Payload config\n */\nexport function mediaCloudPlugin(pluginOptions: MediaCloudPluginOptions) {\n return function (config: Config): Config {\n if (!pluginOptions) {\n logError(MediaCloudError.PLUGIN_NOT_CONFIGURED)\n return config\n }\n\n const isPluginDisabled = pluginOptions.enabled === false\n\n /**\n * Gets or creates a Mux client instance\n * @returns The Mux client instance\n */\n function getMuxClient(): Mux {\n // Return existing Mux client if already created\n if (muxClient) {\n return muxClient\n }\n // Create and return a new Mux client\n return createMuxClient(pluginOptions.mux)\n }\n\n // Validate Mux configuration\n if (pluginOptions.mux) {\n validateMuxConfig(pluginOptions.mux)\n muxClient = getMuxClient()\n } else {\n logError(MediaCloudError.MUX_CONFIG_INCOMPLETE)\n muxClient = null\n }\n\n const s3Store = createS3Store(pluginOptions.s3)\n const tusServer = createTusServer({ config, s3Store })\n const mediaCollection = getMediaCollection({ s3Store })\n\n if (isPluginDisabled) {\n return config\n }\n\n // Initialize client uploads\n initClientUploads({\n clientHandler:\n '@maas/payload-plugin-media-cloud/components#UploadHandler',\n collections: {\n media: {\n clientUploads: true,\n disableLocalStorage: true,\n },\n },\n config,\n enabled: true,\n serverHandler: () => {\n return Response.json(\n { message: 'Server handler is not implemented' },\n { status: 501 }\n )\n },\n serverHandlerPath: '/media-cloud/upload',\n })\n\n const cloudStorageConfig = {\n collections: {\n media: {\n adapter: getStorageAdapter({ getMuxClient, pluginOptions, s3Store }),\n clientUploads: true,\n disableLocalStorage: true,\n },\n },\n }\n\n const mappedConfig: Config = {\n ...config,\n admin: {\n ...(config.admin ?? {}),\n components: {\n ...(config.admin?.components ?? {}),\n providers: [\n '@maas/payload-plugin-media-cloud/components#UploadManagerProvider',\n ...(config.admin?.components?.providers ?? []),\n ],\n },\n },\n collections: [...(config.collections ?? []), mediaCollection],\n endpoints: [\n ...(config.endpoints ?? []),\n ...createTusEndpoints(tusServer),\n ...createMuxEndpoints({ getMuxClient, pluginOptions }),\n ],\n }\n\n return cloudStoragePlugin(cloudStorageConfig)(mappedConfig)\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { S3 } from "@aws-sdk/client-s3";
2
+
3
+ //#region src/tus/stores/s3/expiration-manager.d.ts
4
+ type GetExpirationDateArgs = {
5
+ createdAt: string;
6
+ };
7
+ declare class S3ExpirationManager {
8
+ private client;
9
+ private bucket;
10
+ private expirationPeriodInMilliseconds;
11
+ private generateInfoKey;
12
+ private generatePartKey;
13
+ constructor(client: S3, bucket: string, expirationPeriodInMilliseconds: number, generateInfoKey: (id: string) => string, generatePartKey: (id: string, isIncompletePart: boolean) => string);
14
+ /**
15
+ * Calculates the expiration date for a file based on a creation date
16
+ * @param args - The function arguments
17
+ * @param args.createdAt - The creation date as a string
18
+ * @returns The expiration date
19
+ */
20
+ getExpirationDate(args: GetExpirationDateArgs): Date;
21
+ /**
22
+ * Gets the expiration period in milliseconds
23
+ * @returns The expiration period in milliseconds
24
+ */
25
+ getExpiration(): number;
26
+ /**
27
+ * Deletes expired incomplete uploads based on their initialization date.
28
+ * Returns the number of deleted uploads.
29
+ * @param args - The function arguments (empty object)
30
+ * @returns Promise that resolves to the number of deleted uploads
31
+ */
32
+ deleteExpired(): Promise<number>;
33
+ }
34
+ //#endregion
35
+ export { S3ExpirationManager };
36
+ //# sourceMappingURL=expiration-manager.d.ts.map
@@ -0,0 +1,76 @@
1
+ //#region src/tus/stores/s3/expiration-manager.ts
2
+ var S3ExpirationManager = class {
3
+ constructor(client, bucket, expirationPeriodInMilliseconds, generateInfoKey, generatePartKey) {
4
+ this.client = client;
5
+ this.bucket = bucket;
6
+ this.expirationPeriodInMilliseconds = expirationPeriodInMilliseconds;
7
+ this.generateInfoKey = generateInfoKey;
8
+ this.generatePartKey = generatePartKey;
9
+ }
10
+ /**
11
+ * Calculates the expiration date for a file based on a creation date
12
+ * @param args - The function arguments
13
+ * @param args.createdAt - The creation date as a string
14
+ * @returns The expiration date
15
+ */
16
+ getExpirationDate(args) {
17
+ const { createdAt } = args;
18
+ const date = new Date(createdAt);
19
+ return new Date(date.getTime() + this.expirationPeriodInMilliseconds);
20
+ }
21
+ /**
22
+ * Gets the expiration period in milliseconds
23
+ * @returns The expiration period in milliseconds
24
+ */
25
+ getExpiration() {
26
+ return this.expirationPeriodInMilliseconds;
27
+ }
28
+ /**
29
+ * Deletes expired incomplete uploads based on their initialization date.
30
+ * Returns the number of deleted uploads.
31
+ * @param args - The function arguments (empty object)
32
+ * @returns Promise that resolves to the number of deleted uploads
33
+ */
34
+ async deleteExpired() {
35
+ if (this.getExpiration() === 0) return 0;
36
+ let keyMarker = void 0;
37
+ let uploadIdMarker = void 0;
38
+ let isTruncated = true;
39
+ let deleted = 0;
40
+ while (isTruncated) {
41
+ const listResponse = await this.client.listMultipartUploads({
42
+ Bucket: this.bucket,
43
+ KeyMarker: keyMarker,
44
+ UploadIdMarker: uploadIdMarker
45
+ });
46
+ const expiredUploads = listResponse.Uploads?.filter((multiPartUpload) => {
47
+ const initiatedDate = multiPartUpload.Initiated;
48
+ return initiatedDate && (/* @__PURE__ */ new Date()).getTime() > this.getExpirationDate({ createdAt: initiatedDate.toISOString() }).getTime();
49
+ }) || [];
50
+ const objectsToDelete = expiredUploads.reduce((all, expiredUpload) => {
51
+ all.push({ Key: this.generateInfoKey(expiredUpload.Key) }, { Key: this.generatePartKey(expiredUpload.Key, true) });
52
+ return all;
53
+ }, []);
54
+ const deletions = [];
55
+ if (objectsToDelete.length > 0) deletions.push(this.client.deleteObjects({
56
+ Bucket: this.bucket,
57
+ Delete: { Objects: objectsToDelete }
58
+ }));
59
+ const abortions = expiredUploads.map((expiredUpload) => this.client.abortMultipartUpload({
60
+ Bucket: this.bucket,
61
+ Key: expiredUpload.Key,
62
+ UploadId: expiredUpload.UploadId
63
+ }));
64
+ await Promise.all([...deletions, ...abortions]);
65
+ deleted += expiredUploads.length;
66
+ isTruncated = listResponse.IsTruncated || false;
67
+ keyMarker = listResponse.NextKeyMarker;
68
+ uploadIdMarker = listResponse.NextUploadIdMarker;
69
+ }
70
+ return deleted;
71
+ }
72
+ };
73
+
74
+ //#endregion
75
+ export { S3ExpirationManager };
76
+ //# sourceMappingURL=expiration-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expiration-manager.js","names":["client: S3","bucket: string","expirationPeriodInMilliseconds: number","generateInfoKey: (id: string) => string","generatePartKey: (id: string, isIncompletePart: boolean) => string","args: GetExpirationDateArgs","keyMarker: string | undefined","uploadIdMarker: string | undefined","listResponse: AWS.ListMultipartUploadsCommandOutput","multiPartUpload: AWS.MultipartUpload","all: { Key: string }[]","expiredUpload: AWS.MultipartUpload","deletions: Promise<AWS.DeleteObjectsCommandOutput>[]"],"sources":["../../../../src/tus/stores/s3/expiration-manager.ts"],"sourcesContent":["import type AWS from '@aws-sdk/client-s3'\nimport type { S3 } from '@aws-sdk/client-s3'\n\ntype GetExpirationDateArgs = {\n createdAt: string\n}\n\nexport class S3ExpirationManager {\n constructor(\n private client: S3,\n private bucket: string,\n private expirationPeriodInMilliseconds: number,\n private generateInfoKey: (id: string) => string,\n private generatePartKey: (id: string, isIncompletePart: boolean) => string\n ) {}\n\n /**\n * Calculates the expiration date for a file based on a creation date\n * @param args - The function arguments\n * @param args.createdAt - The creation date as a string\n * @returns The expiration date\n */\n getExpirationDate(args: GetExpirationDateArgs): Date {\n const { createdAt } = args\n const date = new Date(createdAt)\n return new Date(date.getTime() + this.expirationPeriodInMilliseconds)\n }\n\n /**\n * Gets the expiration period in milliseconds\n * @returns The expiration period in milliseconds\n */\n getExpiration(): number {\n return this.expirationPeriodInMilliseconds\n }\n\n /**\n * Deletes expired incomplete uploads based on their initialization date.\n * Returns the number of deleted uploads.\n * @param args - The function arguments (empty object)\n * @returns Promise that resolves to the number of deleted uploads\n */\n async deleteExpired(): Promise<number> {\n // No arguments to destructure\n if (this.getExpiration() === 0) {\n return 0\n }\n\n let keyMarker: string | undefined = undefined\n let uploadIdMarker: string | undefined = undefined\n let isTruncated = true\n let deleted = 0\n\n while (isTruncated) {\n const listResponse: AWS.ListMultipartUploadsCommandOutput =\n await this.client.listMultipartUploads({\n Bucket: this.bucket,\n KeyMarker: keyMarker,\n UploadIdMarker: uploadIdMarker,\n })\n\n const expiredUploads =\n listResponse.Uploads?.filter((multiPartUpload: AWS.MultipartUpload) => {\n const initiatedDate = multiPartUpload.Initiated\n return (\n initiatedDate &&\n new Date().getTime() >\n this.getExpirationDate({\n createdAt: initiatedDate.toISOString(),\n }).getTime()\n )\n }) || []\n\n const objectsToDelete = expiredUploads.reduce(\n (all: { Key: string }[], expiredUpload: AWS.MultipartUpload) => {\n all.push(\n {\n Key: this.generateInfoKey(expiredUpload.Key as string),\n },\n {\n Key: this.generatePartKey(expiredUpload.Key as string, true),\n }\n )\n return all\n },\n [] as { Key: string }[]\n )\n\n const deletions: Promise<AWS.DeleteObjectsCommandOutput>[] = []\n\n if (objectsToDelete.length > 0) {\n deletions.push(\n this.client.deleteObjects({\n Bucket: this.bucket,\n Delete: {\n Objects: objectsToDelete,\n },\n })\n )\n }\n\n const abortions = expiredUploads.map(\n (expiredUpload: AWS.MultipartUpload) =>\n this.client.abortMultipartUpload({\n Bucket: this.bucket,\n Key: expiredUpload.Key,\n UploadId: expiredUpload.UploadId,\n })\n )\n\n await Promise.all([...deletions, ...abortions])\n\n deleted += expiredUploads.length\n\n isTruncated = listResponse.IsTruncated || false\n keyMarker = listResponse.NextKeyMarker\n uploadIdMarker = listResponse.NextUploadIdMarker\n }\n\n return deleted\n }\n}\n"],"mappings":";AAOA,IAAa,sBAAb,MAAiC;CAC/B,YACUA,QACAC,QACAC,gCACAC,iBACAC,iBACR;EALQ;EACA;EACA;EACA;EACA;CACN;;;;;;;CAQJ,kBAAkBC,MAAmC;EACnD,MAAM,EAAE,WAAW,GAAG;EACtB,MAAM,OAAO,IAAI,KAAK;AACtB,SAAO,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK;CACvC;;;;;CAMD,gBAAwB;AACtB,SAAO,KAAK;CACb;;;;;;;CAQD,MAAM,gBAAiC;AAErC,MAAI,KAAK,eAAe,KAAK,EAC3B,QAAO;EAGT,IAAIC,YAAgC;EACpC,IAAIC,iBAAqC;EACzC,IAAI,cAAc;EAClB,IAAI,UAAU;AAEd,SAAO,aAAa;GAClB,MAAMC,eACJ,MAAM,KAAK,OAAO,qBAAqB;IACrC,QAAQ,KAAK;IACb,WAAW;IACX,gBAAgB;GACjB,EAAC;GAEJ,MAAM,iBACJ,aAAa,SAAS,OAAO,CAACC,oBAAyC;IACrE,MAAM,gBAAgB,gBAAgB;AACtC,WACE,kCACA,IAAI,QAAO,SAAS,GAClB,KAAK,kBAAkB,EACrB,WAAW,cAAc,aAAa,CACvC,EAAC,CAAC,SAAS;GAEjB,EAAC,IAAI,CAAE;GAEV,MAAM,kBAAkB,eAAe,OACrC,CAACC,KAAwBC,kBAAuC;IAC9D,IAAI,KACF,EACE,KAAK,KAAK,gBAAgB,cAAc,IAAc,CACvD,GACD,EACE,KAAK,KAAK,gBAAgB,cAAc,KAAe,KAAK,CAC7D,EACF;AACD,WAAO;GACR,GACD,CAAE,EACH;GAED,MAAMC,YAAuD,CAAE;AAE/D,OAAI,gBAAgB,SAAS,GAC3B,UAAU,KACR,KAAK,OAAO,cAAc;IACxB,QAAQ,KAAK;IACb,QAAQ,EACN,SAAS,gBACV;GACF,EAAC,CACH;GAGH,MAAM,YAAY,eAAe,IAC/B,CAACD,kBACC,KAAK,OAAO,qBAAqB;IAC/B,QAAQ,KAAK;IACb,KAAK,cAAc;IACnB,UAAU,cAAc;GACzB,EAAC,CACL;GAED,MAAM,QAAQ,IAAI,CAAC,GAAG,WAAW,GAAG,SAAU,EAAC;GAE/C,WAAW,eAAe;GAE1B,cAAc,aAAa,eAAe;GAC1C,YAAY,aAAa;GACzB,iBAAiB,aAAa;EAC/B;AAED,SAAO;CACR;AACF"}
@@ -0,0 +1,66 @@
1
+ import AWS from "@aws-sdk/client-s3";
2
+
3
+ //#region src/tus/stores/s3/file-operations.d.ts
4
+ type CalculateOptimalPartSizeArgs = {
5
+ size?: number;
6
+ };
7
+ type CalculateOffsetFromPartsArgs = {
8
+ parts?: Array<AWS.Part>;
9
+ };
10
+ type CalculatePartNumberArgs = {
11
+ parts: Array<AWS.Part>;
12
+ };
13
+ type GenerateUniqueTmpFileNameArgs = {
14
+ template: string;
15
+ };
16
+ type CalculateOffsetFromPartsExportArgs = {
17
+ parts?: Array<{
18
+ Size?: number;
19
+ }>;
20
+ };
21
+ declare class S3FileOperations {
22
+ private maxMultipartParts;
23
+ private maxUploadSize;
24
+ private minPartSize;
25
+ private preferredPartSize;
26
+ constructor(maxMultipartParts: number, maxUploadSize: number, minPartSize: number, preferredPartSize: number);
27
+ /**
28
+ * Calculates the optimal part size for S3 multipart upload
29
+ * @param args - The function arguments
30
+ * @param args.size - The upload size in bytes (optional)
31
+ * @returns The optimal part size in bytes
32
+ */
33
+ calculateOptimalPartSize(args: CalculateOptimalPartSizeArgs): number;
34
+ /**
35
+ * Calculates the offset based on uploaded parts
36
+ * @param args - The function arguments
37
+ * @param args.parts - Array of uploaded parts (optional)
38
+ * @returns The total offset in bytes
39
+ */
40
+ calculateOffsetFromParts(args: CalculateOffsetFromPartsArgs): number;
41
+ /**
42
+ * Calculates the next part number based on existing parts
43
+ * @param args - The function arguments
44
+ * @param args.parts - Array of uploaded parts
45
+ * @returns The next part number to use
46
+ */
47
+ calculatePartNumber(args: CalculatePartNumberArgs): number;
48
+ /**
49
+ * Generates a unique temporary file name
50
+ * @param args - The function arguments
51
+ * @param args.template - The template string for the file name
52
+ * @returns Promise that resolves to the unique file path
53
+ * @throws Error if unable to find unique name after max tries
54
+ */
55
+ generateUniqueTmpFileName(args: GenerateUniqueTmpFileNameArgs): Promise<string>;
56
+ }
57
+ /**
58
+ * Calculates the total offset from uploaded parts
59
+ * @param args - The function arguments
60
+ * @param args.parts - Array of parts with size information (optional)
61
+ * @returns The total offset in bytes
62
+ */
63
+ declare function calculateOffsetFromParts(args: CalculateOffsetFromPartsExportArgs): number;
64
+ //#endregion
65
+ export { S3FileOperations, calculateOffsetFromParts };
66
+ //# sourceMappingURL=file-operations.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { MediaCloudError } from "../../../types/errors.js";
2
+ import { useErrorHandler } from "../../../hooks/useErrorHandler.js";
3
+ import crypto from "node:crypto";
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+
8
+ //#region src/tus/stores/s3/file-operations.ts
9
+ const { throwError } = useErrorHandler();
10
+ var S3FileOperations = class {
11
+ constructor(maxMultipartParts, maxUploadSize, minPartSize, preferredPartSize) {
12
+ this.maxMultipartParts = maxMultipartParts;
13
+ this.maxUploadSize = maxUploadSize;
14
+ this.minPartSize = minPartSize;
15
+ this.preferredPartSize = preferredPartSize;
16
+ }
17
+ /**
18
+ * Calculates the optimal part size for S3 multipart upload
19
+ * @param args - The function arguments
20
+ * @param args.size - The upload size in bytes (optional)
21
+ * @returns The optimal part size in bytes
22
+ */
23
+ calculateOptimalPartSize(args) {
24
+ const { size } = args;
25
+ let uploadSize = size;
26
+ if (uploadSize === void 0) uploadSize = this.maxUploadSize;
27
+ let optimalPartSize;
28
+ if (uploadSize <= this.preferredPartSize) optimalPartSize = uploadSize;
29
+ else if (uploadSize <= this.preferredPartSize * this.maxMultipartParts) optimalPartSize = this.preferredPartSize;
30
+ else optimalPartSize = Math.ceil(uploadSize / this.maxMultipartParts);
31
+ return Math.max(optimalPartSize, this.minPartSize);
32
+ }
33
+ /**
34
+ * Calculates the offset based on uploaded parts
35
+ * @param args - The function arguments
36
+ * @param args.parts - Array of uploaded parts (optional)
37
+ * @returns The total offset in bytes
38
+ */
39
+ calculateOffsetFromParts(args) {
40
+ const { parts } = args;
41
+ return parts && parts.length > 0 ? parts.reduce((a, b) => a + (b.Size ?? 0), 0) : 0;
42
+ }
43
+ /**
44
+ * Calculates the next part number based on existing parts
45
+ * @param args - The function arguments
46
+ * @param args.parts - Array of uploaded parts
47
+ * @returns The next part number to use
48
+ */
49
+ calculatePartNumber(args) {
50
+ const { parts } = args;
51
+ return parts.length > 0 ? parts[parts.length - 1].PartNumber + 1 : 1;
52
+ }
53
+ /**
54
+ * Generates a unique temporary file name
55
+ * @param args - The function arguments
56
+ * @param args.template - The template string for the file name
57
+ * @returns Promise that resolves to the unique file path
58
+ * @throws Error if unable to find unique name after max tries
59
+ */
60
+ async generateUniqueTmpFileName(args) {
61
+ const { template } = args;
62
+ const tries = 5;
63
+ for (let i = 0; i < tries; i++) {
64
+ const randomId = crypto.randomBytes(16).toString("hex");
65
+ const filePath = path.join(os.tmpdir(), `${template}${randomId}`);
66
+ try {
67
+ await fs.promises.access(filePath, fs.constants.F_OK);
68
+ } catch (error) {
69
+ const nodeError = error;
70
+ if (nodeError.code === "ENOENT") return filePath;
71
+ }
72
+ }
73
+ throwError(MediaCloudError.S3_UNIQUE_NAME_ERROR);
74
+ throw new Error();
75
+ }
76
+ };
77
+ /**
78
+ * Calculates the total offset from uploaded parts
79
+ * @param args - The function arguments
80
+ * @param args.parts - Array of parts with size information (optional)
81
+ * @returns The total offset in bytes
82
+ */
83
+ function calculateOffsetFromParts(args) {
84
+ const { parts } = args;
85
+ return parts && parts.length > 0 ? parts.reduce((a, b) => a + (b.Size ?? 0), 0) : 0;
86
+ }
87
+
88
+ //#endregion
89
+ export { S3FileOperations, calculateOffsetFromParts };
90
+ //# sourceMappingURL=file-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-operations.js","names":["maxMultipartParts: number","maxUploadSize: number","minPartSize: number","preferredPartSize: number","args: CalculateOptimalPartSizeArgs","optimalPartSize: number","args: CalculateOffsetFromPartsArgs","args: CalculatePartNumberArgs","args: GenerateUniqueTmpFileNameArgs","args: CalculateOffsetFromPartsExportArgs"],"sources":["../../../../src/tus/stores/s3/file-operations.ts"],"sourcesContent":["import crypto from 'node:crypto'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudError } from '../../../types/errors'\n\nimport type { NodeFSError } from '../../../types'\nimport type AWS from '@aws-sdk/client-s3'\n\ntype CalculateOptimalPartSizeArgs = {\n size?: number\n}\n\ntype CalculateOffsetFromPartsArgs = {\n parts?: Array<AWS.Part>\n}\n\ntype CalculatePartNumberArgs = {\n parts: Array<AWS.Part>\n}\n\ntype GenerateUniqueTmpFileNameArgs = {\n template: string\n}\n\ntype CalculateOffsetFromPartsExportArgs = {\n parts?: Array<{ Size?: number }>\n}\n\nconst { throwError } = useErrorHandler()\n\nexport class S3FileOperations {\n constructor(\n private maxMultipartParts: number,\n private maxUploadSize: number,\n private minPartSize: number,\n private preferredPartSize: number\n ) {}\n\n /**\n * Calculates the optimal part size for S3 multipart upload\n * @param args - The function arguments\n * @param args.size - The upload size in bytes (optional)\n * @returns The optimal part size in bytes\n */\n calculateOptimalPartSize(args: CalculateOptimalPartSizeArgs): number {\n const { size } = args\n // When upload size is not known we assume largest possible value (`maxUploadSize`)\n let uploadSize = size\n if (uploadSize === undefined) {\n uploadSize = this.maxUploadSize\n }\n\n let optimalPartSize: number\n\n // When upload is smaller or equal to PreferredPartSize, we upload in just one part.\n if (uploadSize <= this.preferredPartSize) {\n optimalPartSize = uploadSize\n }\n // Does the upload fit in MaxMultipartParts parts or less with PreferredPartSize.\n else if (uploadSize <= this.preferredPartSize * this.maxMultipartParts) {\n optimalPartSize = this.preferredPartSize\n // The upload is too big for the preferred size.\n // We divide the size with the max amount of parts and round it up.\n } else {\n optimalPartSize = Math.ceil(uploadSize / this.maxMultipartParts)\n }\n\n // Always ensure the part size is at least minPartSize\n return Math.max(optimalPartSize, this.minPartSize)\n }\n\n /**\n * Calculates the offset based on uploaded parts\n * @param args - The function arguments\n * @param args.parts - Array of uploaded parts (optional)\n * @returns The total offset in bytes\n */\n calculateOffsetFromParts(args: CalculateOffsetFromPartsArgs): number {\n const { parts } = args\n return parts && parts.length > 0\n ? parts.reduce((a, b) => a + (b.Size ?? 0), 0)\n : 0\n }\n\n /**\n * Calculates the next part number based on existing parts\n * @param args - The function arguments\n * @param args.parts - Array of uploaded parts\n * @returns The next part number to use\n */\n calculatePartNumber(args: CalculatePartNumberArgs): number {\n const { parts } = args\n return parts.length > 0 ? parts[parts.length - 1].PartNumber! + 1 : 1\n }\n\n /**\n * Generates a unique temporary file name\n * @param args - The function arguments\n * @param args.template - The template string for the file name\n * @returns Promise that resolves to the unique file path\n * @throws Error if unable to find unique name after max tries\n */\n async generateUniqueTmpFileName(\n args: GenerateUniqueTmpFileNameArgs\n ): Promise<string> {\n const { template } = args\n const tries = 5\n for (let i = 0; i < tries; i++) {\n const randomId = crypto.randomBytes(16).toString('hex')\n const filePath = path.join(os.tmpdir(), `${template}${randomId}`)\n try {\n await fs.promises.access(filePath, fs.constants.F_OK)\n } catch (error) {\n const nodeError = error as NodeFSError\n if (nodeError.code === 'ENOENT') {\n return filePath\n }\n }\n }\n throwError(MediaCloudError.S3_UNIQUE_NAME_ERROR)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n}\n\n/**\n * Calculates the total offset from uploaded parts\n * @param args - The function arguments\n * @param args.parts - Array of parts with size information (optional)\n * @returns The total offset in bytes\n */\nexport function calculateOffsetFromParts(\n args: CalculateOffsetFromPartsExportArgs\n) {\n const { parts } = args\n return parts && parts.length > 0\n ? parts.reduce((a, b) => a + (b.Size ?? 0), 0)\n : 0\n}\n"],"mappings":";;;;;;;;AA+BA,MAAM,EAAE,YAAY,GAAG,iBAAiB;AAExC,IAAa,mBAAb,MAA8B;CAC5B,YACUA,mBACAC,eACAC,aACAC,mBACR;EAJQ;EACA;EACA;EACA;CACN;;;;;;;CAQJ,yBAAyBC,MAA4C;EACnE,MAAM,EAAE,MAAM,GAAG;EAEjB,IAAI,aAAa;AACjB,MAAI,eAAe,QACjB,aAAa,KAAK;EAGpB,IAAIC;AAGJ,MAAI,cAAc,KAAK,mBACrB,kBAAkB;WAGX,cAAc,KAAK,oBAAoB,KAAK,mBACnD,kBAAkB,KAAK;OAIvB,kBAAkB,KAAK,KAAK,aAAa,KAAK,kBAAkB;AAIlE,SAAO,KAAK,IAAI,iBAAiB,KAAK,YAAY;CACnD;;;;;;;CAQD,yBAAyBC,MAA4C;EACnE,MAAM,EAAE,OAAO,GAAG;AAClB,SAAO,SAAS,MAAM,SAAS,IAC3B,MAAM,OAAO,CAAC,GAAG,MAAM,KAAK,EAAE,QAAQ,IAAI,EAAE,GAC5C;CACL;;;;;;;CAQD,oBAAoBC,MAAuC;EACzD,MAAM,EAAE,OAAO,GAAG;AAClB,SAAO,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,GAAG,aAAc,IAAI;CACrE;;;;;;;;CASD,MAAM,0BACJC,MACiB;EACjB,MAAM,EAAE,UAAU,GAAG;EACrB,MAAM,QAAQ;AACd,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;GAC9B,MAAM,WAAW,OAAO,YAAY,GAAG,CAAC,SAAS,MAAM;GACvD,MAAM,WAAW,KAAK,KAAK,GAAG,QAAQ,EAAE,GAAG,WAAW,UAAU,CAAC;AACjE,OAAI;IACF,MAAM,GAAG,SAAS,OAAO,UAAU,GAAG,UAAU,KAAK;GACtD,SAAQ,OAAO;IACd,MAAM,YAAY;AAClB,QAAI,UAAU,SAAS,SACrB,QAAO;GAEV;EACF;EACD,WAAW,gBAAgB,qBAAqB;AAChD,QAAM,IAAI;CACX;AACF;;;;;;;AAQD,SAAgB,yBACdC,MACA;CACA,MAAM,EAAE,OAAO,GAAG;AAClB,QAAO,SAAS,MAAM,SAAS,IAC3B,MAAM,OAAO,CAAC,GAAG,MAAM,KAAK,EAAE,QAAQ,IAAI,EAAE,GAC5C;AACL"}
@@ -0,0 +1,5 @@
1
+ //#region src/tus/stores/s3/log.d.ts
2
+ declare function log(message: string, ...args: unknown[]): void;
3
+ //#endregion
4
+ export { log };
5
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1,8 @@
1
+ //#region src/tus/stores/s3/log.ts
2
+ function log(message, ...args) {
3
+ console.log(`tus:stores:s3store - ${message}`, ...args);
4
+ }
5
+
6
+ //#endregion
7
+ export { log };
8
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","names":["message: string"],"sources":["../../../../src/tus/stores/s3/log.ts"],"sourcesContent":["export function log(message: string, ...args: unknown[]) {\n console.log(`tus:stores:s3store - ${message}`, ...args)\n}\n"],"mappings":";AAAA,SAAgB,IAAIA,SAAiB,GAAG,MAAiB;CACvD,QAAQ,IAAI,CAAC,qBAAqB,EAAE,SAAS,EAAE,GAAG,KAAK;AACxD"}